* Re: [PATCH] ipv6: Fix for adding multicast route for loopback device automatically.
From: Li Wei @ 2011-12-13 0:56 UTC (permalink / raw)
To: David Miller; +Cc: netdev, yoshfuji
In-Reply-To: <20111212.184929.1998244574424635802.davem@davemloft.net>
> From: Li Wei <lw@cn.fujitsu.com>
> Date: Thu, 08 Dec 2011 08:58:21 +0800
>
>> The problem is that the resulting routing table depends on the sequence
>> of interface's initialization and in some situation, that would block all
>> muticast packets. Suppose there are two interfaces on my computer
>> (lo and eth0), if we initailize 'lo' before 'eth0', the resuting routing
>> table(for multicast) would be
>>
>> # ip -6 route show | grep ff00::
>> unreachable ff00::/8 dev lo metric 256 error -101
>> ff00::/8 dev eth0 metric 256
>>
>> When sending multicasting packets, routing subsystem will return the first
>> route entry which with a error set to -101(ENETUNREACH).
>>
>> I know the kernel will set the default ipv6 address for 'lo' when it is up
>> and won't set the default multicast route for it, but there is no reason to
>> stop 'init' program from setting address for 'lo', and that is exactly what
>> systemd did.
>
> Ok, I added this more detailed explanation to the commit message and
> applied your patch.
>
> Probably it is a good idea to explain things completely, with all details
> and examples, in the commit message from the beginning :-)
>
>
Thanks David, remember your advice :)
^ permalink raw reply
* Re: [PATCH] net: do not pass vlan pkts to real dev pkt handler also
From: Vasu Dev @ 2011-12-13 1:08 UTC (permalink / raw)
To: Jiri Pirko; +Cc: Vasu Dev, netdev, devel, eric.dumazet
In-Reply-To: <20111212225622.GA7083@minipsycho>
On Mon, 2011-12-12 at 23:56 +0100, Jiri Pirko wrote:
> Mon, Dec 12, 2011 at 11:19:23PM CET, vasu.dev@intel.com wrote:
> >The orig_dev has to be updated before going another round
> >for vlan pkts, otherwise currently unmodified real orig_dev
> >causes vlan pkt delivered to real orig_dev also.
> >
> >The fcoe stack doesn't expects its vlan pkts on real dev
> >and it causes crash in fcoe stack.
>
> Could you please provide more info on where exactly it would crash and
> why?
Its in fcoe stack due to its fip rx skb list getting corrupt as same skb
instance getting queued twice without being cloned, though list was well
protected by its spin lock, it was queued on its two fcoe instances, one
on real dev and other on its vlan.
I could also handle this gracefully in fcoe stack by cloning but any
case netdev should not forward vlan pkt to its read dev pkt handler also
and that is getting fixed with this patch, so patch will restore
orig_dev uses for *only* vlan pkts as it was with recursive
__netif_receive_skb calling prior to commit 0dfe178.
Here is the detailed crash log:
[ 340.679591] BUG: unable to handle kernel NULL pointer dereference at
0000000000000008
[ 340.680112] IP: [<ffffffff815088a5>] skb_dequeue+0x55/0x90
[ 340.680112] PGD 0
[ 340.680112] Oops: 0002 [#1] SMP
[ 340.680112] CPU 3
[ 340.680112] Modules linked in: fcoe libfcoe libfc scsi_transport_fc
8021q e1000 virtio_balloon ixgbe mdio virtio_blk virtio_pci virtio_ring
virtio [last unloaded: scsi_wait_scan]
[ 340.680112]
[ 340.680112] Pid: 442, comm: kworker/3:1 Not tainted 3.2.0-rc4+ #53
Bochs Bochs
[ 340.680112] RIP: 0010:[<ffffffff815088a5>] [<ffffffff815088a5>]
skb_dequeue+0x55/0x90
[ 340.680112] RSP: 0018:ffff88007c963c80 EFLAGS: 00010097
[ 340.680112] RAX: 0000000000000282 RBX: ffff88007baee9b4 RCX:
0000000000000000
[ 340.680112] RDX: 0000000000000000 RSI: 0000000000000286 RDI:
ffff88007baee9b4
[ 340.680112] RBP: ffff88007c963ca0 R08: ffff88007c35ddc0 R09:
0000000000000001
[ 340.680112] R10: 0000000000000006 R11: 0000000000000001 R12:
ffff88007bedca00
[ 340.680112] R13: ffff88007baee9a0 R14: ffff88007c963d80 R15:
ffff88007baeea00
[ 340.680112] FS: 0000000000000000(0000) GS:ffff88007fd80000(0000)
knlGS:0000000000000000
[ 340.680112] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b
[ 340.680112] CR2: 0000000000000008 CR3: 0000000001c05000 CR4:
00000000000006e0
[ 340.680112] DR0: 0000000000000000 DR1: 0000000000000000 DR2:
0000000000000000
[ 340.680112] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7:
0000000000000400
[ 340.680112] Process kworker/3:1 (pid: 442, threadinfo
ffff88007c962000, task ffff88007c9f60b0)
[ 340.680112] Stack:
[ 340.680112] ffff88007baee9a0 ffff88007baee8c0 ffff88007baee9a0
ffff88007bedca00
[ 340.680112] ffff88007c963df0 ffffffffa00af7a7 ffff88007baad4d4
ffffffff81060dee
[ 340.680112] 0000000000000001 ffff88007c35ddc0 ffff88007c963fd8
0000000000000000
[ 340.680112] Call Trace:
[ 340.680112] [<ffffffffa00af7a7>] fcoe_ctlr_recv_work+0x147/0x1870
[libfcoe]
[ 340.680112] [<ffffffff81060dee>] ? queue_delayed_work_on+0x9e/0x170
[ 340.680112] [<ffffffffa00af660>] ? fcoe_ctlr_vn_recv+0x9a0/0x9a0
[libfcoe]
[ 340.680112] [<ffffffff810612de>] process_one_work+0x11e/0x460
[ 340.680112] [<ffffffff81063af8>] worker_thread+0x178/0x400
[ 340.680112] [<ffffffff81063980>] ? manage_workers+0x210/0x210
[ 340.680112] [<ffffffff81068576>] kthread+0x96/0xa0
[ 340.680112] [<ffffffff81663c74>] kernel_thread_helper+0x4/0x10
[ 340.680112] [<ffffffff810684e0>] ? kthread_worker_fn+0x1a0/0x1a0
[ 340.680112] [<ffffffff81663c70>] ? gs_change+0xb/0xb
[ 340.680112] Code: 65 00 4d 39 e5 74 4f 4d 85 e4 74 26 41 83 6d 10 01
49 8b 0c 24 49 8b 54 24 08 49 c7 04 24 00 00 00 00 49 c7 44 24 08 00 00
00 00
[ 340.680112] 89 51 08 48 89 0a 48 89 c6 48 89 df e8 39 1b 15 00 4c 89
e0
[ 340.680112] RIP [<ffffffff815088a5>] skb_dequeue+0x55/0x90
[ 340.680112] RSP <ffff88007c963c80>
[ 340.680112] CR2: 0000000000000008
Thanks
Vasu
>
> Thanks.
>
> Jirka
>
> >
> >This wasn't issue untill __netif_receive_skb recursive calling
> >was removed with this commit 0dfe178, so this patch restores
> >orig_dev uses as it was prior to that commit but still w/o
> >recursive calling to __netif_receive_skb.
> >
> >Signed-off-by: Vasu Dev <vasu.dev@intel.com>
> >---
> >
> > net/core/dev.c | 5 +++--
> > 1 files changed, 3 insertions(+), 2 deletions(-)
> >
> >diff --git a/net/core/dev.c b/net/core/dev.c
> >index f494675..adbcd7a 100644
> >--- a/net/core/dev.c
> >+++ b/net/core/dev.c
> >@@ -3222,9 +3222,10 @@ ncls:
> > ret = deliver_skb(skb, pt_prev, orig_dev);
> > pt_prev = NULL;
> > }
> >- if (vlan_do_receive(&skb, !rx_handler))
> >+ if (vlan_do_receive(&skb, !rx_handler)) {
> >+ orig_dev = skb->dev;
> > goto another_round;
> >- else if (unlikely(!skb))
> >+ } else if (unlikely(!skb))
> > goto out;
> > }
> >
> >
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 1/1] net/fec: add KSZ9021RN phy fixup
From: Richard Zhao @ 2011-12-13 2:10 UTC (permalink / raw)
To: Veli-Pekka Peltola, s.hauer, shawn.guo
Cc: Fabio Estevam, patches, netdev, jgq516, eric.miao, davem,
Richard Zhao, linux-arm-kernel
In-Reply-To: <4EE612BF.9050907@bluegiga.com>
On Mon, Dec 12, 2011 at 04:42:07PM +0200, Veli-Pekka Peltola wrote:
> On 12/12/2011 03:51 PM, Richard Zhao wrote:
> >On Mon, Dec 12, 2011 at 10:56:48AM -0200, Fabio Estevam wrote:
> >>On Mon, Dec 12, 2011 at 8:09 AM, Richard Zhao<richard.zhao@linaro.org> wrote:
> >>>For imx6q sabrelite board, set phy RGMII pad skew.
> >>>
> >>>Signed-off-by: Richard Zhao<richard.zhao@linaro.org>
> >>>---
> >>> drivers/net/ethernet/freescale/fec.c | 19 +++++++++++++++++++
> >>> 1 files changed, 19 insertions(+), 0 deletions(-)
> >>>
> >>>diff --git a/drivers/net/ethernet/freescale/fec.c b/drivers/net/ethernet/freescale/fec.c
> >>>index 112af9b..d3b4463 100644
> >>>--- a/drivers/net/ethernet/freescale/fec.c
> >>>+++ b/drivers/net/ethernet/freescale/fec.c
> >>>@@ -1513,6 +1513,21 @@ static inline void fec_reset_phy(struct platform_device *pdev)
> >>> }
> >>> #endif /* CONFIG_OF */
> >>>
> >>>+/* For imx6q sabrelite board: set KSZ9021RN RGMII pad skew */
> >>>+static int ksz9021rn_phy_fixup(struct phy_device *phydev)
> >>>+{
> >>>+ /* min rx data delay */
> >>>+ phy_write(phydev, 0x0b, 0x8105);
> >>>+ phy_write(phydev, 0x0c, 0x0000);
> >>>+
> >>>+ /* max rx/tx clock delay, min rx/tx control delay */
> >>>+ phy_write(phydev, 0x0b, 0x8104);
> >>>+ phy_write(phydev, 0x0c, 0xf0f0);
> >>>+ phy_write(phydev, 0x0b, 0x104);
> >>>+
> >>>+ return 0;
> >>
> >>This should go to drivers/net/phy/micrel.c.
> >Why? It's specific to fec.
>
> For me it seems to be a board specific and should be placed to the
> machine file.
By converting to DT, machine code's becoming common too. I feel hard to
find a place. Put it in imx6q_init_machine?
Sascha & Shawn, Could you give comments here?
> Please include linux/micrel_phy.h and use proper
> defines (PHY_ID_KSZ9021, MICREL_PHY_ID_MASK).
right. thanks.
Richard
>
> --
> Veli-Pekka Peltola
>
^ permalink raw reply
* Re: [PATCH 1/1] net/fec: add KSZ9021RN phy fixup
From: Shawn Guo @ 2011-12-13 2:36 UTC (permalink / raw)
To: Richard Zhao
Cc: Veli-Pekka Peltola, s.hauer, shawn.guo, Fabio Estevam, patches,
netdev, jgq516, eric.miao, davem, Richard Zhao, linux-arm-kernel
In-Reply-To: <20111213021008.GE28768@b20223-02.ap.freescale.net>
On Tue, Dec 13, 2011 at 10:10:08AM +0800, Richard Zhao wrote:
> By converting to DT, machine code's becoming common too. I feel hard to
> find a place. Put it in imx6q_init_machine?
>
> Sascha & Shawn, Could you give comments here?
>
I see no problem to do that by checking board compatible string and
do the setup for particular board.
--
Regards,
Shawn
^ permalink raw reply
* Re: [PATCH 1/1] net/fec: add KSZ9021RN phy fixup
From: Jason Liu @ 2011-12-13 2:29 UTC (permalink / raw)
To: Shawn Guo
Cc: Richard Zhao, patches, netdev, s.hauer, jgq516, eric.miao,
Richard Zhao, Veli-Pekka Peltola, shawn.guo, Fabio Estevam, davem,
linux-arm-kernel
In-Reply-To: <20111213023626.GA10866@S2100-06.ap.freescale.net>
2011/12/13 Shawn Guo <shawn.guo@freescale.com>:
> On Tue, Dec 13, 2011 at 10:10:08AM +0800, Richard Zhao wrote:
>> By converting to DT, machine code's becoming common too. I feel hard to
>> find a place. Put it in imx6q_init_machine?
>>
>> Sascha & Shawn, Could you give comments here?
>>
> I see no problem to do that by checking board compatible string and
> do the setup for particular board.
Then, you may end up having more and more fix up in the fec driver
when all the platforms converted to DT,
which will put the fec driver into mess.
>
> --
> Regards,
> Shawn
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 1/1] net/fec: add KSZ9021RN phy fixup
From: Shawn Guo @ 2011-12-13 2:47 UTC (permalink / raw)
To: Jason Liu
Cc: Richard Zhao, patches, netdev, s.hauer, jgq516, eric.miao,
Richard Zhao, Veli-Pekka Peltola, shawn.guo, Fabio Estevam, davem,
linux-arm-kernel
In-Reply-To: <CAB4PhKfSegpwrZbedN7KtJd+xFk1JAOCWuSFq2you5dWn9bqwA@mail.gmail.com>
On Tue, Dec 13, 2011 at 10:29:53AM +0800, Jason Liu wrote:
> 2011/12/13 Shawn Guo <shawn.guo@freescale.com>:
> > On Tue, Dec 13, 2011 at 10:10:08AM +0800, Richard Zhao wrote:
> >> By converting to DT, machine code's becoming common too. I feel hard to
> >> find a place. Put it in imx6q_init_machine?
> >>
> >> Sascha & Shawn, Could you give comments here?
> >>
> > I see no problem to do that by checking board compatible string and
> > do the setup for particular board.
>
> Then, you may end up having more and more fix up in the fec driver
> when all the platforms converted to DT,
> which will put the fec driver into mess.
>
I meant we do this in imx6q_init_machine().
--
Regards,
Shawn
^ permalink raw reply
* Re: [PATCH RFC] virtio_net: fix refill related races
From: Rusty Russell @ 2011-12-13 2:35 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: Amit Shah, netdev, Tejun Heo, linux-kernel, virtualization
In-Reply-To: <20111212115405.GB7946@redhat.com>
On Mon, 12 Dec 2011 13:54:06 +0200, "Michael S. Tsirkin" <mst@redhat.com> wrote:
> On Mon, Dec 12, 2011 at 09:25:07AM +1030, Rusty Russell wrote:
> > Orthogonally, the refill-stop code is still buggy, as you noted.
>
> Sorry I don't understand how it's still buggy.
Both places where we call:
cancel_delayed_work_sync(&vi->refill);
Do not actually guarantee that vi->refill isn't running, because it
can requeue itself. A 'bool no_more_refill' field seems like the
simplest fix for this, but I don't think it's sufficient.
Tejun, is this correct? What's the correct way to synchronously stop a
delayed_work which can "schedule_delayed_work(&vi->refill, HZ/2);" on
itself?
Thanks,
Rusty.
^ permalink raw reply
* [PATCH net-next 2/2] igb: offer a PTP Hardware Clock instead of the timecompare method
From: Richard Cochran @ 2011-12-13 3:00 UTC (permalink / raw)
To: netdev
Cc: e1000-devel, John Ronciak, John Stultz, Jacob Keller,
Thomas Gleixner
In-Reply-To: <cover.1323744724.git.richardcochran@gmail.com>
This commit removes the legacy timecompare code from the igb driver and
offers a tunable PHC instead.
Signed-off-by: Richard Cochran <richardcochran@gmail.com>
---
drivers/net/ethernet/intel/igb/Makefile | 2 +-
drivers/net/ethernet/intel/igb/igb.h | 13 ++-
drivers/net/ethernet/intel/igb/igb_main.c | 167 +----------------------------
drivers/net/ethernet/intel/igb/igb_ptp.c | 42 +++++++
4 files changed, 53 insertions(+), 171 deletions(-)
diff --git a/drivers/net/ethernet/intel/igb/Makefile b/drivers/net/ethernet/intel/igb/Makefile
index c6e4621..42f0868 100644
--- a/drivers/net/ethernet/intel/igb/Makefile
+++ b/drivers/net/ethernet/intel/igb/Makefile
@@ -32,6 +32,6 @@
obj-$(CONFIG_IGB) += igb.o
-igb-objs := igb_main.o igb_ethtool.o e1000_82575.o \
+igb-objs := igb_main.o igb_ethtool.o igb_ptp.o e1000_82575.o \
e1000_mac.o e1000_nvm.o e1000_phy.o e1000_mbx.o
diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h
index 001f95d..2ea5e91 100644
--- a/drivers/net/ethernet/intel/igb/igb.h
+++ b/drivers/net/ethernet/intel/igb/igb.h
@@ -34,8 +34,6 @@
#include "e1000_mac.h"
#include "e1000_82575.h"
-#include <linux/clocksource.h>
-#include <linux/timecompare.h>
#include <linux/net_tstamp.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/bitops.h>
@@ -329,9 +327,6 @@ struct igb_adapter {
/* OS defined structs */
struct pci_dev *pdev;
- struct cyclecounter cycles;
- struct timecounter clock;
- struct timecompare compare;
struct hwtstamp_config hwtstamp_config;
spinlock_t stats64_lock;
@@ -386,7 +381,6 @@ struct igb_adapter {
#define IGB_DMCTLX_DCFLUSH_DIS 0x80000000 /* Disable DMA Coal Flush */
#define IGB_82576_TSYNC_SHIFT 19
-#define IGB_82580_TSYNC_SHIFT 24
#define IGB_TS_HDR_LEN 16
enum e1000_state_t {
__IGB_TESTING,
@@ -423,6 +417,13 @@ extern bool igb_has_link(struct igb_adapter *adapter);
extern void igb_set_ethtool_ops(struct net_device *);
extern void igb_power_up_link(struct igb_adapter *);
+extern void igb_ptp_init(struct igb_adapter *adapter);
+extern void igb_ptp_remove(struct igb_adapter *adapter);
+
+extern void igb_systim_to_hwtstamp(struct igb_adapter *adapter,
+ struct skb_shared_hwtstamps *hwtstamps,
+ u64 systim);
+
static inline s32 igb_reset_phy(struct e1000_hw *hw)
{
if (hw->phy.ops.reset)
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
index 89d576c..0ee04b9 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -113,7 +113,6 @@ static void igb_free_all_rx_resources(struct igb_adapter *);
static void igb_setup_mrqc(struct igb_adapter *);
static int igb_probe(struct pci_dev *, const struct pci_device_id *);
static void __devexit igb_remove(struct pci_dev *pdev);
-static void igb_init_hw_timer(struct igb_adapter *adapter);
static int igb_sw_init(struct igb_adapter *);
static int igb_open(struct net_device *);
static int igb_close(struct net_device *);
@@ -549,33 +548,6 @@ exit:
return;
}
-
-/**
- * igb_read_clock - read raw cycle counter (to be used by time counter)
- */
-static cycle_t igb_read_clock(const struct cyclecounter *tc)
-{
- struct igb_adapter *adapter =
- container_of(tc, struct igb_adapter, cycles);
- struct e1000_hw *hw = &adapter->hw;
- u64 stamp = 0;
- int shift = 0;
-
- /*
- * The timestamp latches on lowest register read. For the 82580
- * the lowest register is SYSTIMR instead of SYSTIML. However we never
- * adjusted TIMINCA so SYSTIMR will just read as all 0s so ignore it.
- */
- if (hw->mac.type >= e1000_82580) {
- stamp = rd32(E1000_SYSTIMR) >> 8;
- shift = IGB_82580_TSYNC_SHIFT;
- }
-
- stamp |= (u64)rd32(E1000_SYSTIML) << shift;
- stamp |= (u64)rd32(E1000_SYSTIMH) << (shift + 32);
- return stamp;
-}
-
/**
* igb_get_hw_dev - return device
* used by hardware layer to print debugging information
@@ -2080,7 +2052,7 @@ static int __devinit igb_probe(struct pci_dev *pdev,
#endif
/* do hw tstamp init after resetting */
- igb_init_hw_timer(adapter);
+ igb_ptp_init(adapter);
dev_info(&pdev->dev, "Intel(R) Gigabit Ethernet Network Connection\n");
/* print bus type/speed/width info */
@@ -2150,6 +2122,8 @@ static void __devexit igb_remove(struct pci_dev *pdev)
struct igb_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
+ igb_ptp_remove(adapter);
+
/*
* The watchdog timer may be rescheduled, so explicitly
* disable watchdog from being rescheduled.
@@ -2269,112 +2243,6 @@ out:
}
/**
- * igb_init_hw_timer - Initialize hardware timer used with IEEE 1588 timestamp
- * @adapter: board private structure to initialize
- *
- * igb_init_hw_timer initializes the function pointer and values for the hw
- * timer found in hardware.
- **/
-static void igb_init_hw_timer(struct igb_adapter *adapter)
-{
- struct e1000_hw *hw = &adapter->hw;
-
- switch (hw->mac.type) {
- case e1000_i350:
- case e1000_82580:
- memset(&adapter->cycles, 0, sizeof(adapter->cycles));
- adapter->cycles.read = igb_read_clock;
- adapter->cycles.mask = CLOCKSOURCE_MASK(64);
- adapter->cycles.mult = 1;
- /*
- * The 82580 timesync updates the system timer every 8ns by 8ns
- * and the value cannot be shifted. Instead we need to shift
- * the registers to generate a 64bit timer value. As a result
- * SYSTIMR/L/H, TXSTMPL/H, RXSTMPL/H all have to be shifted by
- * 24 in order to generate a larger value for synchronization.
- */
- adapter->cycles.shift = IGB_82580_TSYNC_SHIFT;
- /* disable system timer temporarily by setting bit 31 */
- wr32(E1000_TSAUXC, 0x80000000);
- wrfl();
-
- /* Set registers so that rollover occurs soon to test this. */
- wr32(E1000_SYSTIMR, 0x00000000);
- wr32(E1000_SYSTIML, 0x80000000);
- wr32(E1000_SYSTIMH, 0x000000FF);
- wrfl();
-
- /* enable system timer by clearing bit 31 */
- wr32(E1000_TSAUXC, 0x0);
- wrfl();
-
- timecounter_init(&adapter->clock,
- &adapter->cycles,
- ktime_to_ns(ktime_get_real()));
- /*
- * Synchronize our NIC clock against system wall clock. NIC
- * time stamp reading requires ~3us per sample, each sample
- * was pretty stable even under load => only require 10
- * samples for each offset comparison.
- */
- memset(&adapter->compare, 0, sizeof(adapter->compare));
- adapter->compare.source = &adapter->clock;
- adapter->compare.target = ktime_get_real;
- adapter->compare.num_samples = 10;
- timecompare_update(&adapter->compare, 0);
- break;
- case e1000_82576:
- /*
- * Initialize hardware timer: we keep it running just in case
- * that some program needs it later on.
- */
- memset(&adapter->cycles, 0, sizeof(adapter->cycles));
- adapter->cycles.read = igb_read_clock;
- adapter->cycles.mask = CLOCKSOURCE_MASK(64);
- adapter->cycles.mult = 1;
- /**
- * Scale the NIC clock cycle by a large factor so that
- * relatively small clock corrections can be added or
- * subtracted at each clock tick. The drawbacks of a large
- * factor are a) that the clock register overflows more quickly
- * (not such a big deal) and b) that the increment per tick has
- * to fit into 24 bits. As a result we need to use a shift of
- * 19 so we can fit a value of 16 into the TIMINCA register.
- */
- adapter->cycles.shift = IGB_82576_TSYNC_SHIFT;
- wr32(E1000_TIMINCA,
- (1 << E1000_TIMINCA_16NS_SHIFT) |
- (16 << IGB_82576_TSYNC_SHIFT));
-
- /* Set registers so that rollover occurs soon to test this. */
- wr32(E1000_SYSTIML, 0x00000000);
- wr32(E1000_SYSTIMH, 0xFF800000);
- wrfl();
-
- timecounter_init(&adapter->clock,
- &adapter->cycles,
- ktime_to_ns(ktime_get_real()));
- /*
- * Synchronize our NIC clock against system wall clock. NIC
- * time stamp reading requires ~3us per sample, each sample
- * was pretty stable even under load => only require 10
- * samples for each offset comparison.
- */
- memset(&adapter->compare, 0, sizeof(adapter->compare));
- adapter->compare.source = &adapter->clock;
- adapter->compare.target = ktime_get_real;
- adapter->compare.num_samples = 10;
- timecompare_update(&adapter->compare, 0);
- break;
- case e1000_82575:
- /* 82575 does not support timesync */
- default:
- break;
- }
-
-}
-
-/**
* igb_sw_init - Initialize general software structures (struct igb_adapter)
* @adapter: board private structure to initialize
*
@@ -5628,35 +5496,6 @@ static int igb_poll(struct napi_struct *napi, int budget)
}
/**
- * igb_systim_to_hwtstamp - convert system time value to hw timestamp
- * @adapter: board private structure
- * @shhwtstamps: timestamp structure to update
- * @regval: unsigned 64bit system time value.
- *
- * We need to convert the system time value stored in the RX/TXSTMP registers
- * into a hwtstamp which can be used by the upper level timestamping functions
- */
-static void igb_systim_to_hwtstamp(struct igb_adapter *adapter,
- struct skb_shared_hwtstamps *shhwtstamps,
- u64 regval)
-{
- u64 ns;
-
- /*
- * The 82580 starts with 1ns at bit 0 in RX/TXSTMPL, shift this up to
- * 24 to match clock shift we setup earlier.
- */
- if (adapter->hw.mac.type >= e1000_82580)
- regval <<= IGB_82580_TSYNC_SHIFT;
-
- ns = timecounter_cyc2time(&adapter->clock, regval);
- timecompare_update(&adapter->compare, ns);
- memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps));
- shhwtstamps->hwtstamp = ns_to_ktime(ns);
- shhwtstamps->syststamp = timecompare_transform(&adapter->compare, ns);
-}
-
-/**
* igb_tx_hwtstamp - utility function which checks for TX time stamp
* @q_vector: pointer to q_vector containing needed info
* @buffer: pointer to igb_tx_buffer structure
diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c
index 7a9f6bc..7a62980 100644
--- a/drivers/net/ethernet/intel/igb/igb_ptp.c
+++ b/drivers/net/ethernet/intel/igb/igb_ptp.c
@@ -406,3 +406,45 @@ void igb_ptp_remove(struct igb_adapter *adapter)
adapter->netdev->name);
}
}
+
+/**
+ * igb_systim_to_hwtstamp - convert system time value to hw timestamp
+ * @adapter: board private structure
+ * @shhwtstamps: timestamp structure to update
+ * @systim: unsigned 64bit system time value.
+ *
+ * We need to convert the system time value stored in the RX/TXSTMP registers
+ * into a hwtstamp which can be used by the upper level timestamping functions
+ */
+void igb_systim_to_hwtstamp(struct igb_adapter *adapter,
+ struct skb_shared_hwtstamps *hwtstamps,
+ u64 systim)
+{
+ u64 ns;
+ unsigned long flags;
+ unsigned int shift;
+ int msb_set;
+
+ switch (adapter->hw.mac.type) {
+ case e1000_i350:
+ case e1000_82580:
+ shift = OFL_SHIFT_82580;
+ msb_set = (systim >> 32) & SYSTIMH_MSB_82580;
+ break;
+ case e1000_82576:
+ shift = OFL_SHIFT_82576;
+ msb_set = (systim >> 32) & SYSTIMH_MSB_82576;
+ break;
+ default:
+ return;
+ }
+
+ spin_lock_irqsave(&adapter->tmreg_lock, flags);
+
+ ns = igb_overflow_get(adapter, systim, msb_set, shift);
+
+ spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
+
+ memset(hwtstamps, 0, sizeof(*hwtstamps));
+ hwtstamps->hwtstamp = ns_to_ktime(ns);
+}
--
1.7.2.5
------------------------------------------------------------------------------
Systems Optimization Self Assessment
Improve efficiency and utilization of IT resources. Drive out cost and
improve service delivery. Take 5 minutes to use this Systems Optimization
Self Assessment. http://www.accelacomm.com/jaw/sdnl/114/51450054/
_______________________________________________
E1000-devel mailing list
E1000-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/e1000-devel
To learn more about Intel® Ethernet, visit http://communities.intel.com/community/wired
^ permalink raw reply related
* [PATCH net-next 0/2] [RFC] igb: ptp hardware clock
From: Richard Cochran @ 2011-12-13 3:00 UTC (permalink / raw)
To: netdev
Cc: e1000-devel, Jacob Keller, Jeff Kirsher, John Ronciak,
John Stultz, Thomas Gleixner
This patch series implements a PHC driver for the Intel 82576 and
82580 devices, as part of the igb driver.
The first patch adds the PHC driver code as a new source module but
does not link it into the main igb driver. Because the system time
counter is not so very wide, the code implements an overflow counter
in software. Every read operation maintains the overflow counter, as
does a "delayed work" watchdog. Only the base clock operations are
implemented. The hardware does have some ancillary features, but these
can be easily added later.
The second patch removes the timecompare code and links in the new
functions.
I have tested the 82580 with good results. However, I don't have the
82576 and so would appreciate testing and feedback.
Thanks,
Richard
Richard Cochran (2):
igb: add PTP Hardware Clock code
igb: offer a PTP Hardware Clock instead of the timecompare method
drivers/net/ethernet/intel/igb/Makefile | 2 +-
drivers/net/ethernet/intel/igb/igb.h | 21 +-
drivers/net/ethernet/intel/igb/igb_main.c | 167 +-----------
drivers/net/ethernet/intel/igb/igb_ptp.c | 450 +++++++++++++++++++++++++++++
4 files changed, 469 insertions(+), 171 deletions(-)
create mode 100644 drivers/net/ethernet/intel/igb/igb_ptp.c
--
1.7.2.5
^ permalink raw reply
* [PATCH net-next 1/2] igb: add PTP Hardware Clock code
From: Richard Cochran @ 2011-12-13 3:00 UTC (permalink / raw)
To: netdev
Cc: e1000-devel, Jacob Keller, Jeff Kirsher, John Ronciak,
John Stultz, Thomas Gleixner
In-Reply-To: <cover.1323744724.git.richardcochran@gmail.com>
This patch adds a source file implementing a PHC. Only the basic
clock operations have been implemented, although the hardware
would offer some ancillary functions. The code is fairly self
contained and is not yet used in the main igb driver.
Signed-off-by: Richard Cochran <richardcochran@gmail.com>
---
drivers/net/ethernet/intel/igb/igb.h | 8 +
drivers/net/ethernet/intel/igb/igb_ptp.c | 408 ++++++++++++++++++++++++++++++
2 files changed, 416 insertions(+), 0 deletions(-)
create mode 100644 drivers/net/ethernet/intel/igb/igb_ptp.c
diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h
index c69feeb..001f95d 100644
--- a/drivers/net/ethernet/intel/igb/igb.h
+++ b/drivers/net/ethernet/intel/igb/igb.h
@@ -37,6 +37,7 @@
#include <linux/clocksource.h>
#include <linux/timecompare.h>
#include <linux/net_tstamp.h>
+#include <linux/ptp_clock_kernel.h>
#include <linux/bitops.h>
#include <linux/if_vlan.h>
@@ -364,6 +365,13 @@ struct igb_adapter {
u32 wvbr;
int node;
u32 *shadow_vfta;
+
+ struct ptp_clock *ptp_clock;
+ struct ptp_clock_info caps;
+ struct delayed_work overflow_work;
+ spinlock_t tmreg_lock;
+ u32 overflow_counter;
+ int last_msb;
};
#define IGB_FLAG_HAS_MSI (1 << 0)
diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c
new file mode 100644
index 0000000..7a9f6bc
--- /dev/null
+++ b/drivers/net/ethernet/intel/igb/igb_ptp.c
@@ -0,0 +1,408 @@
+/*
+ * PTP Hardware Clock (PHC) driver for the Intel 82576 and 82580
+ *
+ * Copyright (C) 2011 Richard Cochran <richardcochran@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include <linux/device.h>
+#include <linux/pci.h>
+
+#include "igb.h"
+
+#define INCVALUE_MASK 0x7fffffff
+#define ISGN 0x80000000
+#define SYSTIMH_MSB_82576 0x80000000
+#define SYSTIMH_MSB_82580 0x00000080
+
+/*
+ * Neither the 82576 nor the 82580 offer registers wide enough to hold
+ * nanoseconds time values for very long. For the 82580, SYSTIM always
+ * counts nanoseconds, but the upper 24 bits are not availible. The
+ * frequency is adjusted by changing the 32 bit fractional nanoseconds
+ * register, TIMINCA.
+ *
+ * For the 82576, the SYSTIM register time unit is affect by the
+ * choice of the 24 bit TININCA:IV (incvalue) field. Five bits of this
+ * field are needed to provide the nominal 16 nanosecond period,
+ * leaving 19 bits for fractional nanoseconds.
+ *
+ *
+ * SYSTIMH SYSTIML
+ * +--------------+ +---+---+------+
+ * 82576 | 32 | | 8 | 5 | 19 |
+ * +--------------+ +---+---+------+
+ * \________ 45 bits _______/ fract
+ *
+ * +----------+---+ +--------------+
+ * 82580 | 24 | 8 | | 32 |
+ * +----------+---+ +--------------+
+ * reserved \______ 40 bits _____/
+ *
+ *
+ * The 45 bit 82576 SYSTIM overflows every
+ * 2^45 * 10^-9 / 3600 = 9.77 hours.
+ *
+ * The 40 bit 82580 SYSTIM overflows every
+ * 2^40 * 10^-9 / 60 = 18.3 minutes.
+ *
+ * We implement a 32 bit overflow counter in software to hold the
+ * missing 19 or 24 bits, for a combined virtual nanosecond time
+ * register of 64 bits.
+ */
+
+#define IGB_OVERFLOW_PERIOD (HZ * 60 * 5)
+#define INCPERIOD_82576 (1 << E1000_TIMINCA_16NS_SHIFT)
+#define INCVALUE_82576_MASK ((1 << E1000_TIMINCA_16NS_SHIFT) - 1)
+#define INCVALUE_82576 (16 << IGB_82576_TSYNC_SHIFT)
+#define OFL_SHIFT_82580 40
+#define OFL_SHIFT_82576 45
+#define NS_SHIFT_82576 IGB_82576_TSYNC_SHIFT
+
+/*
+ * Overflow counter for the SYSTIM register.
+ */
+
+static u64 igb_overflow_get(struct igb_adapter *igb, u64 low, int msb_set,
+ unsigned int shift)
+{
+ if (igb->last_msb && !msb_set)
+ igb->overflow_counter++;
+
+ igb->last_msb = msb_set;
+
+ low |= ((u64) igb->overflow_counter) << shift;
+
+ return low;
+}
+
+static void igb_overflow_set(struct igb_adapter *igb, u64 ns, int msb_set,
+ unsigned int shift)
+{
+ igb->overflow_counter = ns >> shift;
+ igb->last_msb = msb_set;
+}
+
+/*
+ * SYSTIM / overflow access functions for the 82576
+ */
+
+static u64 igb_82576_systim_read(struct igb_adapter *igb)
+{
+ u64 ns;
+ u32 lo, hi;
+ struct e1000_hw *hw = &igb->hw;
+
+ lo = rd32(E1000_SYSTIML);
+ hi = rd32(E1000_SYSTIMH);
+
+ ns = ((u64) hi) << 32;
+ ns |= lo;
+ ns >>= NS_SHIFT_82576;
+
+ ns = igb_overflow_get(igb, ns, hi & SYSTIMH_MSB_82576, OFL_SHIFT_82576);
+
+ return ns;
+}
+
+static void igb_82576_systim_write(struct igb_adapter *igb, u64 ns)
+{
+ u32 hi, lo;
+ struct e1000_hw *hw = &igb->hw;
+
+ hi = (ns >> 8) & 0xffffffff;
+ lo = (ns & 0xff) << NS_SHIFT_82576;
+
+ wr32(E1000_SYSTIML, lo);
+ wr32(E1000_SYSTIMH, hi);
+
+ igb_overflow_set(igb, ns, hi & SYSTIMH_MSB_82576, OFL_SHIFT_82576);
+}
+
+/*
+ * SYSTIM / overflow access functions for the 82580
+ */
+
+static u64 igb_82580_systim_read(struct igb_adapter *igb)
+{
+ u64 ns;
+ u32 lo, hi, jk;
+ struct e1000_hw *hw = &igb->hw;
+
+ jk = rd32(E1000_SYSTIMR);
+ lo = rd32(E1000_SYSTIML);
+ hi = rd32(E1000_SYSTIMH);
+
+ ns = ((u64) hi) << 32;
+ ns |= lo;
+
+ ns = igb_overflow_get(igb, ns, hi & SYSTIMH_MSB_82580, OFL_SHIFT_82580);
+
+ return ns;
+}
+
+static void igb_82580_systim_write(struct igb_adapter *igb, u64 ns)
+{
+ u32 hi, lo;
+ struct e1000_hw *hw = &igb->hw;
+
+ hi = ns >> 32;
+ lo = ns & 0xffffffff;
+
+ wr32(E1000_SYSTIMR, 0);
+ wr32(E1000_SYSTIML, lo);
+ wr32(E1000_SYSTIMH, hi & 0xff);
+
+ igb_overflow_set(igb, ns, hi & SYSTIMH_MSB_82580, OFL_SHIFT_82580);
+}
+
+/*
+ * SYSTIM / overflow register access functions
+ * Callers must hold tmreg_lock.
+ */
+
+static u64 igb_systim_read(struct igb_adapter *igb)
+{
+ switch (igb->hw.mac.type) {
+ case e1000_i350:
+ case e1000_82580:
+ return igb_82580_systim_read(igb);
+ case e1000_82576:
+ return igb_82576_systim_read(igb);
+ default:
+ return 0;
+ }
+}
+
+static void igb_systim_write(struct igb_adapter *igb, u64 ns)
+{
+ switch (igb->hw.mac.type) {
+ case e1000_i350:
+ case e1000_82580:
+ igb_82580_systim_write(igb, ns);
+ break;
+ case e1000_82576:
+ igb_82576_systim_write(igb, ns);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * PTP clock operations
+ */
+
+static int ptp_82576_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
+{
+ u64 rate;
+ u32 incvalue;
+ int neg_adj = 0;
+ struct igb_adapter *igb = container_of(ptp, struct igb_adapter, caps);
+ struct e1000_hw *hw = &igb->hw;
+
+ if (ppb < 0) {
+ neg_adj = 1;
+ ppb = -ppb;
+ }
+ rate = ppb;
+ rate <<= 14;
+ rate = div_u64(rate, 1953125);
+
+ incvalue = 16 << IGB_82576_TSYNC_SHIFT;
+
+ if (neg_adj)
+ incvalue -= rate;
+ else
+ incvalue += rate;
+
+ wr32(E1000_TIMINCA, INCPERIOD_82576 | (incvalue & INCVALUE_82576_MASK));
+
+ return 0;
+}
+
+static int ptp_82580_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
+{
+ u64 rate;
+ u32 inca;
+ int neg_adj = 0;
+ struct igb_adapter *igb = container_of(ptp, struct igb_adapter, caps);
+ struct e1000_hw *hw = &igb->hw;
+
+ if (ppb < 0) {
+ neg_adj = 1;
+ ppb = -ppb;
+ }
+ rate = ppb;
+ rate <<= 26;
+ rate = div_u64(rate, 1953125);
+
+ inca = rate & INCVALUE_MASK;
+ if (neg_adj)
+ inca |= ISGN;
+
+ wr32(E1000_TIMINCA, inca);
+
+ return 0;
+}
+
+static int igb_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+ s64 now;
+ unsigned long flags;
+ struct igb_adapter *igb = container_of(ptp, struct igb_adapter, caps);
+
+ spin_lock_irqsave(&igb->tmreg_lock, flags);
+
+ now = igb_systim_read(igb);
+ now += delta;
+ igb_systim_write(igb, now);
+
+ spin_unlock_irqrestore(&igb->tmreg_lock, flags);
+
+ return 0;
+}
+
+static int igb_gettime(struct ptp_clock_info *ptp, struct timespec *ts)
+{
+ u64 ns;
+ u32 remainder;
+ unsigned long flags;
+ struct igb_adapter *igb = container_of(ptp, struct igb_adapter, caps);
+
+ spin_lock_irqsave(&igb->tmreg_lock, flags);
+
+ ns = igb_systim_read(igb);
+
+ spin_unlock_irqrestore(&igb->tmreg_lock, flags);
+
+ ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder);
+ ts->tv_nsec = remainder;
+
+ return 0;
+}
+
+static int igb_settime(struct ptp_clock_info *ptp, const struct timespec *ts)
+{
+ u64 ns;
+ unsigned long flags;
+ struct igb_adapter *igb = container_of(ptp, struct igb_adapter, caps);
+
+ ns = ts->tv_sec * 1000000000ULL;
+ ns += ts->tv_nsec;
+
+ spin_lock_irqsave(&igb->tmreg_lock, flags);
+
+ igb_systim_write(igb, ns);
+
+ spin_unlock_irqrestore(&igb->tmreg_lock, flags);
+
+ return 0;
+}
+
+static int ptp_82576_enable(struct ptp_clock_info *ptp,
+ struct ptp_clock_request *rq, int on)
+{
+ return -EOPNOTSUPP;
+}
+
+static int ptp_82580_enable(struct ptp_clock_info *ptp,
+ struct ptp_clock_request *rq, int on)
+{
+ return -EOPNOTSUPP;
+}
+
+static void igb_overflow_check(struct work_struct *work)
+{
+ struct timespec ts;
+ struct igb_adapter *igb =
+ container_of(work, struct igb_adapter, overflow_work.work);
+
+ igb_gettime(&igb->caps, &ts);
+
+ pr_debug("igb overflow check at %ld.%09lu\n", ts.tv_sec, ts.tv_nsec);
+
+ schedule_delayed_work(&igb->overflow_work, IGB_OVERFLOW_PERIOD);
+}
+
+void igb_ptp_init(struct igb_adapter *adapter)
+{
+ struct e1000_hw *hw = &adapter->hw;
+
+ switch (hw->mac.type) {
+ case e1000_i350:
+ case e1000_82580:
+ adapter->caps.owner = THIS_MODULE;
+ strcpy(adapter->caps.name, "igb-82580");
+ adapter->caps.max_adj = 62499999;
+ adapter->caps.n_ext_ts = 0;
+ adapter->caps.pps = 0;
+ adapter->caps.adjfreq = ptp_82580_adjfreq;
+ adapter->caps.adjtime = igb_adjtime;
+ adapter->caps.gettime = igb_gettime;
+ adapter->caps.settime = igb_settime;
+ adapter->caps.enable = ptp_82580_enable;
+ /* Enable the timer functions. */
+ wr32(E1000_TSAUXC, 0x0);
+ break;
+
+ case e1000_82576:
+ adapter->caps.owner = THIS_MODULE;
+ strcpy(adapter->caps.name, "igb-82576");
+ adapter->caps.max_adj = 1000000000;
+ adapter->caps.n_ext_ts = 0;
+ adapter->caps.pps = 0;
+ adapter->caps.adjfreq = ptp_82576_adjfreq;
+ adapter->caps.adjtime = igb_adjtime;
+ adapter->caps.gettime = igb_gettime;
+ adapter->caps.settime = igb_settime;
+ adapter->caps.enable = ptp_82576_enable;
+ /* Dial the nominal frequency. */
+ wr32(E1000_TIMINCA, INCPERIOD_82576 | INCVALUE_82576);
+ break;
+
+ default:
+ adapter->ptp_clock = NULL;
+ return;
+ }
+
+ wrfl();
+
+ INIT_DELAYED_WORK(&adapter->overflow_work, igb_overflow_check);
+
+ spin_lock_init(&adapter->tmreg_lock);
+
+ adapter->ptp_clock = ptp_clock_register(&adapter->caps);
+ if (IS_ERR(adapter->ptp_clock)) {
+ adapter->ptp_clock = NULL;
+ dev_err(&adapter->pdev->dev, "ptp_clock_register failed\n");
+ return;
+ }
+
+ schedule_delayed_work(&adapter->overflow_work, IGB_OVERFLOW_PERIOD);
+
+ dev_info(&adapter->pdev->dev, "added PHC on %s\n",
+ adapter->netdev->name);
+}
+
+void igb_ptp_remove(struct igb_adapter *adapter)
+{
+ if (adapter->ptp_clock) {
+ cancel_delayed_work_sync(&adapter->overflow_work);
+ ptp_clock_unregister(adapter->ptp_clock);
+ dev_info(&adapter->pdev->dev, "removed PHC on %s\n",
+ adapter->netdev->name);
+ }
+}
--
1.7.2.5
^ permalink raw reply related
* Re: [PATCH net-next 2/2] igb: offer a PTP Hardware Clock instead of the timecompare method
From: Eric Dumazet @ 2011-12-13 3:28 UTC (permalink / raw)
To: Richard Cochran
Cc: netdev, e1000-devel, Jacob Keller, Jeff Kirsher, John Ronciak,
John Stultz, Thomas Gleixner
In-Reply-To: <388385c50909f63bff239894c60dbb00abc65c93.1323744725.git.richardcochran@gmail.com>
Le mardi 13 décembre 2011 à 04:00 +0100, Richard Cochran a écrit :
> This commit removes the legacy timecompare code from the igb driver and
> offers a tunable PHC instead.
>
> Signed-off-by: Richard Cochran <richardcochran@gmail.com>
> ---
> +void igb_systim_to_hwtstamp(struct igb_adapter *adapter,
> + struct skb_shared_hwtstamps *hwtstamps,
> + u64 systim)
> +{
> + u64 ns;
> + unsigned long flags;
> + unsigned int shift;
> + int msb_set;
> +
> + switch (adapter->hw.mac.type) {
> + case e1000_i350:
> + case e1000_82580:
> + shift = OFL_SHIFT_82580;
> + msb_set = (systim >> 32) & SYSTIMH_MSB_82580;
> + break;
> + case e1000_82576:
> + shift = OFL_SHIFT_82576;
> + msb_set = (systim >> 32) & SYSTIMH_MSB_82576;
> + break;
> + default:
> + return;
> + }
> +
> + spin_lock_irqsave(&adapter->tmreg_lock, flags);
> +
> + ns = igb_overflow_get(adapter, systim, msb_set, shift);
> +
> + spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
> +
> + memset(hwtstamps, 0, sizeof(*hwtstamps));
> + hwtstamps->hwtstamp = ns_to_ktime(ns);
> +}
Adding a (shared) spinlock on a multiqueue device is source of extra
delay (because of extra cache line trafic), I guess.
It seems current code doesnt need a spinlock, maybe it was a bug ?
^ permalink raw reply
* Re: [PATCH net-next 2/2] igb: offer a PTP Hardware Clock instead of the timecompare method
From: Richard Cochran @ 2011-12-13 3:41 UTC (permalink / raw)
To: Eric Dumazet
Cc: e1000-devel, netdev, John Ronciak, John Stultz, Jacob Keller,
Thomas Gleixner
In-Reply-To: <1323746932.2583.33.camel@edumazet-laptop>
On Tue, Dec 13, 2011 at 04:28:52AM +0100, Eric Dumazet wrote:
>
> Adding a (shared) spinlock on a multiqueue device is source of extra
> delay (because of extra cache line trafic), I guess.
>
> It seems current code doesnt need a spinlock, maybe it was a bug ?
(I didn't think about the old code. I only deleted it. ;)
The spinlock is needed because reading the 64 bit time value involves
reading two 32 registers. The first read latches the value. Ditto for
writing.
In addition, here we have to watch the most significant bit for
one-to-zero transistion, in order to keep count of the overflow.
It is too bad that we have to take the spinlock for every time stamped
packet, but it is the hardware's fault for not providing a 64 bit wide
nanosecond time register.
Richard
------------------------------------------------------------------------------
Systems Optimization Self Assessment
Improve efficiency and utilization of IT resources. Drive out cost and
improve service delivery. Take 5 minutes to use this Systems Optimization
Self Assessment. http://www.accelacomm.com/jaw/sdnl/114/51450054/
_______________________________________________
E1000-devel mailing list
E1000-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/e1000-devel
To learn more about Intel® Ethernet, visit http://communities.intel.com/community/wired
^ permalink raw reply
* Re: [PATCH net-next 2/2] igb: offer a PTP Hardware Clock instead of the timecompare method
From: Eric Dumazet @ 2011-12-13 3:52 UTC (permalink / raw)
To: Richard Cochran
Cc: netdev, e1000-devel, Jacob Keller, Jeff Kirsher, John Ronciak,
John Stultz, Thomas Gleixner
In-Reply-To: <20111213034132.GC9604@netboy.at.omicron.at>
Le mardi 13 décembre 2011 à 04:41 +0100, Richard Cochran a écrit :
> On Tue, Dec 13, 2011 at 04:28:52AM +0100, Eric Dumazet wrote:
> >
> > Adding a (shared) spinlock on a multiqueue device is source of extra
> > delay (because of extra cache line trafic), I guess.
> >
> > It seems current code doesnt need a spinlock, maybe it was a bug ?
>
> (I didn't think about the old code. I only deleted it. ;)
>
> The spinlock is needed because reading the 64 bit time value involves
> reading two 32 registers. The first read latches the value. Ditto for
> writing.
>
> In addition, here we have to watch the most significant bit for
> one-to-zero transistion, in order to keep count of the overflow.
>
> It is too bad that we have to take the spinlock for every time stamped
> packet, but it is the hardware's fault for not providing a 64 bit wide
> nanosecond time register.
>
Yes, probably. Are you sure a workaround is not possible, using a
seqlock for synchronization of threads, and two hardware reads ?
Or maybe it doesnt matter at all :)
^ permalink raw reply
* [PATCH 1/3] option: Removing one bogus and adding some new Huawei combinations
From: Bjorn Mork @ 2011-12-13 4:33 UTC (permalink / raw)
To: netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA
Cc: Bjørn Mork
In-Reply-To: <1323750784-32608-1-git-send-email-bjorn-yOkvZcmFvRU@public.gmane.org>
From: Bjørn Mork <bjorn-yOkvZcmFvRU@public.gmane.org>
Huawei use the product code HUAWEI_PRODUCT_E353 (0x1506) for a
number of different devices, which each can appear with a number
of different descriptor sets. Different types of interfaces
can be identified by looking at the subclass and protocol fields
Subclass 1 protocol 8 is actually the data interface of a CDC
ECM set, with subclass 1 protocol 9 as the control interface.
Neither support serial data communcation, and cannot therefore
be supported by this driver.
At the same time, add a few other sets which appear if the
device is configured in "Windows mode" using this modeswitch
message:
55534243000000000000000000000011060000000100000000000000000000
Signed-off-by: Bjørn Mork <bjorn-yOkvZcmFvRU@public.gmane.org>
---
drivers/usb/serial/option.c | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index e342660..c90f5a4 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -663,7 +663,9 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x01) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x02) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x03) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x08) },
+ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x10) },
+ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x12) },
+ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x13) },
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V640) },
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V620) },
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V740) },
--
1.7.7.3
--
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 related
* [PATCH 2/3] cdc_ether: allow vendor specific data interface if control interface is vendor specific
From: Bjorn Mork @ 2011-12-13 4:33 UTC (permalink / raw)
To: netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA
Cc: Bjørn Mork
In-Reply-To: <1323750784-32608-1-git-send-email-bjorn-yOkvZcmFvRU@public.gmane.org>
From: Bjørn Mork <bjorn-yOkvZcmFvRU@public.gmane.org>
Some vendors, like Huawei, use the vendor specific class code all over the
place even for devices which otherwise conform pretty well to the CDC ECM
specification. This allows such devices to be supported merely by
adding them to the device specific whitelist.
Signed-off-by: Bjørn Mork <bjorn-yOkvZcmFvRU@public.gmane.org>
---
drivers/net/usb/cdc_ether.c | 8 ++++++--
1 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 99ed6eb..84840dd 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -211,8 +211,12 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
/* a data interface altsetting does the real i/o */
d = &info->data->cur_altsetting->desc;
- if (d->bInterfaceClass != USB_CLASS_CDC_DATA) {
- dev_dbg(&intf->dev, "slave class %u\n",
+ if ((d->bInterfaceClass != USB_CLASS_CDC_DATA) &&
+ /* Allow vendor specific data interface iff
+ control interface is vendor specific */
+ !(info->control->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC &&
+ d->bInterfaceClass == USB_CLASS_VENDOR_SPEC)) {
+ dev_dbg(&intf->dev, "xslave class %u\n",
d->bInterfaceClass);
goto bad_desc;
}
--
1.7.7.3
--
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 related
* [PATCH 3/3] qmi_wwan: Driver for WWAN devices requiring use of the QMI protocol
From: Bjorn Mork @ 2011-12-13 4:33 UTC (permalink / raw)
To: netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA
Cc: Bjørn Mork
In-Reply-To: <1323750784-32608-1-git-send-email-bjorn-yOkvZcmFvRU@public.gmane.org>
From: Bjørn Mork <bjorn-yOkvZcmFvRU@public.gmane.org>
Some WWAN LTE/3G devices based on chipsets from Qualcomm provide
near standard CDC ECM interfaces in addition to the usual serial
interfaces. But they cannot be fully configured using AT commands
over a serial interface. It is necessary to speak the proprietary
Qualcomm Messaging Interface (QMI) protocol to the device to
enable the ethernet proxy functionality.
This protocol is exposed by the device through the standard CDC
USB_CDC_SEND_ENCAPSULATED_COMMAND and USB_CDC_GET_ENCAPSULATED_RESPONSE
control messages, and using USB_CDC_NOTIFY_RESPONSE_AVAILABLE
notifications.
This driver adds a usbnet minidriver with very rudimentary QMI
support.
The layout of the usb interfaces can vary a lot within a single
device, depending on which "modeswitch" command has been used to
switch it from usb-storage mode. Control and data interfaces
can be combined, or they can look like standard CDC ECM interfaces
with the appropriate descriptors but with vendor specific class
code. This driver is attempting to support them all, by reusing
as much as possible of the existing usbnet infrastructure.
Signed-off-by: Bjørn Mork <bjorn-yOkvZcmFvRU@public.gmane.org>
---
drivers/net/usb/Kconfig | 13 +
drivers/net/usb/Makefile | 2 +
drivers/net/usb/qmi.c | 730 +++++++++++++++++++++++++++++++++++++++
drivers/net/usb/qmi.h | 101 ++++++
drivers/net/usb/qmi_wwan_core.c | 206 +++++++++++
5 files changed, 1052 insertions(+), 0 deletions(-)
create mode 100644 drivers/net/usb/qmi.c
create mode 100644 drivers/net/usb/qmi.h
create mode 100644 drivers/net/usb/qmi_wwan_core.c
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index 2335761..eed9a37 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -461,4 +461,17 @@ config USB_VL600
http://ubuntuforums.org/showpost.php?p=10589647&postcount=17
+config USB_NET_QMI_WWAN
+ tristate "USB-to-WWAN driver for QMI based 3G and LTE modems"
+ depends on USB_NET_CDCETHER
+ help
+ Support WWAN LTE/3G devices based on chipsets from Qualcomm,
+ using the Qualcomm Messaging Interface (QMI) protocol to
+ configure the device.
+
+ Note that it is still necessary to configure some aspects of
+ the device using AT commands on a tty. Select the option
+ driver to get this support.
+
+
endmenu
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index c203fa2..ed715da 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -29,4 +29,6 @@ obj-$(CONFIG_USB_SIERRA_NET) += sierra_net.o
obj-$(CONFIG_USB_NET_CX82310_ETH) += cx82310_eth.o
obj-$(CONFIG_USB_NET_CDC_NCM) += cdc_ncm.o
obj-$(CONFIG_USB_VL600) += lg-vl600.o
+obj-$(CONFIG_USB_NET_QMI_WWAN) += qmi_wwan.o
+qmi_wwan-objs := qmi_wwan_core.o qmi.o
diff --git a/drivers/net/usb/qmi.c b/drivers/net/usb/qmi.c
new file mode 100644
index 0000000..b4f6a4b
--- /dev/null
+++ b/drivers/net/usb/qmi.c
@@ -0,0 +1,730 @@
+/*
+ * Copyright (c) 2011 Bjørn Mork <bjorn-yOkvZcmFvRU@public.gmane.org>
+ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+/*
+ * Dealing with devices using Qualcomm Messaging Interface (QMI) for
+ * configuration. Full documentation of the protocol can be obtained
+ * from http://developer.qualcomm.com/
+ *
+ * Some of this code may be inspired by code from the Qualcomm Gobi
+ * 2000 and Gobi 3000 drivers, available at http://www.codeaurora.org/
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/cdc.h>
+#include "qmi.h"
+
+
+/* QMI protocol debug output */
+static int qmi_debug;
+module_param(qmi_debug, int, 0);
+MODULE_PARM_DESC(qmi_debug, "Enable QMI protocol debugging");
+
+/* find and return a pointer to the requested tlv */
+struct qmi_tlv *qmi_get_tlv(u8 type, u8 *buf, size_t len)
+{
+ u8 *p;
+ struct qmi_tlv *t;
+
+ for (p = buf; p < buf + len; p += t->len + sizeof(struct qmi_tlv)) {
+ t = (struct qmi_tlv *)p;
+ if (t->type == type)
+ return (p + t->len <= buf + len) ? t : NULL;
+ }
+ return NULL;
+}
+
+
+/* TLV 0x02 is status: return a negative QMI error code or 0 if OK */
+int qmi_verify_status_tlv(u8 *buf, size_t len)
+{
+ struct qmi_tlv *tlv = (void *)qmi_get_tlv(0x02, buf, len);
+ struct qmi_tlv_response_data *r = (void *)tlv->bytes;
+
+ if (!tlv || tlv->len != 2 * sizeof(__le16))
+ return -1; /* QMI_ERR_MALFORMED_MSG */
+
+ return r->error ? -r->code : 0;
+}
+
+static char *decode_trans_flags(const u8 flags)
+{
+ switch (flags) {
+ case 0: return "request";
+ case 2: return "response";
+ case 4: return "indication";
+ case 6: return "reserved";
+ default: return "invalid";
+ }
+}
+
+static char *qmi_system(u8 system)
+{
+ switch (system) {
+ case 0: return "QMI_CTL";
+ case 1: return "QMI_WDS";
+ case 2: return "QMI_DMS";
+ default: return "(unknown)";
+ }
+}
+
+
+/* debug print TLVs from a QMI message */
+static void qmi_tlv_dump(struct qmi_state *qmi, u8 *data, size_t len, u8 system, __le16 msgid)
+{
+ u8 *p;
+ struct qmi_tlv *t;
+ char linebuf[100];
+
+ for (p = data; p < data + len; p += t->len + sizeof(struct qmi_tlv)) {
+ t = (struct qmi_tlv *)p;
+
+ /* mark an empty string */
+ linebuf[0] = 0;
+ if (t->type == 0x02) { /* status response */
+ struct qmi_tlv_response_data *r = (void *)t->bytes;
+ snprintf(linebuf, sizeof(linebuf), "%s (0x%04x)",
+ r->error ? "FAILED" : "SUCCESS", r->code);
+ } else {
+ switch (system) {
+ case QMI_WDS:
+ switch (msgid) {
+ case 0x0001:
+ switch (t->type) {
+ case 0x16:
+ snprintf(linebuf, sizeof(linebuf), "tx: %d bps, rx: %d bps",
+ *(__le32 *)t->bytes, *(__le32 *)(t->bytes + 4));
+ }
+ }
+ break;
+ case QMI_DMS:
+ switch (msgid) {
+ case 0x002b:
+ if (t->type == 0x11 || t->type == 0x12)
+ snprintf(linebuf, sizeof(linebuf),
+ "PIN%d status=%d, %d verify retries left, %d unblock retries left",
+ t->type & 0x3, t->bytes[0], t->bytes[1], t->bytes[2]);
+ }
+ }
+ }
+
+ /* do a default byte dump if the linebuf is empty */
+ if (linebuf[0] == 0)
+ hex_dump_to_buffer(t->bytes, t->len, 16, 1, linebuf, sizeof(linebuf), true);
+
+ dev_info(&qmi->dev->dev, "[0x%02x] (%d) %s\n", t->type, t->len, linebuf);
+ }
+}
+
+/* verify QMUX message header and return pointer to the (first) message if OK */
+static struct qmi_msg *qmi_qmux_verify(u8 *data, size_t len)
+{
+ struct qmux *h = (void *)data;
+ struct qmi_msg *m;
+ ssize_t hsize = sizeof(struct qmux);
+
+ if (len < sizeof(struct qmux) || /* short packet */
+ h->tf != 0x01 || h->len != (len - 1)) /* invalid QMUX packet! */
+ return NULL;
+
+ /* tid has a different size for QMI_CTL for some fucking stupid reason */
+ if (h->service == QMI_CTL)
+ hsize--;
+
+ m = (struct qmi_msg *)(data + hsize);
+
+ /* insanity checking before any further dereferencing... */
+ if (hsize + sizeof(struct qmi_msg) + m->len > len)
+ return NULL;
+
+ return m;
+}
+
+/* debug print a QMUX packet */
+static void qmi_dump_qmux(struct qmi_state *qmi, u8 *data, size_t len)
+{
+ struct qmux *h = (void *)data;
+ struct qmi_msg *m;
+
+ m = qmi_qmux_verify(data, len);
+ if (!m) {
+ dev_info(&qmi->dev->dev, "invalid QMUX packet (%zu bytes)\n", len);
+ return;
+ }
+
+ /* dump parts of the QMUX header and the message ID */
+ dev_info(&qmi->dev->dev, "%s %s: msg 0x%04x (len %d) from \"%s\" with cid=0x%02x%s and TLVs:\n",
+ qmi_system(h->service),
+ decode_trans_flags(h->service == QMI_CTL ? h->flags << 1 : h->flags),
+ m->msgid,
+ h->len,
+ h->ctrl & 0x80 ? "service" : "control point",
+ h->qmicid, h->qmicid == 0xff ? " (broadcast)" : "");
+
+ /* dump the TLVs */
+ qmi_tlv_dump(qmi, m->tlv, m->len, h->service, m->msgid);
+}
+
+static int qmi_dms_parse(struct qmi_state *qmi, struct qmi_msg *m)
+{
+ int status;
+ struct qmi_tlv *tlv;
+
+ if (!m)
+ return -1;
+
+ /* check and save status TLV */
+ status = qmi_verify_status_tlv(m->tlv, m->len);
+
+ /* no QMI_DMS messages requires parsing unless status is OK */
+ if (status < 0)
+ return status;
+
+ switch (m->msgid) {
+ case 0x002b: /* QMI_DMS_UIM_GET_PIN_STATUS */
+ if (status == 0) {
+ /* PIN1 status */
+ tlv = qmi_get_tlv(0x11, m->tlv, m->len);
+ if (tlv && tlv->len == 3) {
+ switch (tlv->bytes[0]) {
+ case 2: /* PIN enabled, verified */
+ case 3: /* PIN disabled */
+ qmi->flags |= QMI_FLAG_PINOK;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int qmi_ctl_parse(struct qmi_state *qmi, struct qmi_msg *m)
+{
+ int status;
+ struct qmi_tlv *tlv;
+
+ if (!m)
+ return -1;
+
+ /* check and save status TLV */
+ status = qmi_verify_status_tlv(m->tlv, m->len);
+
+ if (status < 0)
+ return status;
+
+ /* TLVs are message dependent */
+ switch (m->msgid) {
+ case 0x0022: /* allocate cid */
+ /* TLV 0x01 is system + cid */
+ tlv = qmi_get_tlv(0x01, m->tlv, m->len);
+
+ /* can only save CID if there is a slot for the subsystem */
+ if (tlv && tlv->len == 2 && tlv->bytes[0] < sizeof(qmi->cid))
+ qmi->cid[tlv->bytes[0]] = tlv->bytes[1];
+ else
+ return -1;
+ }
+
+ return status;
+}
+
+static int qmi_wds_parse(struct qmi_state *qmi, struct qmi_msg *m)
+{
+ int status;
+ struct qmi_tlv *tlv;
+
+ if (!m)
+ return -1;
+
+ /* check and save status TLV */
+ status = qmi_verify_status_tlv(m->tlv, m->len);
+
+ if (m->msgid == 0x0020 && -status == 0x001A) { /* QMI_ERR_NO_EFFECT */
+ memset(qmi->handle, 0xff, sizeof(qmi->handle)); /* global handle */
+ return 0;
+ }
+
+ if (status < 0)
+ return status;
+
+ switch (m->msgid) {
+ case 0x0020: /* QMI_WDS_START_NETWORK_INTERFACE */
+ /* TLV 0x01 is a 4 byte packet data handle we need to keep */
+ tlv = qmi_get_tlv(0x01, m->tlv, m->len);
+ if (tlv && tlv->len == sizeof(qmi->handle))
+ memcpy(qmi->handle, tlv->bytes, sizeof(qmi->handle));
+
+ break;
+
+ case 0x0022: /* QMI_WDS_GET_PKT_SRVC_STATUS */
+ /* TLV 0x01 is a 1 byte connection status */
+ tlv = qmi_get_tlv(0x01, m->tlv, m->len);
+ if (tlv && tlv->len == 1)
+ qmi->wds_status = tlv->bytes[0];
+
+ }
+
+ return status;
+}
+
+static void qmi_read_callback(struct urb *urb)
+{
+ struct qmi_state *qmi = (void *)urb->context;
+
+ if (urb->status == 0) {
+ struct qmux *h = (void *)urb->transfer_buffer;
+ struct qmi_msg *m = qmi_qmux_verify(urb->transfer_buffer, urb->actual_length);
+
+ switch (h->service) {
+ case QMI_CTL:
+ qmi_ctl_parse(qmi, m);
+ break;
+ case QMI_WDS:
+ qmi_wds_parse(qmi, m);
+ break;
+ case QMI_DMS:
+ qmi_dms_parse(qmi, m);
+ }
+
+ if (qmi_debug)
+ qmi_dump_qmux(qmi, urb->transfer_buffer, urb->actual_length);
+ }
+}
+
+static int qmi_recv_sync(struct qmi_state *qmi)
+{
+ int status;
+
+ status = usb_control_msg(qmi->dev,
+ usb_rcvctrlpipe(qmi->dev, 0),
+ USB_CDC_GET_ENCAPSULATED_RESPONSE, USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE,
+ 0, qmi->intfnr,
+ qmi->rcvbuf, QMI_BUFLEN, 1000);
+
+ if (status > 0 && qmi_debug)
+ qmi_dump_qmux(qmi, qmi->rcvbuf, status);
+
+ return status;
+}
+
+static int qmi_send_sync(struct qmi_state *qmi, unsigned char *msg, size_t len)
+{
+ int status;
+
+ status = usb_control_msg(qmi->dev,
+ usb_sndctrlpipe(qmi->dev, 0),
+ USB_CDC_SEND_ENCAPSULATED_COMMAND, USB_DIR_OUT|USB_TYPE_CLASS|USB_RECIP_INTERFACE,
+ 0, qmi->intfnr,
+ msg, len, 1000);
+
+ if (qmi_debug) /* NOTE: regardless of status! */
+ qmi_dump_qmux(qmi, msg, len);
+
+ return status;
+}
+
+/* send message and wait for the reply - returns pointer to reply message */
+struct qmi_msg *qmi_send_and_wait_for_ack(struct qmi_state *qmi, unsigned char *data, size_t len, int timeout)
+{
+ int ret;
+ u8 system;
+ __le16 msgid;
+ struct qmi_msg *msg = qmi_qmux_verify(data, len);
+ struct qmux *h = (void *)data;
+
+ /* should never happen? */
+ if (!msg)
+ return NULL;
+ msgid = msg->msgid;
+ system = h->service;
+
+ /* flush any pending messages */
+ do {
+ ret = qmi_recv_sync(qmi);
+ } while (ret > 0);
+
+ /* send our message */
+ ret = qmi_send_sync(qmi, data, len);
+ if (ret != len)
+ return NULL;
+
+ /* wait a while for the (correct) reply */
+ do {
+ ret = qmi_recv_sync(qmi);
+ msg = qmi_qmux_verify(qmi->rcvbuf, ret);
+ if (msg) {
+ h = (struct qmux *)qmi->rcvbuf;
+ if (msg->msgid == msgid && h->service == system) /* found it */
+ return msg;
+
+ }
+ if (timeout--)
+ msleep(100);
+ } while (timeout > 0);
+
+ /* no reply */
+ return NULL;
+}
+
+/* add a TLV to an existing QMUX */
+static size_t qmi_add_tlv(u8 *buf, size_t buflen, u8 type, const u8 *tlvdata, size_t datalen)
+{
+ struct qmux *h = (void *)buf;
+ struct qmi_msg *m = (void *)(buf + sizeof(struct qmux) - (h->service == QMI_CTL));
+ struct qmi_tlv *tlv = (void *)(buf + h->len + 1);
+ size_t tlvlen = sizeof(struct qmi_tlv) + datalen;
+
+ if (buflen < h->len + tlvlen - 1)
+ return -1;
+
+ tlv->type = type;
+ tlv->len = datalen;
+ memcpy(tlv->bytes, tlvdata, datalen);
+ h->len += tlvlen;
+ m->len += tlvlen;
+ return h->len + 1;
+}
+
+/* get a new transaction id */
+static __le16 new_tid(void)
+{
+ static __le16 tid;
+ return ++tid;
+}
+
+/* assemble a complete QMUX packet with one QMI message */
+static size_t qmi_create_msg(char *buf, size_t buflen, u8 cid, u8 system, __le16 msgid)
+{
+ struct qmux *h = (void *)buf;
+ struct qmi_msg *m;
+ size_t hsize = sizeof(struct qmux);
+
+ /* this is weird, but it seems as though QMI_CTL uses 1 byte
+ transaction ids while the other systems use 2 bytes as specified
+ in the standard */
+ if (system == QMI_CTL)
+ hsize--;
+
+ /* sanity check */
+ if (buflen < hsize + sizeof(struct qmi_msg))
+ return -1;
+
+ h->tf = 1; /* always 1 */
+ h->ctrl = 0; /* 0 - control point */
+ h->service = system;
+ h->qmicid = cid;
+ h->flags = 0; /* 0 - request */
+
+ /* see comment above */
+ if (system == QMI_CTL)
+ h->tid.b = new_tid() & 0xff;
+ else
+ h->tid.w = new_tid();
+
+ m = (struct qmi_msg *)(buf + hsize);
+ m->msgid = msgid;
+
+ h->len = hsize + sizeof(struct qmi_msg) - 1;
+ m->len = 0;
+ return h->len + 1;
+}
+
+/* QMI_CTL msg 0x0022 is "request cid", TLV 0x01 is system */
+static int qmi_ctl_request_cid(struct qmi_state *qmi, u8 system)
+{
+ size_t len;
+ u8 tlvdata[1] = { system };
+ char buf[32];
+
+ /* return immediately if a CID is already allocated */
+ if (qmi->cid[system])
+ return qmi->cid[system];
+
+ /* else request a new one */
+ len = qmi_create_msg(buf, sizeof(buf), 0, QMI_CTL, 0x0022);
+ len = qmi_add_tlv(buf, sizeof(buf), 0x01, tlvdata, sizeof(tlvdata));
+
+ /* send message */
+ return qmi_ctl_parse(qmi, qmi_send_and_wait_for_ack(qmi, buf, len, 30));
+}
+
+/* QMI_WDS msg 0x0001 is "" */
+static int qmi_wds_report(struct qmi_state *qmi)
+{
+ size_t len;
+ u8 cid = qmi->cid[QMI_WDS];
+ u8 tlvdata[1] = { 1 }; /* generic "enable" */
+ char buf[32];
+
+ /* enable connection status reports */
+ len = qmi_create_msg(buf, sizeof(buf), cid, QMI_WDS, 0x0001);
+ len = qmi_add_tlv(buf, sizeof(buf), 0x10, tlvdata, sizeof(tlvdata)); /* current channel rate */
+ len = qmi_add_tlv(buf, sizeof(buf), 0x15, tlvdata, sizeof(tlvdata)); /* current data bearer tech */
+
+ /* send message */
+ return qmi_wds_parse(qmi, qmi_send_and_wait_for_ack(qmi, buf, len, 30));
+}
+
+/* QMI_WDS msg 0x0020 is "" */
+static int qmi_wds_start(struct qmi_state *qmi)
+{
+ size_t len;
+ u8 cid = qmi->cid[QMI_WDS];
+ char buf[32];
+
+ if (!cid)
+ return -1;
+
+ len = qmi_create_msg(buf, sizeof(buf), cid, QMI_WDS, 0x0020);
+
+ /* send message */
+ return qmi_wds_parse(qmi, qmi_send_and_wait_for_ack(qmi, buf, len, 30));
+}
+
+/* QMI_WDS msg 0x0021 is "" */
+static int qmi_wds_stop(struct qmi_state *qmi)
+{
+ size_t len;
+ u8 cid = qmi->cid[QMI_WDS];
+ char buf[32];
+
+ if (!cid)
+ return -1;
+
+ len = qmi_create_msg(buf, sizeof(buf), cid, QMI_WDS, 0x0021);
+ len = qmi_add_tlv(buf, sizeof(buf), 0x01, qmi->handle, sizeof(qmi->handle)); /* packet data handle */
+
+ return qmi_wds_parse(qmi, qmi_send_and_wait_for_ack(qmi, buf, len, 30));
+}
+
+/* QMI_WDS msg 0x0022 is "" */
+static int qmi_wds_status(struct qmi_state *qmi)
+{
+ int ret;
+ size_t len;
+ u8 cid = qmi->cid[QMI_WDS];
+ char buf[32];
+
+ if (!cid)
+ return -1;
+
+ len = qmi_create_msg(buf, sizeof(buf), cid, QMI_WDS, 0x0022);
+
+ ret = qmi_wds_parse(qmi, qmi_send_and_wait_for_ack(qmi, buf, len, 30));
+ if (ret < 0)
+ return ret;
+
+ return qmi->wds_status;
+}
+
+/* QMI_CTL msg 0x0023 is "release cid", TLV 0x01 is system + cid */
+static int qmi_ctl_release_cid(struct qmi_state *qmi, u8 system)
+{
+ char buf[32];
+ size_t len = qmi_create_msg(buf, sizeof(buf), 0, QMI_CTL, 0x0023);
+
+ if (system < sizeof(qmi->cid)) {
+ u8 tlvdata[2] = { system, qmi->cid[system] };
+ len = qmi_add_tlv(buf, sizeof(buf), 0x01, tlvdata, sizeof(tlvdata));
+
+ } else {
+ return -1;
+ }
+
+ /* no need to parse result - cant reuse CID in any case */
+ qmi_send_and_wait_for_ack(qmi, buf, len, 30);
+ qmi->cid[system] = 0;
+
+ return 1;
+}
+
+static int qmi_dms_verify_pin(struct qmi_state *qmi)
+{
+ int ret;
+ size_t len;
+ u8 cid = qmi->cid[QMI_DMS];
+ char buf[32];
+
+ if (!cid)
+ return -1;
+
+ len = qmi_create_msg(buf, sizeof(buf), cid, QMI_DMS, 0x002b);
+ ret = qmi_dms_parse(qmi, qmi_send_and_wait_for_ack(qmi, buf, len, 30));
+ return qmi->flags && QMI_FLAG_PINOK;
+}
+
+
+/* ----- exported API ----- */
+
+int qmi_reset(struct qmi_state *qmi)
+{
+ /* NOTE: We do NOT clear the allocated CIDs! */
+ qmi->state = QMI_STATE_INIT;
+ qmi->wds_status = 0;
+ qmi->flags = 0;
+ return 1;
+}
+
+int qmi_disconnect(struct qmi_state *qmi)
+{
+ qmi->flags &= ~QMI_FLAG_RECV; /* disable async receiving */
+ qmi_wds_stop(qmi);
+ qmi_wds_status(qmi);
+ qmi->state = QMI_STATE_DOWN;
+ return 1;
+}
+
+/* receive a QMUX can be called in interrupt context! */
+int qmi_recv(struct qmi_state *qmi)
+{
+ if (qmi->flags & QMI_FLAG_RECV) /* async receiving enabled? */
+ return usb_submit_urb(qmi->urb, GFP_ATOMIC);
+ else
+ return 0;
+}
+
+/*
+ * return current connection state with side effects: will run through
+ * the states from INIT to CONN if possible
+ */
+int qmi_conn_state(struct qmi_state *qmi)
+{
+ int ret;
+
+ dev_dbg(&qmi->dev->dev, ".state=%d, .cid[QMI_WDS]=0x%02x, .cid[QMI_DMS]=0x%02x, .flags=0x%08lx, .handle=0x%08x\n",
+ qmi->state,
+ qmi->cid[QMI_WDS],
+ qmi->cid[QMI_DMS],
+ qmi->flags,
+ *(__le32 *)qmi->handle);
+
+ switch (qmi->state) {
+ /* these states don't trigger any action */
+ case QMI_STATE_DOWN:
+ return 1;
+ case QMI_STATE_UP:
+ qmi->flags |= QMI_FLAG_RECV; /* enable async receiving */
+ return 0;
+ case QMI_STATE_ERR:
+ return -1;
+
+ case QMI_STATE_INIT:
+ qmi->flags &= ~QMI_FLAG_RECV; /* disable async receiving */
+
+ /* request CIDs */
+ if (qmi_ctl_request_cid(qmi, QMI_WDS) < 0 || qmi_ctl_request_cid(qmi, QMI_DMS) < 0) {
+ qmi->state = QMI_STATE_ERR; /* FATAL - no way out except for unplugging the device */
+ return -1;
+ }
+
+ /* stay in this state until PIN code is entered */
+ if (!qmi_dms_verify_pin(qmi)) {
+ dev_notice(&qmi->dev->dev, "please enter PIN code using AT+CPIN=\"xxxx\" on a ttyUSB0 device\n");
+ return 1;
+ }
+ /* check status */
+ switch (qmi_wds_status(qmi)) {
+ case 1: /* DISCONNECTED - attempt to connect */
+ ret = qmi_wds_start(qmi);
+ if (ret < 0) {
+ dev_notice(&qmi->dev->dev, "connection failed: 0x%04x\n", -ret);
+ break;
+ }
+
+ /* fallthrough */
+ case 2: /* CONNECTED - note that */
+ qmi_wds_report(qmi); /* enable status reports */
+ qmi->state = QMI_STATE_UP;
+
+ /* do nothing for the other states */
+ }
+ break;
+
+ default:
+ dev_dbg(&qmi->dev->dev, "%s() unknown state=%d\n", __func__, qmi->state);
+ }
+ return 1; /* not connected yet - */
+}
+
+
+/* allocate and initiate a QMI state device */
+struct qmi_state *qmi_init(struct usb_interface *intf)
+{
+ struct qmi_state *qmi = kmalloc(sizeof(struct qmi_state), GFP_KERNEL);
+
+ if (!qmi)
+ return NULL;
+
+ memset(qmi, 0, sizeof(*qmi));
+
+ /*
+ * keep track of the device receiving the control messages and the
+ * number of the CDC (like) control interface which is our target.
+ * Note that the interface might be disguised as vendor specific,
+ * and be a combined CDC control/data interface
+ */
+ qmi->dev = interface_to_usbdev(intf);
+ qmi->intfnr = intf->cur_altsetting->desc.bInterfaceNumber;
+ /* FIXME: it would be useful to verify that this interface actually talks CDC */
+
+ /* create async receive URB */
+ qmi->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!qmi->urb) {
+ kfree(qmi);
+ return NULL;
+ }
+
+ /* pre-allocate buffer for receiving */
+ qmi->rcvbuf = kmalloc(QMI_BUFLEN, GFP_KERNEL);
+
+ if (!qmi->rcvbuf) {
+ usb_free_urb(qmi->urb);
+ kfree(qmi);
+ return NULL;
+ }
+
+ /* usb control setup */
+ qmi->setup.bRequestType = USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE;
+ qmi->setup.bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
+ qmi->setup.wValue = 0; /* zero */
+ qmi->setup.wIndex = qmi->intfnr;
+ qmi->setup.wLength = QMI_BUFLEN;
+
+ /* prepare the async receive URB */
+ usb_fill_control_urb(qmi->urb, qmi->dev,
+ usb_rcvctrlpipe(qmi->dev, 0),
+ (char *)&qmi->setup,
+ qmi->rcvbuf,
+ QMI_BUFLEN,
+ qmi_read_callback, qmi);
+
+ return qmi;
+}
+
+/* disable and free a QMI state device */
+int qmi_exit(struct qmi_state *qmi)
+{
+ int i;
+
+ /* release all CIDs - may need to sleep */
+ for (i = 1; i < sizeof(qmi->cid); i++)
+ qmi_ctl_release_cid(qmi, i);
+
+ /* free all buffers */
+ kfree(qmi->rcvbuf);
+
+ /* free URBs */
+ usb_free_urb(qmi->urb);
+
+ kfree(qmi);
+
+ return 0;
+}
diff --git a/drivers/net/usb/qmi.h b/drivers/net/usb/qmi.h
new file mode 100644
index 0000000..bff94f1
--- /dev/null
+++ b/drivers/net/usb/qmi.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2011 Bjørn Mork <bjorn-yOkvZcmFvRU@public.gmane.org>
+ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+/*
+ * Dealing with devices using Qualcomm Messaging Interface (QMI) for
+ * configuration. Full documentation of the protocol can be obtained
+ * from http://developer.qualcomm.com/
+ *
+ * Some of this code may be inspired by code from the Qualcomm Gobi
+ * 2000 and Gobi 3000 drivers, available at http://www.codeaurora.org/
+ */
+
+#define QMI_BUFLEN 128
+
+enum qmi_device_state {
+ QMI_STATE_INIT = 0, /* allocate CIDs, verify pin code */
+ QMI_STATE_DOWN, /* connection is down */
+ QMI_STATE_UP, /* connection is up */
+ QMI_STATE_ERR, /* fatal and final error state, e.g. no CID available - no way out of here! */
+};
+
+/* the different QMI subsystems we are using */
+enum qmi_subsystems {
+ QMI_CTL = 0,
+ QMI_WDS,
+ QMI_DMS,
+ QMI_SYSMAX,
+};
+
+#define QMI_FLAG_RECV 0x00000001
+#define QMI_FLAG_PINOK 0x00000002
+
+struct qmi_state {
+ struct usb_device *dev;
+ struct urb *urb; /* receive urb */
+ unsigned char *rcvbuf; /* pre-allocated receive buffer */
+ struct usb_ctrlrequest setup; /* the receive setup - 8 bytes */
+ unsigned long flags; /* used for en/dis-abling functionality on the fly */
+ u8 state; /* for connection state machine */
+ u8 wds_status; /* current value for QMI_WDS message 0x0022 or 0 if uninitialized */
+ __le16 intfnr; /* keeping track of the interface we are referring to */
+ u8 handle[4]; /* connection handle needed for disconnect */
+ u8 cid[QMI_SYSMAX]; /* keeping track of cid per subsystem */
+};
+
+/* combined QMUX and SDU */
+struct qmux {
+ u8 tf; /* always 1 */
+ __le16 len; /* excluding tf */
+ u8 ctrl; /* b7: sendertype 1 => service, 0 => control point */
+ u8 service; /* 0 => QMI_CTL, 1 => QMI_WDS, .. */
+ u8 qmicid; /* client id or 0xff for broadcast */
+ u8 flags; /* always 0 for req */
+ union {
+ __le16 w; /* each control point maintains a transaction id counter - non-zero */
+ u8 b; /* system QMI_CTL uses one byte transaction ids! */
+ } tid;
+ u8 msg[]; /* one or more messages */
+} __packed;
+
+struct qmi_msg {
+ __le16 msgid;
+ __le16 len;
+ u8 tlv[]; /* zero or more tlvs */
+} __packed;
+
+struct qmi_tlv {
+ u8 type;
+ __le16 len;
+ u8 bytes[];
+} __packed;
+
+struct qmi_tlv_response_data {
+ __le16 error;
+ __le16 code;
+} __packed;
+
+
+/* reset state to INIT */
+extern int qmi_reset(struct qmi_state *qmi);
+
+/* disconnect WWAN connection */
+extern int qmi_disconnect(struct qmi_state *qmi);
+
+/* must be called whenever USB_CDC_NOTIFY_RESPONSE_AVAILABLE is received */
+extern int qmi_recv(struct qmi_state *qmi);
+
+/* called periodically, whenever a state transition is wanted */
+extern int qmi_conn_state(struct qmi_state *qmi);
+
+/* initialize */
+extern struct qmi_state *qmi_init(struct usb_interface *intf);
+
+/* clean up */
+extern int qmi_exit(struct qmi_state *qmi);
diff --git a/drivers/net/usb/qmi_wwan_core.c b/drivers/net/usb/qmi_wwan_core.c
new file mode 100644
index 0000000..bf25e33
--- /dev/null
+++ b/drivers/net/usb/qmi_wwan_core.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2011 Bjørn Mork <bjorn-yOkvZcmFvRU@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/usb/cdc.h>
+#include <linux/usb/usbnet.h>
+#include "qmi.h"
+
+/* overloading the cdc_state structure */
+struct qmi_wwan_state {
+ struct qmi_state *qmi;
+ struct usb_cdc_union_desc *u;
+ struct usb_cdc_ether_desc *ether;
+ struct usb_interface *control;
+ struct usb_interface *data;
+};
+
+
+static void qmi_wwan_cdc_status(struct usbnet *dev, struct urb *urb)
+{
+ struct usb_cdc_notification *event;
+ struct qmi_wwan_state *info = (void *)&dev->data;
+
+ if (urb->actual_length < sizeof *event) {
+ netdev_dbg(dev->net, "short status urb rcvd: %d bytes\n", urb->actual_length);
+ return;
+ }
+
+ event = urb->transfer_buffer;
+ switch (event->bNotificationType) {
+ case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
+ qmi_recv(info->qmi); /* request response */
+ break;
+ default:
+ /* reuse usbnet() for handling other messages */
+ usbnet_cdc_status(dev, urb);
+ }
+}
+
+static int qmi_wwan_cdc_ecmlike_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+ int status;
+ struct qmi_wwan_state *info = (void *)&dev->data;
+
+ status = usbnet_generic_cdc_bind(dev, intf);
+ if (status < 0)
+ return status;
+
+ /* initialize QMI data */
+ info->qmi = qmi_init(intf);
+ if (!info->qmi) {
+ usbnet_cdc_unbind(dev, intf);
+ return -1;
+ }
+ return 0;
+}
+
+static int qmi_wwan_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+ int status;
+ struct qmi_wwan_state *info = (void *)&dev->data;
+
+ memset(info, 0, sizeof(*info));
+ status = usbnet_get_endpoints(dev, intf);
+ if (status < 0)
+ return status;
+
+ /* initialize QMI data */
+ info->qmi = qmi_init(intf);
+ if (!info->qmi)
+ return -1;
+
+ info->control = intf;
+ return 0;
+}
+
+static void qmi_wwan_cdc_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+ struct qmi_wwan_state *info = (void *)&dev->data;
+
+ /* release our private structure */
+ qmi_exit(info->qmi);
+ info->qmi = NULL;
+
+ /* disconnect */
+ usbnet_cdc_unbind(dev, intf);
+}
+
+static int qmi_wwan_reset(struct usbnet *dev)
+{
+ struct qmi_wwan_state *info = (void *)&dev->data;
+
+ qmi_reset(info->qmi);
+ return 1;
+}
+
+static int qmi_wwan_stop(struct usbnet *dev)
+{
+ struct qmi_wwan_state *info = (void *)&dev->data;
+
+ qmi_disconnect(info->qmi);
+ return 1;
+}
+
+/* abusing check_connect for triggering QMI state transitions */
+static int qmi_wwan_check_connect(struct usbnet *dev)
+{
+ struct qmi_wwan_state *info = (void *)&dev->data;
+ return qmi_conn_state(info->qmi);
+}
+
+static const struct driver_info qmi_wwan_ecm_info = {
+ .description = "QMI speaking CDC ECM like wwan device",
+ .flags = FLAG_WWAN,
+ .bind = qmi_wwan_cdc_ecmlike_bind,
+ .unbind = qmi_wwan_cdc_unbind,
+ .status = qmi_wwan_cdc_status,
+ .check_connect = qmi_wwan_check_connect,
+ .stop = qmi_wwan_stop,
+ .reset = qmi_wwan_reset,
+};
+
+static const struct driver_info qmi_wwan_info = {
+ .description = "QMI speaking wwan device",
+ .flags = FLAG_WWAN,
+ .bind = qmi_wwan_cdc_bind,
+ .unbind = qmi_wwan_cdc_unbind,
+ .status = qmi_wwan_cdc_status,
+ .check_connect = qmi_wwan_check_connect,
+ .stop = qmi_wwan_stop,
+ .reset = qmi_wwan_reset,
+};
+
+#define HUAWEI_VENDOR_ID 0x12D1
+
+static const struct usb_device_id products[] = {
+{
+ /* Qmi_Wwan E392, E398, ++? */
+ .match_flags = USB_DEVICE_ID_MATCH_VENDOR
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = HUAWEI_VENDOR_ID,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 9,
+ .driver_info = (unsigned long)&qmi_wwan_ecm_info,
+}, {
+ /* Qmi_Wwan device id 1413 ++? */
+ .match_flags = USB_DEVICE_ID_MATCH_VENDOR
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = HUAWEI_VENDOR_ID,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 6,
+ .bInterfaceProtocol = 255,
+ .driver_info = (unsigned long)&qmi_wwan_ecm_info,
+}, {
+ /* Qmi_Wwan E392, E398, ++? "Windows mode" */
+ .match_flags = USB_DEVICE_ID_MATCH_VENDOR
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = HUAWEI_VENDOR_ID,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 17,
+ .driver_info = (unsigned long)&qmi_wwan_info,
+},
+{ }, /* END */
+};
+MODULE_DEVICE_TABLE(usb, products);
+
+static struct usb_driver qmi_wwan_driver = {
+ .name = "qmi_wwan",
+ .id_table = products,
+ .probe = usbnet_probe,
+ .disconnect = usbnet_disconnect,
+ .suspend = usbnet_suspend,
+ .resume = usbnet_resume,
+ .reset_resume = usbnet_resume,
+ .supports_autosuspend = 1,
+};
+
+static int __init qmi_wwan_init(void)
+{
+ /* we remap struct (cdc_state) so we should be compatible */
+ BUILD_BUG_ON(sizeof(struct cdc_state) != sizeof(struct qmi_wwan_state));
+
+ return usb_register(&qmi_wwan_driver);
+}
+module_init(qmi_wwan_init);
+
+static void __exit qmi_wwan_exit(void)
+{
+ usb_deregister(&qmi_wwan_driver);
+}
+module_exit(qmi_wwan_exit);
+
+MODULE_AUTHOR("Bjørn Mork <bjorn-yOkvZcmFvRU@public.gmane.org>");
+MODULE_DESCRIPTION("Qualcomm Messaging Interface (QMI) WWAN driver");
+MODULE_LICENSE("GPL");
--
1.7.7.3
--
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 related
* [PATCH 0/3] Adding a new driver for Huawei E392 WWAN modem
From: Bjorn Mork @ 2011-12-13 4:33 UTC (permalink / raw)
To: netdev, linux-usb; +Cc: Bjørn Mork
From: Bjørn Mork <bjorn@mork.no>
This can probably support many more devices based on modern Qualcomm
chipsets, but has only been tested with a Huawei E392 LTE modem based
on Qualcomm MDM9200.
These chips require using the QMI protocol to enable anything but
serial mode. They can, and should, still be configured using
AT commands on one of the serial interfaces. In particular, this
driver does not support entering SIM PIN code or APN, which may be
required before opening a connection. PIN can be set by using the
standard AT+CPIN="xxxx" command. APN can most likely be set by
storing it in the default profile.
patch 1 releases one of the interfaces used by this protocol from
the option driver. Note that this interface does not support serial
data anyway, so the option driver binding is completely bogus.
patch 2 relaxes the cdc_ether CDC descriptor parsing in such a way
that it will allow a vendor specific data interface if and only if
the control interface also is vendor specific. This allows us to
reuse the bind() function from cdc_ether for one of the possible
device configurations we support
patch 3 adds the new driver. Most of it is QMI protocol handling.
The network device is a minimalistic usbnet minidriver.
Bjørn Mork (3):
option: Removing one bogus and adding some new Huawei combinations
cdc_ether: allow vendor specific data interface if control interface
is vendor specific
qmi_wwan: Driver for WWAN devices requiring use of the QMI protocol
drivers/net/usb/Kconfig | 13 +
drivers/net/usb/Makefile | 2 +
drivers/net/usb/cdc_ether.c | 8 +-
drivers/net/usb/qmi.c | 730 +++++++++++++++++++++++++++++++++++++++
drivers/net/usb/qmi.h | 101 ++++++
drivers/net/usb/qmi_wwan_core.c | 206 +++++++++++
drivers/usb/serial/option.c | 4 +-
7 files changed, 1061 insertions(+), 3 deletions(-)
create mode 100644 drivers/net/usb/qmi.c
create mode 100644 drivers/net/usb/qmi.h
create mode 100644 drivers/net/usb/qmi_wwan_core.c
--
1.7.7.3
^ permalink raw reply
* Re: [PATCH net-next 2/2] igb: offer a PTP Hardware Clock instead of the timecompare method
From: Richard Cochran @ 2011-12-13 4:40 UTC (permalink / raw)
To: Eric Dumazet
Cc: netdev, e1000-devel, Jacob Keller, Jeff Kirsher, John Ronciak,
John Stultz, Thomas Gleixner
In-Reply-To: <1323748349.2583.38.camel@edumazet-laptop>
On Tue, Dec 13, 2011 at 04:52:29AM +0100, Eric Dumazet wrote:
> > It is too bad that we have to take the spinlock for every time stamped
> > packet, but it is the hardware's fault for not providing a 64 bit wide
> > nanosecond time register.
> >
>
> Yes, probably. Are you sure a workaround is not possible, using a
> seqlock for synchronization of threads, and two hardware reads ?
Many things are possible...
> Or maybe it doesnt matter at all :)
Yes, I think it not worth the effort. In general, the whole time
stamping thing is at odds with network throughput.
Richard
^ permalink raw reply
* Re: [PATCH 2/3] cdc_ether: allow vendor specific data interface if control interface is vendor specific
From: Joe Perches @ 2011-12-13 4:54 UTC (permalink / raw)
To: Bjorn Mork
Cc: netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1323750784-32608-3-git-send-email-bjorn-yOkvZcmFvRU@public.gmane.org>
On Tue, 2011-12-13 at 05:33 +0100, Bjorn Mork wrote:
> diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
[]
> @@ -211,8 +211,12 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
[]
> - dev_dbg(&intf->dev, "slave class %u\n",
[]
> + dev_dbg(&intf->dev, "xslave class %u\n",
Why change the output from slave to xslave?
--
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
* Re: [PATCH 2/3] cdc_ether: allow vendor specific data interface if control interface is vendor specific
From: Bjørn Mork @ 2011-12-13 4:59 UTC (permalink / raw)
To: Joe Perches
Cc: netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1323752053.3837.17.camel@joe2Laptop>
Joe Perches <joe-6d6DIl74uiNBDgjK7y7TUQ@public.gmane.org> writes:
> On Tue, 2011-12-13 at 05:33 +0100, Bjorn Mork wrote:
>> diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
> []
>> @@ -211,8 +211,12 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
> []
>> - dev_dbg(&intf->dev, "slave class %u\n",
> []
>> + dev_dbg(&intf->dev, "xslave class %u\n",
>
> Why change the output from slave to xslave?
That was unintentional. Sorry. Don't understand how checkpatch could
have missed that :-)
Will fix and submit again, unless there are objections to the actual
code change.
Bjørn
--
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
* RE: Thanks
From: Song Li @ 2011-12-13 23:47 UTC (permalink / raw)
To: Recipients
l am a Staff of Hang Seng Bank HongKong, I do not know if we can work together in transferring $19,500,000.USD from my bank. Finally if you are interested I shall provide you with more details. my private Email: mrsong.lile@yahoo.cn
^ permalink raw reply
* Re: [PATCH 1/1] net/fec: add KSZ9021RN phy fixup
From: Richard Zhao @ 2011-12-13 8:28 UTC (permalink / raw)
To: Shawn Guo
Cc: Jason Liu, patches, netdev, s.hauer, jgq516, eric.miao,
Richard Zhao, Veli-Pekka Peltola, shawn.guo, Fabio Estevam, davem,
linux-arm-kernel
In-Reply-To: <20111213024733.GB10866@S2100-06.ap.freescale.net>
On Tue, Dec 13, 2011 at 10:47:33AM +0800, Shawn Guo wrote:
> On Tue, Dec 13, 2011 at 10:29:53AM +0800, Jason Liu wrote:
> > 2011/12/13 Shawn Guo <shawn.guo@freescale.com>:
> > > On Tue, Dec 13, 2011 at 10:10:08AM +0800, Richard Zhao wrote:
> > >> By converting to DT, machine code's becoming common too. I feel hard to
> > >> find a place. Put it in imx6q_init_machine?
> > >>
> > >> Sascha & Shawn, Could you give comments here?
> > >>
> > > I see no problem to do that by checking board compatible string and
> > > do the setup for particular board.
> >
> > Then, you may end up having more and more fix up in the fec driver
> > when all the platforms converted to DT,
> > which will put the fec driver into mess.
> >
> I meant we do this in imx6q_init_machine().
all right. moved to machine code.
Thanks
Richard
>
> --
> Regards,
> Shawn
^ permalink raw reply
* Re: [PATCH 0/3] Adding a new driver for Huawei E392 WWAN modem
From: Bjørn Mork @ 2011-12-13 9:02 UTC (permalink / raw)
To: netdev; +Cc: linux-usb
In-Reply-To: <1323750784-32608-1-git-send-email-bjorn@mork.no>
Bjorn Mork <bjorn@mork.no> writes:
> patch 3 adds the new driver. Most of it is QMI protocol handling.
> The network device is a minimalistic usbnet minidriver.
In case anyone wonders what this really does, here's an excerpt of a
connection being initiated by simply doing "dhclient wwan1".
"ip link set dev wwan1 up" would achieve the same, except for address
configuration of course.
I'm explaining inline:
Dec 13 04:15:38 nemi kernel: [23893.115819] qmi_wwan 2-2:1.3: wwan1: register 'qmi_wwan' at usb-0000:00:1d.7-2, QMI speaking CDC ECM like wwan device, 76:bb:09:ca:8e:84
Dec 13 04:15:38 nemi kernel: [23893.116732] usbcore: registered new interface driver qmi_wwan
Dec 13 04:15:40 nemi kernel: [23894.842205] usb 2-2: QMI_CTL request: msg 0x0022 (len 15) from "control point" with cid=0x00 and TLVs:
Dec 13 04:15:40 nemi kernel: [23894.842214] usb 2-2: [0x01] (1) 01 .
requesting a client ID for subsystem QMI_WDS. We need this for starting
the network connection.
Dec 13 04:15:40 nemi kernel: [23894.944602] usb 2-2: QMI_CTL response: msg 0x0022 (len 23) from "service" with cid=0x00 and TLVs:
Dec 13 04:15:40 nemi kernel: [23894.944614] usb 2-2: [0x02] (4) SUCCESS (0x0000)
Dec 13 04:15:40 nemi kernel: [23894.944622] usb 2-2: [0x01] (2) 01 03 ..
received QMI_WDS client ID = 3
Dec 13 04:15:40 nemi kernel: [23894.945827] usb 2-2: QMI_CTL request: msg 0x0022 (len 15) from "control point" with cid=0x00 and TLVs:
Dec 13 04:15:40 nemi kernel: [23894.945837] usb 2-2: [0x01] (1) 02 .
requesting a client ID for subsystem QMI_DMS. We need this for
verifying SIM PIN state (well, we don't really need to do that, but it
enables us to understand why the connection fails if a PIN code is
required).
Dec 13 04:15:40 nemi kernel: [23895.048729] usb 2-2: QMI_CTL response: msg 0x0022 (len 23) from "service" with cid=0x00 and TLVs:
Dec 13 04:15:40 nemi kernel: [23895.048738] usb 2-2: [0x02] (4) SUCCESS (0x0000)
Dec 13 04:15:40 nemi kernel: [23895.048743] usb 2-2: [0x01] (2) 02 03 ..
received QMI_DMS client ID = 3
Dec 13 04:15:40 nemi kernel: [23895.050066] usb 2-2: QMI_DMS request: msg 0x002b (len 12) from "control point" with cid=0x03 and TLVs:
requesting PIN code state
Dec 13 04:15:40 nemi kernel: [23895.152746] usb 2-2: QMI_DMS response: msg 0x002b (len 31) from "service" with cid=0x03 and TLVs:
Dec 13 04:15:40 nemi kernel: [23895.152758] usb 2-2: [0x02] (4) SUCCESS (0x0000)
Dec 13 04:15:40 nemi kernel: [23895.152767] usb 2-2: [0x12] (3) PIN2 status=1, 3 verify retries left, 10 unblock retries left
Dec 13 04:15:40 nemi kernel: [23895.152777] usb 2-2: [0x11] (3) PIN1 status=2, 3 verify retries left, 10 unblock retries left
PIN1 is enabled but already verified, so we're ready to go.
Dec 13 04:15:40 nemi kernel: [23895.153987] usb 2-2: QMI_WDS request: msg 0x0022 (len 12) from "control point" with cid=0x03 and TLVs:
requesting connection state
Dec 13 04:15:41 nemi kernel: [23895.256746] usb 2-2: QMI_WDS response: msg 0x0022 (len 23) from "service" with cid=0x03 and TLVs:
Dec 13 04:15:41 nemi kernel: [23895.256757] usb 2-2: [0x02] (4) SUCCESS (0x0000)
Dec 13 04:15:41 nemi kernel: [23895.256765] usb 2-2: [0x01] (1) 01 .
state is disconnected
Dec 13 04:15:41 nemi kernel: [23895.257967] usb 2-2: QMI_WDS request: msg 0x0020 (len 12) from "control point" with cid=0x03 and TLVs:
starting a new connection
Dec 13 04:15:41 nemi kernel: [23895.360622] usb 2-2: QMI_WDS response: msg 0x0020 (len 26) from "service" with cid=0x03 and TLVs:
Dec 13 04:15:41 nemi kernel: [23895.360634] usb 2-2: [0x02] (4) SUCCESS (0x0000)
Dec 13 04:15:41 nemi kernel: [23895.360641] usb 2-2: [0x01] (4) e8 90 15 02 ....
successfully connected. Got a 32bit handle (needed for disconnect).
The connection is now fully usable, and DHCP will now succeed (not
shown).
Dec 13 04:15:41 nemi kernel: [23895.361114] usb 2-2: QMI_WDS indication: msg 0x0022 (len 21) from "service" with cid=0xff (broadcast) and TLVs:
Dec 13 04:15:41 nemi kernel: [23895.361124] usb 2-2: [0x01] (2) 02 00 ..
Dec 13 04:15:41 nemi kernel: [23895.361131] usb 2-2: [0x12] (1) 04 .
received an unsolicited QMI_WDS status message (sent to all clients)
with the new IPv4 connected state
Dec 13 04:15:41 nemi kernel: [23895.362343] usb 2-2: QMI_WDS request: msg 0x0001 (len 20) from "control point" with cid=0x03 and TLVs:
Dec 13 04:15:41 nemi kernel: [23895.362352] usb 2-2: [0x10] (1) 01 .
Dec 13 04:15:41 nemi kernel: [23895.362360] usb 2-2: [0x15] (1) 01 .
request speed and system change updates. Nice to know, but not strictly
required.
Dec 13 04:15:41 nemi kernel: [23895.464623] usb 2-2: QMI_WDS response: msg 0x0001 (len 19) from "service" with cid=0x03 and TLVs:
Dec 13 04:15:41 nemi kernel: [23895.464635] usb 2-2: [0x02] (4) SUCCESS (0x0000)
received ack to the last request. That's the last we'll see until
something changes.
Bjørn
^ permalink raw reply
* [PATCH] bridge: push blocking slaves to forwarding when turning stp off
From: Vitalii Demianets @ 2011-12-13 9:36 UTC (permalink / raw)
To: netdev; +Cc: Stephen Hemminger, bridge
If there is a slave in blocking state when stp is turned off, that slave will
remain in blocking state for indefinitely long time until interface state
changed. We should push all blocking slaves into forwarding state after
turning stp off.
Signed-off-by: Vitalii Demianets <vitas@nppfactor.kiev.ua>
---
net/bridge/br_stp.c | 5 ++++-
net/bridge/br_stp_if.c | 10 +++++-----
2 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c
index dd147d7..aed7e21 100644
--- a/net/bridge/br_stp.c
+++ b/net/bridge/br_stp.c
@@ -415,7 +415,10 @@ void br_port_state_selection(struct net_bridge *br)
} else {
p->config_pending = 0;
p->topology_change_ack = 0;
- br_make_blocking(p);
+ if(br->stp_enabled == BR_NO_STP)
+ br_make_forwarding(p);
+ else
+ br_make_blocking(p);
}
}
diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c
index 19308e3..38d8dd7 100644
--- a/net/bridge/br_stp_if.c
+++ b/net/bridge/br_stp_if.c
@@ -153,14 +153,14 @@ static void br_stp_stop(struct net_bridge *br)
if (br->stp_enabled == BR_USER_STP) {
r = call_usermodehelper(BR_STP_PROG, argv, envp, UMH_WAIT_PROC);
br_info(br, "userspace STP stopped, return code %d\n", r);
-
- /* To start timers on any ports left in blocking */
- spin_lock_bh(&br->lock);
- br_port_state_selection(br);
- spin_unlock_bh(&br->lock);
}
br->stp_enabled = BR_NO_STP;
+
+ /* To push in forwarding state any ports left in blocking */
+ spin_lock_bh(&br->lock);
+ br_port_state_selection(br);
+ spin_unlock_bh(&br->lock);
}
void br_stp_set_enabled(struct net_bridge *br, unsigned long val)
--
1.7.3.4
^ permalink raw reply related
* [PATCH net-next] bnx2x: properly update skb when mtu > 1500
From: Dmitry Kravkov @ 2011-12-13 9:40 UTC (permalink / raw)
To: davem, netdev; +Cc: Dmitry Kravkov, Eilon Greenstein
Since commit e52fcb2462ac484e6dd6e68869536609f0216938 newly allocated
skb for small packets are not updated properly and dropped by stack.
Signed-off-by: Dmitry Kravkov <dmitry@broadcom.com>
Signed-off-by: Eilon Greenstein <eilong@broadcom.com>
---
drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 22 +++++++++++-----------
1 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
index 477bc97..64f5cf5 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
@@ -731,22 +731,22 @@ reuse_rx:
bnx2x_reuse_rx_data(fp, bd_cons, bd_prod);
goto next_rx;
}
+ }
- skb_put(skb, len);
- skb->protocol = eth_type_trans(skb, bp->dev);
+ skb_put(skb, len);
+ skb->protocol = eth_type_trans(skb, bp->dev);
- /* Set Toeplitz hash for a none-LRO skb */
- skb->rxhash = bnx2x_get_rxhash(bp, cqe_fp);
+ /* Set Toeplitz hash for a none-LRO skb */
+ skb->rxhash = bnx2x_get_rxhash(bp, cqe_fp);
- skb_checksum_none_assert(skb);
+ skb_checksum_none_assert(skb);
- if (bp->dev->features & NETIF_F_RXCSUM) {
+ if (bp->dev->features & NETIF_F_RXCSUM) {
- if (likely(BNX2X_RX_CSUM_OK(cqe)))
- skb->ip_summed = CHECKSUM_UNNECESSARY;
- else
- fp->eth_q_stats.hw_csum_err++;
- }
+ if (likely(BNX2X_RX_CSUM_OK(cqe)))
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ else
+ fp->eth_q_stats.hw_csum_err++;
}
skb_record_rx_queue(skb, fp->rx_queue);
--
1.7.7.2
^ 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