* Re: [PATCH wireless-2.6 or stable] iwlwifi: return error when fail to start scanning
From: Florian Mickler @ 2010-10-06 17:32 UTC (permalink / raw)
To: Guy, Wey-Yi
Cc: Stanislaw Gruszka, linville@tuxdriver.com, stable@kernel.org,
linux-wireless@vger.kernel.org, Chatre, Reinette,
ilw@linux.intel.com, Berg, Johannes, Cahill, Ben M,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <1286381578.12594.18.camel@wwguy-ubuntu>
On Wed, 06 Oct 2010 09:12:58 -0700
"Guy, Wey-Yi" <wey-yi.w.guy@intel.com> wrote:
> Hi Gruszka,
>
> On Wed, 2010-10-06 at 09:04 -0700, Stanislaw Gruszka wrote:
> > This is stable friendly backport of wireless-testing commit
> > 3eecce527c7434dfd16517ce08b49632c8a26717 "iwlwifi: unify scan start
> > checks". Wireless-testing version include also a lot of cleanups.
> >
> > On error case in {iwl3945,iwlagn}_request_scan we queue abort_scan work,
> > which in a fact do nothing, so we never complete the scan. Thats gives
> > "WARNING: at net/wireless/core.c:614 wdev_cleanup_work" and stopped
> > network connection until reload iwlwifi modules. Return of -EIO, and
> > stopping queuing any work is a fix.
> >
> > Note there are still many cases when we can get:
> >
> > WARNING: at net/wireless/core.c:614 wdev_cleanup_work
> > WARNING: at net/mac80211/scan.c:266 ieee80211_scan_completed
> > WARNING: at net/mac80211/scan.c:269 ieee80211_scan_complete
> >
> > which are not fixed. Unfortunately does not exist single, small fix
> > for that problems, and we probably will see some more bug reports
> > with scan warnings. As solution we rewrite iwl-scan.c code to avoid
> > all possible race conditions. That quite big patch set is queued
> > for 2.6.37 .
> >
> > Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
> > ---
> > Patch is intended for wireless-2.6, or in case when it does not
> > make 2.6.36 release, for -stable 2.6.36.y
> >
>
> Is this the replacement for Mickler's patch?
>
> Thanks
> Wey
>
This is on top of current mainline, as far as I see. (And it replaces
the patch I sent few hours ago)
Regards,
Flo
^ permalink raw reply
* Re: powerpc, fs_enet: scanning PHY after Linux is up
From: Grant Likely @ 2010-10-06 17:30 UTC (permalink / raw)
To: Holger brunck; +Cc: hs, linuxppc-dev, netdev, devicetree-discuss, Detlev Zundel
In-Reply-To: <4CAC7EB0.6080502@keymile.com>
On Wed, Oct 6, 2010 at 7:50 AM, Holger brunck <holger.brunck@keymile.com> wrote:
> Hello Heiko,
>
> On 10/06/2010 11:53 AM, Heiko Schocher wrote:
>>>> So, the question is, is there a possibility to solve this problem?
>>>>
>>>> If there is no standard option, what would be with adding a
>>>> "scan_phy" file in
>>>>
>>>> /proc/device-tree/soc\@f0000000/cpm\@119c0/mdio\@10d40
>>>> (or better destination?)
>>>>
>>>> which with we could rescan a PHY with
>>>> "echo addr > /proc/device-tree/soc\@f0000000/cpm\@119c0/mdio\@10d40/scan_phy"
>>>> (so there is no need for using of_find_node_by_path(), as we should
>>>> have the associated device node here, and can step through the child
>>>> nodes with "for_each_child_of_node(np, child)" and check if reg == addr)
>>>>
>>>> or shouldn;t be at least, if the phy couldn;t be found when opening
>>>> the port, retrigger a scanning, if the phy now is accessible?
>>>
>>> One option would be to still register a phy_device for each phy
>>> described in the device tree, but defer binding a driver to each phy
>>> that doesn't respond. Then at of_phy_find_device() time, if it
>>
>> Maybe I din;t get the trick, but the problem is, that
>> you can;t register a phy_device in drivers/of/of_mdio.c
>> of_mdiobus_register(), if the phy didn;t respond with the
>> phy_id ... and of_phy_find_device() is not (yet) used in fs_enet
>>
>>> matches with a phy_device that isn't bound to a driver yet, then
>>> re-trigger the binding operation. At which point the phy id can be
>>> probed and the correct driver can be chosen. If binding succeeds,
>>> then return the phy_device handle. If not, then fail as it currently
>>> does.
>>
>> Wouldn;t it be good, just if we need a PHY (on calling fs_enet_open)
>> to look if there is one?
>>
>> Something like that (not tested):
>>
>> in drivers/net/fs_enet/fs_enet-main.c in fs_init_phy()
>> called from fs_enet_open():
>>
>> Do first:
>> phydev = of_phy_find_device(fep->fpi->phy_node);
>>
>> Look if there is a driver (phy_dev->drv == NULL ?)
>>
>> If not, call new function
>> of_mdiobus_register_phy(mii_bus, fep->fpi->phy_node)
>> see below patch for it.
>>
>> If this succeeds, all is OK, and we can use this phy,
>> else ethernet not work.
>>
>> !!just no idea, how to get mii_bus pointer ...
>>
>
> in my understanding it should be posssible to get this pointer via the parent of
> the device_node you got via the private data of the fs_enet:
> fep->fpi->phy_node->parent should point you to the device_node for the mdio_bus.
Yes, this will give you the mdio bus node pointer.
> In the next step you should be able to get the pointer of of_device for the
> mdio_bus:
> ofdevice* ofdev = to_of_device(fep->fpi->phy_node->parent);
of_device is just an alias for platform_device now, and not all mdio
busses will be instantiated by a platform device. This method won't
always work. What is really needed is the pointer to the mii_bus
structure. That can be obtained by looping over the members of the
mdio_bus_class and comparing the mii_bus->device.parent->of_node to
the parent node from above.
g.
^ permalink raw reply
* Re: [PATCH] SIW: iWARP Protocol headers
From: Jason Gunthorpe @ 2010-10-06 17:25 UTC (permalink / raw)
To: Bernard Metzler
Cc: Bart Van Assche, linux-rdma-u79uwXL29TY76Z2rM5mHXA,
linux-rdma-owner-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <OFC70D3F8A.C5D69C2F-ONC12577B4.0044CFB7-C12577B4.0046C012-Xeyd2O9EBijQT0dZR+AlfA@public.gmane.org>
On Wed, Oct 06, 2010 at 02:52:46PM +0200, Bernard Metzler wrote:
>
>
> linux-rdma-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org wrote on 10/05/2010 06:10:45 PM:
>
> > On Tue, Oct 5, 2010 at 8:53 AM, Bernard Metzler <bmt-OA+xvbQnYDHMbYB6QlFGEg@public.gmane.org>
> wrote:
> > > +} __attribute__((__packed__));
> >
> > Apparently you make extensive use of the packed attribute. Please read
> > this blog entry, in which it is explained why this is harmful:
> >
> > http://digitalvampire.org/blog/index.php/2006/07/31/why-you-
> > shouldnt-use-__attribute__packed/
> >
> right, thanks, i was not aware of that
> big overhead.
> but...there are reasons why we may need it _packed_:
> (1) to get the size of the packet hdr
> (2) to read/write the hdr content
It is actually a little more complicated than just this. I assume you
are casting the structures over packet payloads? In this case you
have to guarentee alignment (or used packed everywhere). Does iwarp
have provisions for alignment? If so you can construct your bitfields
using the alignment type, ie if iWarp guarantees 4 bytes then the
biggest type you can use is u32 - then you can avoid using packed.
Mind you, I'm not sure how to guarentee alignment when you consider
all the possible sources of unalignment in the stack: TCP, IP, L2 stack ?
Otherwise you need a strategy for dealing with unaligned data for
portability. :(
Jason
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" 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: [v2 RFC PATCH 0/4] Implement multiqueue virtio-net
From: Krishna Kumar2 @ 2010-10-06 17:14 UTC (permalink / raw)
To: Arnd Bergmann
Cc: anthony, avi, davem, Ben Greear, kvm, Michael S. Tsirkin, netdev,
rusty
In-Reply-To: <201010061419.00257.arnd@arndb.de>
Arnd Bergmann <arnd@arndb.de> wrote on 10/06/2010 05:49:00 PM:
> > I don't see any reasons mentioned above. However, for higher
> > number of netperf sessions, I see a big increase in retransmissions:
> > _______________________________________
> > #netperf ORG NEW
> > BW (#retr) BW (#retr)
> > _______________________________________
> > 1 70244 (0) 64102 (0)
> > 4 21421 (0) 36570 (416)
> > 8 21746 (0) 38604 (148)
> > 16 21783 (0) 40632 (464)
> > 32 22677 (0) 37163 (1053)
> > 64 23648 (4) 36449 (2197)
> > 128 23251 (2) 31676 (3185)
> > _______________________________________
>
>
> This smells like it could be related to a problem that Ben Greear found
> recently (see "macvlan: Enable qdisc backoff logic"). When the hardware
> is busy, used to just drop the packet. With Ben's patch, we return
-EAGAIN
> to qemu (or vhost-net) to trigger a resend.
>
> I suppose what we really should do is feed that condition back to the
> guest network stack and implement the backoff in there.
Thanks for the pointer. I will take a look at this as I hadn't seen
this patch earlier. Is there any way to figure out if this is the
issue?
Thanks,
- KK
^ permalink raw reply
* Re: [v2 RFC PATCH 0/4] Implement multiqueue virtio-net
From: Krishna Kumar2 @ 2010-10-06 17:02 UTC (permalink / raw)
To: Michael S. Tsirkin; +Cc: anthony, arnd, avi, davem, kvm, netdev, rusty
In-Reply-To: <cover.1286372004.git.mst@redhat.com>
"Michael S. Tsirkin" <mst@redhat.com> wrote on 10/06/2010 07:04:31 PM:
> "Michael S. Tsirkin" <mst@redhat.com>
> 10/06/2010 07:04 PM
>
> To
>
> Krishna Kumar2/India/IBM@IBMIN
>
> cc
>
> rusty@rustcorp.com.au, davem@davemloft.net, kvm@vger.kernel.org,
> arnd@arndb.de, netdev@vger.kernel.org, avi@redhat.com,
anthony@codemonkey.ws
>
> Subject
>
> Re: [v2 RFC PATCH 0/4] Implement multiqueue virtio-net
>
> On Fri, Sep 17, 2010 at 03:33:07PM +0530, Krishna Kumar wrote:
> > For 1 TCP netperf, I ran 7 iterations and summed it. Explanation
> > for degradation for 1 stream case:
>
> I thought about possible RX/TX contention reasons, and I realized that
> we get/put the mm counter all the time. So I write the following: I
> haven't seen any performance gain from this in a single queue case, but
> maybe this will help multiqueue?
Great! I am on vacation tomorrow, but will test with this patch
tomorrow night.
Thanks,
- KK
^ permalink raw reply
* Re: powerpc, fs_enet: scanning PHY after Linux is up
From: Grant Likely @ 2010-10-06 16:52 UTC (permalink / raw)
To: hs; +Cc: linuxppc-dev, netdev, devicetree-discuss, Holger Brunck,
Detlev Zundel
In-Reply-To: <4CAC472F.5070001@denx.de>
On Wed, Oct 6, 2010 at 3:53 AM, Heiko Schocher <hs@denx.de> wrote:
> Hello Grant,
>
> Thanks for your answer!
>
> Grant Likely wrote:
>> On Mon, Oct 4, 2010 at 1:32 AM, Heiko Schocher <hs@denx.de> wrote:
>>> Hello all,
>>>
>>> we have on the mgcoge arch/powerpc/boot/dts/mgcoge.dts 3 fs_enet
>>> devices. The first is accessible on boot, and so get correct
>>> probed and works fine. For the other two fs_enet devices the PHYs
>>> are on startup in reset, and gets later, through userapplikations,
>>> out of reset ... so, on bootup, this 2 fs_enet devices could
>>> not detect the PHY in drivers/of/of_mdio.c of_mdiobus_register(),
>>> and if we want to use them later, we get for example:
>>>
>>> -bash-3.2# ifconfig eth2 172.31.31.33
>>> net eth2: Could not attach to PHY
>>> SIOCSIFFLAGS: No such device
>>>
>>> So the problem is, that we cannot rescan the PHYs, if they are
>>> accessible. Also we could not load the fs_enet driver as a module,
>>> because the first port is used fix.
>>>
>>> So, first question which comes in my mind, is:
>>>
>>> Is detecting the phy in drivers/of/of_mdio.c of_mdiobus_register()
>>> the right place, or should it not better be done, when really
>>> using the port?
>>>
>>> But we found another way to solve this issue:
>>>
>>> After the PHYs are out of reset, we just have to rescan the PHYs
>>> with (for example PHY with addr 1)
>>>
>>> err = mdiobus_scan(bus, 1);
>>>
>>> and
>>>
>>> of_find_node_by_path("/soc@f0000000/cpm@119c0/mdio@10d40/ethernet-phy@1");
>>> of_node_get(np);
>>> dev_archdata_set_node(&err->dev.archdata, np);
>>>
>>> but thats just a hack ...
>>
>> Yeah, that's a hack. It really needs to be done via the of_mdio
>> mechanisms so that dt <--> phy_device linkages remain consistent.
>
> Yep, I know, thats the reason why I ask ;-)
>
>>> So, the question is, is there a possibility to solve this problem?
>>>
>>> If there is no standard option, what would be with adding a
>>> "scan_phy" file in
>>>
>>> /proc/device-tree/soc\@f0000000/cpm\@119c0/mdio\@10d40
>>> (or better destination?)
>>>
>>> which with we could rescan a PHY with
>>> "echo addr > /proc/device-tree/soc\@f0000000/cpm\@119c0/mdio\@10d40/scan_phy"
>>> (so there is no need for using of_find_node_by_path(), as we should
>>> have the associated device node here, and can step through the child
>>> nodes with "for_each_child_of_node(np, child)" and check if reg == addr)
>>>
>>> or shouldn;t be at least, if the phy couldn;t be found when opening
>>> the port, retrigger a scanning, if the phy now is accessible?
>>
>> One option would be to still register a phy_device for each phy
>> described in the device tree, but defer binding a driver to each phy
>> that doesn't respond. Then at of_phy_find_device() time, if it
>
> Maybe I din;t get the trick, but the problem is, that
> you can;t register a phy_device in drivers/of/of_mdio.c
> of_mdiobus_register(), if the phy didn;t respond with the
> phy_id ... and of_phy_find_device() is not (yet) used in fs_enet
I'm suggesting modifying the phy layer so that it is possible to
register a phy_device that doesn't (yet) respond.
>> matches with a phy_device that isn't bound to a driver yet, then
>> re-trigger the binding operation. At which point the phy id can be
>> probed and the correct driver can be chosen. If binding succeeds,
>> then return the phy_device handle. If not, then fail as it currently
>> does.
>
> Wouldn;t it be good, just if we need a PHY (on calling fs_enet_open)
> to look if there is one?
>
> Something like that (not tested):
>
> in drivers/net/fs_enet/fs_enet-main.c in fs_init_phy()
> called from fs_enet_open():
>
> Do first:
> phydev = of_phy_find_device(fep->fpi->phy_node);
>
> Look if there is a driver (phy_dev->drv == NULL ?)
>
> If not, call new function
> of_mdiobus_register_phy(mii_bus, fep->fpi->phy_node)
> see below patch for it.
>
> If this succeeds, all is OK, and we can use this phy,
> else ethernet not work.
I don't like this approach because it muddies the concept of which
device is actually responsible for managing the phys on the bus. Is
it managed by the mdio bus device or the Ethernet device? It also has
a potential race condition. Whereas triggering a late driver bind
will be safe.
Alternately, I'd also be okay with a common method to trigger a
reprobe of a particular phy from userspace, but I fear that would be a
significantly more complex solution.
>
> !!just no idea, how to get mii_bus pointer ...
You'd have to get the parent of the phy node, and then loop over all
the registered mdio busses looking for a bus that uses that node.
>
> here the patch for the new function of_mdiobus_register_phy():
>
> diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c
> index b474833..7afbb0b 100644
> --- a/drivers/of/of_mdio.c
> +++ b/drivers/of/of_mdio.c
> @@ -21,6 +21,51 @@
> MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
> MODULE_LICENSE("GPL");
>
> +int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *child)
> +{
> + struct phy_device *phy;
> + const __be32 *addr;
> + int len;
> + int rc;
> +
> + /* A PHY must have a reg property in the range [0-31] */
> + addr = of_get_property(child, "reg", &len);
> + if (!addr || len < sizeof(*addr) || *addr >= 32 || *addr < 0) {
> + dev_err(&mdio->dev, "%s has invalid PHY address\n",
> + child->full_name);
> + return -1;
> + }
> +
> + if (mdio->irq) {
> + mdio->irq[*addr] = irq_of_parse_and_map(child, 0);
> + if (!mdio->irq[*addr])
> + mdio->irq[*addr] = PHY_POLL;
> + }
> +
> + phy = get_phy_device(mdio, be32_to_cpup(addr));
> + if (!phy || IS_ERR(phy)) {
> + dev_err(&mdio->dev, "error probing PHY at address %i\n",
> + *addr);
> + return -2;
> + }
> + phy_scan_fixups(phy);
> + /* Associate the OF node with the device structure so it
> + * can be looked up later */
> + of_node_get(child);
> + dev_archdata_set_node(&phy->dev.archdata, child);
> +
> + /* All data is now stored in the phy struct; register it */
> + rc = phy_device_register(phy);
> + if (rc) {
> + phy_device_free(phy);
> + of_node_put(child);
> + return -3;
> + }
> +
> + dev_dbg(&mdio->dev, "registered phy %s at address %i\n",
> + child->name, *addr);
> +}
> +
> /**
> * of_mdiobus_register - Register mii_bus and create PHYs from the device tree
> * @mdio: pointer to mii_bus structure
> @@ -31,7 +76,6 @@ MODULE_LICENSE("GPL");
> */
> int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
> {
> - struct phy_device *phy;
> struct device_node *child;
> int rc, i;
>
> @@ -51,46 +95,7 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
>
> /* Loop over the child nodes and register a phy_device for each one */
> for_each_child_of_node(np, child) {
> - const __be32 *addr;
> - int len;
> -
> - /* A PHY must have a reg property in the range [0-31] */
> - addr = of_get_property(child, "reg", &len);
> - if (!addr || len < sizeof(*addr) || *addr >= 32 || *addr < 0) {
> - dev_err(&mdio->dev, "%s has invalid PHY address\n",
> - child->full_name);
> - continue;
> - }
> -
> - if (mdio->irq) {
> - mdio->irq[*addr] = irq_of_parse_and_map(child, 0);
> - if (!mdio->irq[*addr])
> - mdio->irq[*addr] = PHY_POLL;
> - }
> -
> - phy = get_phy_device(mdio, be32_to_cpup(addr));
> - if (!phy || IS_ERR(phy)) {
> - dev_err(&mdio->dev, "error probing PHY at address %i\n",
> - *addr);
> - continue;
> - }
> - phy_scan_fixups(phy);
> -
> - /* Associate the OF node with the device structure so it
> - * can be looked up later */
> - of_node_get(child);
> - dev_archdata_set_node(&phy->dev.archdata, child);
> -
> - /* All data is now stored in the phy struct; register it */
> - rc = phy_device_register(phy);
> - if (rc) {
> - phy_device_free(phy);
> - of_node_put(child);
> - continue;
> - }
> -
> - dev_dbg(&mdio->dev, "registered phy %s at address %i\n",
> - child->name, *addr);
> + of_mdiobus_register_phy(mdio, child);
> }
>
> return 0;
>
> With this change, it would work on boot as actual (phy_device_register()
> will fail for the PHYs who don;t work when booting).
>
> Later, when opening the ethernet device, fs_init_phy, will look, if
> we have a valid phy with driver, if not we try to register it again.
> If this is successfull, we can use the device, if not we will fail,
> as now ... what do you think?
As mentioned above, it has the potential for some nasty confusion
about where exactly phy devices get registered, not to mention an
unlikely but potential race condition between the mdio bus and the
ethernet device trying to register the same phy since the MDIO bus
gets registered first, and then the bus loops over the phy nodes while
the bus is 'live'.
g.
^ permalink raw reply
* Re: [PATCH v2] Add Qualcomm Gobi 2000 driver.
From: Joe Perches @ 2010-10-06 16:46 UTC (permalink / raw)
To: Elly Jones; +Cc: netdev, jglasgow, mjg59, msb, olofj
In-Reply-To: <20101006151208.GB1571@google.com>
On Wed, 2010-10-06 at 11:12 -0400, Elly Jones wrote:
> From: Elizabeth Jones <ellyjones@google.com>
> create mode 100644 drivers/net/usb/gobi/qcusbnet.h
> create mode 100755 drivers/net/usb/gobi/qmi.c
I believe all files should be 644.
^ permalink raw reply
* Re: [PATCH wireless-2.6 or stable] iwlwifi: return error when fail to start scanning
From: Guy, Wey-Yi @ 2010-10-06 16:12 UTC (permalink / raw)
To: Stanislaw Gruszka
Cc: Florian Mickler, linville@tuxdriver.com, stable@kernel.org,
linux-wireless@vger.kernel.org, Chatre, Reinette,
ilw@linux.intel.com, Berg, Johannes, Cahill, Ben M,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <20101006160434.GB24581@redhat.com>
Hi Gruszka,
On Wed, 2010-10-06 at 09:04 -0700, Stanislaw Gruszka wrote:
> This is stable friendly backport of wireless-testing commit
> 3eecce527c7434dfd16517ce08b49632c8a26717 "iwlwifi: unify scan start
> checks". Wireless-testing version include also a lot of cleanups.
>
> On error case in {iwl3945,iwlagn}_request_scan we queue abort_scan work,
> which in a fact do nothing, so we never complete the scan. Thats gives
> "WARNING: at net/wireless/core.c:614 wdev_cleanup_work" and stopped
> network connection until reload iwlwifi modules. Return of -EIO, and
> stopping queuing any work is a fix.
>
> Note there are still many cases when we can get:
>
> WARNING: at net/wireless/core.c:614 wdev_cleanup_work
> WARNING: at net/mac80211/scan.c:266 ieee80211_scan_completed
> WARNING: at net/mac80211/scan.c:269 ieee80211_scan_complete
>
> which are not fixed. Unfortunately does not exist single, small fix
> for that problems, and we probably will see some more bug reports
> with scan warnings. As solution we rewrite iwl-scan.c code to avoid
> all possible race conditions. That quite big patch set is queued
> for 2.6.37 .
>
> Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
> ---
> Patch is intended for wireless-2.6, or in case when it does not
> make 2.6.36 release, for -stable 2.6.36.y
>
Is this the replacement for Mickler's patch?
Thanks
Wey
^ permalink raw reply
* Re: [PATCH] SIW: Module initialization
From: Bart Van Assche @ 2010-10-06 16:06 UTC (permalink / raw)
To: Stephen Hemminger
Cc: Bernard Metzler, netdev-u79uwXL29TY76Z2rM5mHXA,
linux-rdma-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20101006070242.3ca5a6d9@s6510>
On Wed, Oct 6, 2010 at 12:02 AM, Stephen Hemminger
<shemminger-ZtmgI6mnKB3QT0dZR+AlfA@public.gmane.org> wrote:
> On Tue, 5 Oct 2010 12:57:21 +0200
> Bart Van Assche <bvanassche-HInyCGIudOg@public.gmane.org> wrote:
>
>> > + * TODO: Dynamic device management (network device registration/removal).
>>
>> The current implementation is such that one siw device is created for
>> each network device found at kernel module load time. That means that
>> you force the user to load the siw kernel module after all other
>> kernel modules that register a network device. I'm not sure that's a
>> good idea.
>
> Then device should be controlled by a netlink (rtnl_link_ops) style
> interface see vlan_netlink.c. Using netlink is extensible and provides
> a cleaner interface than all these other parameterization methods.
That will be a lot more work to implement than what Jason proposed:
adding a sysfs variable per Ethernet driver, e.g.
/sys/class/net/eth0/enable_iwarp.
Bart.
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH wireless-2.6 or stable] iwlwifi: return error when fail to start scanning
From: Stanislaw Gruszka @ 2010-10-06 16:04 UTC (permalink / raw)
To: Florian Mickler
Cc: linville-2XuSBdqkA4R54TAoqtyWWQ, stable-DgEjT+Ai2ygdnm+yROfE0A,
linux-wireless-u79uwXL29TY76Z2rM5mHXA,
wey-yi.w.guy-ral2JQCrhuEAvxtiuMwx3w,
reinette.chatre-ral2JQCrhuEAvxtiuMwx3w,
ilw-VuQAYsv1563Yd54FQh9/CA, johannes.berg-ral2JQCrhuEAvxtiuMwx3w,
ben.m.cahill-ral2JQCrhuEAvxtiuMwx3w,
netdev-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20101006090228.GA2523-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
This is stable friendly backport of wireless-testing commit
3eecce527c7434dfd16517ce08b49632c8a26717 "iwlwifi: unify scan start
checks". Wireless-testing version include also a lot of cleanups.
On error case in {iwl3945,iwlagn}_request_scan we queue abort_scan work,
which in a fact do nothing, so we never complete the scan. Thats gives
"WARNING: at net/wireless/core.c:614 wdev_cleanup_work" and stopped
network connection until reload iwlwifi modules. Return of -EIO, and
stopping queuing any work is a fix.
Note there are still many cases when we can get:
WARNING: at net/wireless/core.c:614 wdev_cleanup_work
WARNING: at net/mac80211/scan.c:266 ieee80211_scan_completed
WARNING: at net/mac80211/scan.c:269 ieee80211_scan_complete
which are not fixed. Unfortunately does not exist single, small fix
for that problems, and we probably will see some more bug reports
with scan warnings. As solution we rewrite iwl-scan.c code to avoid
all possible race conditions. That quite big patch set is queued
for 2.6.37 .
Signed-off-by: Stanislaw Gruszka <sgruszka-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
Patch is intended for wireless-2.6, or in case when it does not
make 2.6.36 release, for -stable 2.6.36.y
drivers/net/wireless/iwlwifi/iwl-3945.h | 2 +-
drivers/net/wireless/iwlwifi/iwl-agn-lib.c | 18 ++++++------------
drivers/net/wireless/iwlwifi/iwl-agn.h | 2 +-
drivers/net/wireless/iwlwifi/iwl-core.h | 2 +-
drivers/net/wireless/iwlwifi/iwl-scan.c | 14 +++++++++++---
drivers/net/wireless/iwlwifi/iwl3945-base.c | 19 ++++++-------------
6 files changed, 26 insertions(+), 31 deletions(-)
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.h b/drivers/net/wireless/iwlwifi/iwl-3945.h
index bb2aeeb..98509c5 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945.h
+++ b/drivers/net/wireless/iwlwifi/iwl-3945.h
@@ -295,7 +295,7 @@ extern const struct iwl_channel_info *iwl3945_get_channel_info(
extern int iwl3945_rs_next_rate(struct iwl_priv *priv, int rate);
/* scanning */
-void iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif);
+int iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif);
/* Requires full declaration of iwl_priv before including */
#include "iwl-io.h"
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
index 8fd00a6..2be8faa 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
@@ -1147,7 +1147,7 @@ static int iwl_get_channels_for_scan(struct iwl_priv *priv,
return added;
}
-void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
+int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
{
struct iwl_host_cmd cmd = {
.id = REPLY_SCAN_CMD,
@@ -1394,24 +1394,18 @@ void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
scan->len = cpu_to_le16(cmd.len);
set_bit(STATUS_SCAN_HW, &priv->status);
- if (iwl_send_cmd_sync(priv, &cmd))
+ if (iwl_send_cmd_sync(priv, &cmd)) {
+ clear_bit(STATUS_SCAN_HW, &priv->status);
goto done;
+ }
queue_delayed_work(priv->workqueue, &priv->scan_check,
IWL_SCAN_CHECK_WATCHDOG);
- return;
+ return 0;
done:
- /* Cannot perform scan. Make sure we clear scanning
- * bits from status so next scan request can be performed.
- * If we don't clear scanning status bit here all next scan
- * will fail
- */
- clear_bit(STATUS_SCAN_HW, &priv->status);
- clear_bit(STATUS_SCANNING, &priv->status);
- /* inform mac80211 scan aborted */
- queue_work(priv->workqueue, &priv->abort_scan);
+ return -EIO;
}
int iwlagn_manage_ibss_station(struct iwl_priv *priv,
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.h b/drivers/net/wireless/iwlwifi/iwl-agn.h
index cc6464d..015da26 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.h
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.h
@@ -216,7 +216,7 @@ void iwl_reply_statistics(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb);
/* scan */
-void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif);
+int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif);
/* station mgmt */
int iwlagn_manage_ibss_station(struct iwl_priv *priv,
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h
index 5e6ee3d..110de0f 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.h
+++ b/drivers/net/wireless/iwlwifi/iwl-core.h
@@ -109,7 +109,7 @@ struct iwl_hcmd_utils_ops {
__le16 fc, __le32 *tx_flags);
int (*calc_rssi)(struct iwl_priv *priv,
struct iwl_rx_phy_res *rx_resp);
- void (*request_scan)(struct iwl_priv *priv, struct ieee80211_vif *vif);
+ int (*request_scan)(struct iwl_priv *priv, struct ieee80211_vif *vif);
};
struct iwl_apm_ops {
diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c
index a4b3663..290140a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-scan.c
+++ b/drivers/net/wireless/iwlwifi/iwl-scan.c
@@ -298,6 +298,7 @@ EXPORT_SYMBOL(iwl_init_scan_params);
static int iwl_scan_initiate(struct iwl_priv *priv, struct ieee80211_vif *vif)
{
+ int ret;
lockdep_assert_held(&priv->mutex);
IWL_DEBUG_INFO(priv, "Starting scan...\n");
@@ -308,9 +309,11 @@ static int iwl_scan_initiate(struct iwl_priv *priv, struct ieee80211_vif *vif)
if (WARN_ON(!priv->cfg->ops->utils->request_scan))
return -EOPNOTSUPP;
- priv->cfg->ops->utils->request_scan(priv, vif);
+ ret = priv->cfg->ops->utils->request_scan(priv, vif);
+ if (ret)
+ clear_bit(STATUS_SCANNING, &priv->status);
+ return ret;
- return 0;
}
int iwl_mac_hw_scan(struct ieee80211_hw *hw,
@@ -380,6 +383,7 @@ void iwl_internal_short_hw_scan(struct iwl_priv *priv)
void iwl_bg_start_internal_scan(struct work_struct *work)
{
+ int ret;
struct iwl_priv *priv =
container_of(work, struct iwl_priv, start_internal_scan);
@@ -414,7 +418,11 @@ void iwl_bg_start_internal_scan(struct work_struct *work)
if (WARN_ON(!priv->cfg->ops->utils->request_scan))
goto unlock;
- priv->cfg->ops->utils->request_scan(priv, NULL);
+ ret = priv->cfg->ops->utils->request_scan(priv, NULL);
+ if (ret) {
+ clear_bit(STATUS_SCANNING, &priv->status);
+ priv->is_internal_short_scan = false;
+ }
unlock:
mutex_unlock(&priv->mutex);
}
diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c
index d31661c..fc82da0 100644
--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
@@ -2799,7 +2799,7 @@ static void iwl3945_rfkill_poll(struct work_struct *data)
}
-void iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
+int iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
{
struct iwl_host_cmd cmd = {
.id = REPLY_SCAN_CMD,
@@ -3000,25 +3000,18 @@ void iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
scan->len = cpu_to_le16(cmd.len);
set_bit(STATUS_SCAN_HW, &priv->status);
- if (iwl_send_cmd_sync(priv, &cmd))
+ if (iwl_send_cmd_sync(priv, &cmd)) {
+ clear_bit(STATUS_SCAN_HW, &priv->status);
goto done;
+ }
queue_delayed_work(priv->workqueue, &priv->scan_check,
IWL_SCAN_CHECK_WATCHDOG);
- return;
+ return 0;
done:
- /* can not perform scan make sure we clear scanning
- * bits from status so next scan request can be performed.
- * if we dont clear scanning status bit here all next scan
- * will fail
- */
- clear_bit(STATUS_SCAN_HW, &priv->status);
- clear_bit(STATUS_SCANNING, &priv->status);
^ permalink raw reply related
* Re: [PATCH] SIW: iWARP Protocol headers
From: Bart Van Assche @ 2010-10-06 16:02 UTC (permalink / raw)
To: Bernard Metzler; +Cc: linux-rdma, linux-rdma-owner, netdev
In-Reply-To: <OFC70D3F8A.C5D69C2F-ONC12577B4.0044CFB7-C12577B4.0046C012@ch.ibm.com>
On Wed, Oct 6, 2010 at 2:52 PM, Bernard Metzler <BMT@zurich.ibm.com> wrote:
>
>
> linux-rdma-owner@vger.kernel.org wrote on 10/05/2010 06:10:45 PM:
>
> > On Tue, Oct 5, 2010 at 8:53 AM, Bernard Metzler <bmt@zurich.ibm.com>
> wrote:
> > > +} __attribute__((__packed__));
> >
> > Apparently you make extensive use of the packed attribute. Please read
> > this blog entry, in which it is explained why this is harmful:
> >
> > http://digitalvampire.org/blog/index.php/2006/07/31/why-you-
> > shouldnt-use-__attribute__packed/
> >
> right, thanks, i was not aware of that
> big overhead.
> but...there are reasons why we may need it _packed_:
> (1) to get the size of the packet hdr
> (2) to read/write the hdr content
>
> (1) is not harmful. (2) could be limited to
> just one read/write operation per rx/tx path
> and all other access could be done on a
> structure which contains the host representation
> of the hdr content. both structures could coexist
> in a union, part of the rx or tx context.
> would that satisfy your complain?
Please have a look at e.g. the header file <scsi/srp.h>. As you can
see in that header file, __attribute__((__packed__)) is only specified
when absolutely necessary.
Bart.
^ permalink raw reply
* [PATCH net-next] neigh: RCU conversion of struct neighbour
From: Eric Dumazet @ 2010-10-06 15:54 UTC (permalink / raw)
To: David Miller; +Cc: netdev
This is the second step for neighbour RCU conversion.
(first was commit d6bf7817 : RCU conversion of neigh hash table)
neigh_lookup() becomes lockless, but still take a reference on found
neighbour. (no more read_lock()/read_unlock() on tbl->lock)
struct neighbour gets an additional rcu_head field and is freed after an
RCU grace period.
Future work would need to eventually not take a reference on neighbour
for temporary dst (DST_NOCACHE), but this would need dst->_neighbour to
use a noref bit like we did for skb->_dst.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
include/net/neighbour.h | 5 -
net/core/neighbour.c | 137 +++++++++++++++++++++++---------------
2 files changed, 88 insertions(+), 54 deletions(-)
diff --git a/include/net/neighbour.h b/include/net/neighbour.h
index 37845da..a4538d5 100644
--- a/include/net/neighbour.h
+++ b/include/net/neighbour.h
@@ -91,7 +91,7 @@ struct neigh_statistics {
#define NEIGH_CACHE_STAT_INC(tbl, field) this_cpu_inc((tbl)->stats->field)
struct neighbour {
- struct neighbour *next;
+ struct neighbour __rcu *next;
struct neigh_table *tbl;
struct neigh_parms *parms;
struct net_device *dev;
@@ -111,6 +111,7 @@ struct neighbour {
struct sk_buff_head arp_queue;
struct timer_list timer;
const struct neigh_ops *ops;
+ struct rcu_head rcu;
u8 primary_key[0];
};
@@ -139,7 +140,7 @@ struct pneigh_entry {
*/
struct neigh_hash_table {
- struct neighbour **hash_buckets;
+ struct neighbour __rcu **hash_buckets;
unsigned int hash_mask;
__u32 hash_rnd;
struct rcu_head rcu;
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index dd8920e..2445af2 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -139,10 +139,12 @@ static int neigh_forced_gc(struct neigh_table *tbl)
nht = rcu_dereference_protected(tbl->nht,
lockdep_is_held(&tbl->lock));
for (i = 0; i <= nht->hash_mask; i++) {
- struct neighbour *n, **np;
+ struct neighbour *n;
+ struct neighbour __rcu **np;
np = &nht->hash_buckets[i];
- while ((n = *np) != NULL) {
+ while ((n = rcu_dereference_protected(*np,
+ lockdep_is_held(&tbl->lock))) != NULL) {
/* Neighbour record may be discarded if:
* - nobody refers to it.
* - it is not permanent
@@ -150,7 +152,9 @@ static int neigh_forced_gc(struct neigh_table *tbl)
write_lock(&n->lock);
if (atomic_read(&n->refcnt) == 1 &&
!(n->nud_state & NUD_PERMANENT)) {
- *np = n->next;
+ rcu_assign_pointer(*np,
+ rcu_dereference_protected(n->next,
+ lockdep_is_held(&tbl->lock)));
n->dead = 1;
shrunk = 1;
write_unlock(&n->lock);
@@ -208,14 +212,18 @@ static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev)
lockdep_is_held(&tbl->lock));
for (i = 0; i <= nht->hash_mask; i++) {
- struct neighbour *n, **np = &nht->hash_buckets[i];
+ struct neighbour *n;
+ struct neighbour __rcu **np = &nht->hash_buckets[i];
- while ((n = *np) != NULL) {
+ while ((n = rcu_dereference_protected(*np,
+ lockdep_is_held(&tbl->lock))) != NULL) {
if (dev && n->dev != dev) {
np = &n->next;
continue;
}
- *np = n->next;
+ rcu_assign_pointer(*np,
+ rcu_dereference_protected(n->next,
+ lockdep_is_held(&tbl->lock)));
write_lock(&n->lock);
neigh_del_timer(n);
n->dead = 1;
@@ -323,7 +331,7 @@ static struct neigh_hash_table *neigh_hash_alloc(unsigned int entries)
kfree(ret);
return NULL;
}
- ret->hash_buckets = buckets;
+ rcu_assign_pointer(ret->hash_buckets, buckets);
ret->hash_mask = entries - 1;
get_random_bytes(&ret->hash_rnd, sizeof(ret->hash_rnd));
return ret;
@@ -362,17 +370,22 @@ static struct neigh_hash_table *neigh_hash_grow(struct neigh_table *tbl,
for (i = 0; i <= old_nht->hash_mask; i++) {
struct neighbour *n, *next;
- for (n = old_nht->hash_buckets[i];
+ for (n = rcu_dereference_protected(old_nht->hash_buckets[i],
+ lockdep_is_held(&tbl->lock));
n != NULL;
n = next) {
hash = tbl->hash(n->primary_key, n->dev,
new_nht->hash_rnd);
hash &= new_nht->hash_mask;
- next = n->next;
-
- n->next = new_nht->hash_buckets[hash];
- new_nht->hash_buckets[hash] = n;
+ next = rcu_dereference_protected(n->next,
+ lockdep_is_held(&tbl->lock));
+
+ rcu_assign_pointer(n->next,
+ rcu_dereference_protected(
+ new_nht->hash_buckets[hash],
+ lockdep_is_held(&tbl->lock)));
+ rcu_assign_pointer(new_nht->hash_buckets[hash], n);
}
}
@@ -394,15 +407,18 @@ struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey,
rcu_read_lock_bh();
nht = rcu_dereference_bh(tbl->nht);
hash_val = tbl->hash(pkey, dev, nht->hash_rnd) & nht->hash_mask;
- read_lock(&tbl->lock);
- for (n = nht->hash_buckets[hash_val]; n; n = n->next) {
+
+ for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]);
+ n != NULL;
+ n = rcu_dereference_bh(n->next)) {
if (dev == n->dev && !memcmp(n->primary_key, pkey, key_len)) {
- neigh_hold(n);
+ if (!atomic_inc_not_zero(&n->refcnt))
+ n = NULL;
NEIGH_CACHE_STAT_INC(tbl, hits);
break;
}
}
- read_unlock(&tbl->lock);
+
rcu_read_unlock_bh();
return n;
}
@@ -421,16 +437,19 @@ struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, struct net *net,
rcu_read_lock_bh();
nht = rcu_dereference_bh(tbl->nht);
hash_val = tbl->hash(pkey, NULL, nht->hash_rnd) & nht->hash_mask;
- read_lock(&tbl->lock);
- for (n = nht->hash_buckets[hash_val]; n; n = n->next) {
+
+ for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]);
+ n != NULL;
+ n = rcu_dereference_bh(n->next)) {
if (!memcmp(n->primary_key, pkey, key_len) &&
net_eq(dev_net(n->dev), net)) {
- neigh_hold(n);
+ if (!atomic_inc_not_zero(&n->refcnt))
+ n = NULL;
NEIGH_CACHE_STAT_INC(tbl, hits);
break;
}
}
- read_unlock(&tbl->lock);
+
rcu_read_unlock_bh();
return n;
}
@@ -483,7 +502,11 @@ struct neighbour *neigh_create(struct neigh_table *tbl, const void *pkey,
goto out_tbl_unlock;
}
- for (n1 = nht->hash_buckets[hash_val]; n1; n1 = n1->next) {
+ for (n1 = rcu_dereference_protected(nht->hash_buckets[hash_val],
+ lockdep_is_held(&tbl->lock));
+ n1 != NULL;
+ n1 = rcu_dereference_protected(n1->next,
+ lockdep_is_held(&tbl->lock))) {
if (dev == n1->dev && !memcmp(n1->primary_key, pkey, key_len)) {
neigh_hold(n1);
rc = n1;
@@ -491,10 +514,12 @@ struct neighbour *neigh_create(struct neigh_table *tbl, const void *pkey,
}
}
- n->next = nht->hash_buckets[hash_val];
- nht->hash_buckets[hash_val] = n;
n->dead = 0;
neigh_hold(n);
+ rcu_assign_pointer(n->next,
+ rcu_dereference_protected(nht->hash_buckets[hash_val],
+ lockdep_is_held(&tbl->lock)));
+ rcu_assign_pointer(nht->hash_buckets[hash_val], n);
write_unlock_bh(&tbl->lock);
NEIGH_PRINTK2("neigh %p is created.\n", n);
rc = n;
@@ -651,6 +676,12 @@ static inline void neigh_parms_put(struct neigh_parms *parms)
neigh_parms_destroy(parms);
}
+static void neigh_destroy_rcu(struct rcu_head *head)
+{
+ struct neighbour *neigh = container_of(head, struct neighbour, rcu);
+
+ kmem_cache_free(neigh->tbl->kmem_cachep, neigh);
+}
/*
* neighbour must already be out of the table;
*
@@ -690,7 +721,7 @@ void neigh_destroy(struct neighbour *neigh)
NEIGH_PRINTK2("neigh %p is destroyed.\n", neigh);
atomic_dec(&neigh->tbl->entries);
- kmem_cache_free(neigh->tbl->kmem_cachep, neigh);
+ call_rcu(&neigh->rcu, neigh_destroy_rcu);
}
EXPORT_SYMBOL(neigh_destroy);
@@ -731,7 +762,8 @@ static void neigh_connect(struct neighbour *neigh)
static void neigh_periodic_work(struct work_struct *work)
{
struct neigh_table *tbl = container_of(work, struct neigh_table, gc_work.work);
- struct neighbour *n, **np;
+ struct neighbour *n;
+ struct neighbour __rcu **np;
unsigned int i;
struct neigh_hash_table *nht;
@@ -756,7 +788,8 @@ static void neigh_periodic_work(struct work_struct *work)
for (i = 0 ; i <= nht->hash_mask; i++) {
np = &nht->hash_buckets[i];
- while ((n = *np) != NULL) {
+ while ((n = rcu_dereference_protected(*np,
+ lockdep_is_held(&tbl->lock))) != NULL) {
unsigned int state;
write_lock(&n->lock);
@@ -1213,8 +1246,8 @@ static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst,
}
/* This function can be used in contexts, where only old dev_queue_xmit
- worked, f.e. if you want to override normal output path (eql, shaper),
- but resolution is not made yet.
+ * worked, f.e. if you want to override normal output path (eql, shaper),
+ * but resolution is not made yet.
*/
int neigh_compat_output(struct sk_buff *skb)
@@ -2123,7 +2156,7 @@ static void neigh_update_notify(struct neighbour *neigh)
static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
struct netlink_callback *cb)
{
- struct net * net = sock_net(skb->sk);
+ struct net *net = sock_net(skb->sk);
struct neighbour *n;
int rc, h, s_h = cb->args[1];
int idx, s_idx = idx = cb->args[2];
@@ -2132,13 +2165,14 @@ static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
rcu_read_lock_bh();
nht = rcu_dereference_bh(tbl->nht);
- read_lock(&tbl->lock);
for (h = 0; h <= nht->hash_mask; h++) {
if (h < s_h)
continue;
if (h > s_h)
s_idx = 0;
- for (n = nht->hash_buckets[h], idx = 0; n; n = n->next) {
+ for (n = rcu_dereference_bh(nht->hash_buckets[h]), idx = 0;
+ n != NULL;
+ n = rcu_dereference_bh(n->next)) {
if (!net_eq(dev_net(n->dev), net))
continue;
if (idx < s_idx)
@@ -2150,13 +2184,12 @@ static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
rc = -1;
goto out;
}
- next:
+next:
idx++;
}
}
rc = skb->len;
out:
- read_unlock(&tbl->lock);
rcu_read_unlock_bh();
cb->args[1] = h;
cb->args[2] = idx;
@@ -2195,11 +2228,13 @@ void neigh_for_each(struct neigh_table *tbl, void (*cb)(struct neighbour *, void
rcu_read_lock_bh();
nht = rcu_dereference_bh(tbl->nht);
- read_lock(&tbl->lock);
+ read_lock(&tbl->lock); /* avoid resizes */
for (chain = 0; chain <= nht->hash_mask; chain++) {
struct neighbour *n;
- for (n = nht->hash_buckets[chain]; n; n = n->next)
+ for (n = rcu_dereference_bh(nht->hash_buckets[chain]);
+ n != NULL;
+ n = rcu_dereference_bh(n->next))
cb(n, cookie);
}
read_unlock(&tbl->lock);
@@ -2217,16 +2252,20 @@ void __neigh_for_each_release(struct neigh_table *tbl,
nht = rcu_dereference_protected(tbl->nht,
lockdep_is_held(&tbl->lock));
for (chain = 0; chain <= nht->hash_mask; chain++) {
- struct neighbour *n, **np;
+ struct neighbour *n;
+ struct neighbour __rcu **np;
np = &nht->hash_buckets[chain];
- while ((n = *np) != NULL) {
+ while ((n = rcu_dereference_protected(*np,
+ lockdep_is_held(&tbl->lock))) != NULL) {
int release;
write_lock(&n->lock);
release = cb(n);
if (release) {
- *np = n->next;
+ rcu_assign_pointer(*np,
+ rcu_dereference_protected(n->next,
+ lockdep_is_held(&tbl->lock)));
n->dead = 1;
} else
np = &n->next;
@@ -2250,7 +2289,7 @@ static struct neighbour *neigh_get_first(struct seq_file *seq)
state->flags &= ~NEIGH_SEQ_IS_PNEIGH;
for (bucket = 0; bucket <= nht->hash_mask; bucket++) {
- n = nht->hash_buckets[bucket];
+ n = rcu_dereference_bh(nht->hash_buckets[bucket]);
while (n) {
if (!net_eq(dev_net(n->dev), net))
@@ -2267,8 +2306,8 @@ static struct neighbour *neigh_get_first(struct seq_file *seq)
break;
if (n->nud_state & ~NUD_NOARP)
break;
- next:
- n = n->next;
+next:
+ n = rcu_dereference_bh(n->next);
}
if (n)
@@ -2292,7 +2331,7 @@ static struct neighbour *neigh_get_next(struct seq_file *seq,
if (v)
return n;
}
- n = n->next;
+ n = rcu_dereference_bh(n->next);
while (1) {
while (n) {
@@ -2309,8 +2348,8 @@ static struct neighbour *neigh_get_next(struct seq_file *seq,
if (n->nud_state & ~NUD_NOARP)
break;
- next:
- n = n->next;
+next:
+ n = rcu_dereference_bh(n->next);
}
if (n)
@@ -2319,7 +2358,7 @@ static struct neighbour *neigh_get_next(struct seq_file *seq,
if (++state->bucket > nht->hash_mask)
break;
- n = nht->hash_buckets[state->bucket];
+ n = rcu_dereference_bh(nht->hash_buckets[state->bucket]);
}
if (n && pos)
@@ -2417,7 +2456,6 @@ static void *neigh_get_idx_any(struct seq_file *seq, loff_t *pos)
}
void *neigh_seq_start(struct seq_file *seq, loff_t *pos, struct neigh_table *tbl, unsigned int neigh_seq_flags)
- __acquires(tbl->lock)
__acquires(rcu_bh)
{
struct neigh_seq_state *state = seq->private;
@@ -2428,7 +2466,7 @@ void *neigh_seq_start(struct seq_file *seq, loff_t *pos, struct neigh_table *tbl
rcu_read_lock_bh();
state->nht = rcu_dereference_bh(tbl->nht);
- read_lock(&tbl->lock);
+
return *pos ? neigh_get_idx_any(seq, pos) : SEQ_START_TOKEN;
}
EXPORT_SYMBOL(neigh_seq_start);
@@ -2461,13 +2499,8 @@ out:
EXPORT_SYMBOL(neigh_seq_next);
void neigh_seq_stop(struct seq_file *seq, void *v)
- __releases(tbl->lock)
__releases(rcu_bh)
{
- struct neigh_seq_state *state = seq->private;
- struct neigh_table *tbl = state->tbl;
-
- read_unlock(&tbl->lock);
rcu_read_unlock_bh();
}
EXPORT_SYMBOL(neigh_seq_stop);
^ permalink raw reply related
* Re: [net-next-2.6 PATCH] net: netif_set_real_num_rx_queues may cap num_rx_queues at init time
From: Ben Hutchings @ 2010-10-06 15:31 UTC (permalink / raw)
To: Eric Dumazet, Matt Carlson
Cc: John Fastabend, netdev@vger.kernel.org, therbert@google.com
In-Reply-To: <1286378626.9417.24.camel@edumazet-laptop>
On Wed, 2010-10-06 at 17:23 +0200, Eric Dumazet wrote:
> Le mercredi 06 octobre 2010 à 16:07 +0100, Ben Hutchings a écrit :
>
> > The waste of memory is minimal now that we only allocate kobjects for
> > real_num_rx_queues.
>
> Thats strange, here with tg3 (and mono queue device) :
>
> 0a:04.0 Ethernet controller: Broadcom Corporation NetXtreme BCM5715S
> Gigabit Ethernet (rev a3)
>
> grep . /sys/class/net/eth2/queues/rx-*/rps_cpus
> /sys/class/net/eth2/queues/rx-0/rps_cpus:00000000
> /sys/class/net/eth2/queues/rx-1/rps_cpus:00000000
> /sys/class/net/eth2/queues/rx-2/rps_cpus:00000000
> /sys/class/net/eth2/queues/rx-3/rps_cpus:00000000
> /sys/class/net/eth2/queues/rx-4/rps_cpus:00000000
It looks like I missed a necessary call to
netif_set_real_num_rx_queues() in tg3. I suggest that Matt should check
and correct this since I got the numbers wrong last time.
Ben.
--
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply
* Re: [net-next-2.6 PATCH] net: netif_set_real_num_rx_queues may cap num_rx_queues at init time
From: Eric Dumazet @ 2010-10-06 15:23 UTC (permalink / raw)
To: Ben Hutchings; +Cc: John Fastabend, netdev@vger.kernel.org, therbert@google.com
In-Reply-To: <1286377633.2371.11.camel@achroite.uk.solarflarecom.com>
Le mercredi 06 octobre 2010 à 16:07 +0100, Ben Hutchings a écrit :
> The waste of memory is minimal now that we only allocate kobjects for
> real_num_rx_queues.
Thats strange, here with tg3 (and mono queue device) :
0a:04.0 Ethernet controller: Broadcom Corporation NetXtreme BCM5715S
Gigabit Ethernet (rev a3)
grep . /sys/class/net/eth2/queues/rx-*/rps_cpus
/sys/class/net/eth2/queues/rx-0/rps_cpus:00000000
/sys/class/net/eth2/queues/rx-1/rps_cpus:00000000
/sys/class/net/eth2/queues/rx-2/rps_cpus:00000000
/sys/class/net/eth2/queues/rx-3/rps_cpus:00000000
/sys/class/net/eth2/queues/rx-4/rps_cpus:00000000
^ permalink raw reply
* Re: [net-next-2.6 PATCH] net: netif_set_real_num_rx_queues may cap num_rx_queues at init time
From: John Fastabend @ 2010-10-06 15:20 UTC (permalink / raw)
To: Ben Hutchings; +Cc: Eric Dumazet, netdev@vger.kernel.org, therbert@google.com
In-Reply-To: <1286377633.2371.11.camel@achroite.uk.solarflarecom.com>
On 10/6/2010 8:07 AM, Ben Hutchings wrote:
> On Wed, 2010-10-06 at 07:52 -0700, John Fastabend wrote:
>> On 10/5/2010 10:45 AM, John Fastabend wrote:
>>> On 10/5/2010 9:34 AM, Ben Hutchings wrote:
>>>> On Tue, 2010-10-05 at 09:08 -0700, John Fastabend wrote:
>>>>> On 10/4/2010 10:35 PM, Eric Dumazet wrote:
> [...]
>>>>>> Why should we keep num_rx_queues > real_num_rx_queues ?
>>>>>>
>>>>>
>>>>> If we do not ever need them then we should not keep them I agree.
>>>>> But having netif_set_real_num_rx_queues set something other then
>>>>> 'real_num_rx_queues' does not seem right to me at least. Also
>>>>> netif_set_real_num_tx_queues and netif_set_real_num_rx_queues have
>>>>> different behavior. It would be nice if this weren't the case but
>>>>> they allocate queues in two places.
>>>> [...]
>>>>
>>>> I only did this to satisfy Eric's desire to reduce memory usage.
>>>> However, I believe that there are currently no drivers that dynamically
>>>> increase numbers of RX or TX queues. Until there are, there is not much
>>>> point in removing this assignment to num_rx_queues.
>>>>
>>>> Ben.
>>>>
>>>
>>> ixgbe increases the real_num_[rx|tx]_queues when FCoE or DCB is enabled.
>>> Also many of the drivers could increase the number of queues if they were
>>> given more interrupt vectors at some point.
>>
>>
>> If I update the handful drivers that use netif_set_real_num_rx_queues()
>> before the netdevice is registered to explicitly set num_rx_queues this
>> would address Eric's concerns and fix drivers that really only want to set
>> real_num_rx_queue.
>>
>> Any thoughts?
>
> Don't add assignments to num_rx_queues. If it's useful to increase the
> number of RX queues later then just remove the assignment to
> num_rx_queues from netif_set_real_num_rx_queues() and be done with it.
> The waste of memory is minimal now that we only allocate kobjects for
> real_num_rx_queues.
>
> Ben.
>
OK Thanks Ben. I will get a better description on this and resend it.
John.
^ permalink raw reply
* Re: [net-next-2.6 PATCH] net: netif_set_real_num_rx_queues may cap num_rx_queues at init time
From: Eric Dumazet @ 2010-10-06 15:20 UTC (permalink / raw)
To: John Fastabend; +Cc: Ben Hutchings, netdev@vger.kernel.org, therbert@google.com
In-Reply-To: <4CAC8D11.2060604@intel.com>
Le mercredi 06 octobre 2010 à 07:52 -0700, John Fastabend a écrit :
> If I update the handful drivers that use netif_set_real_num_rx_queues()
> before the netdevice is registered to explicitly set num_rx_queues this
> would address Eric's concerns and fix drivers that really only want to set
> real_num_rx_queue.
>
John, it seems current API is not very clean/orthogonal.
If you believe a driver can increase real_num_rx_queue, then we should
apply your patch, and refine API if necessary.
We now allocate rx queues in register_netdevice(), maybe we should do
the same for tx queues.
^ permalink raw reply
* [PATCH v2] Add Qualcomm Gobi 2000 driver.
From: Elly Jones @ 2010-10-06 15:12 UTC (permalink / raw)
To: netdev; +Cc: jglasgow, mjg59, msb, olofj
From: Elizabeth Jones <ellyjones@google.com>
This driver is a rewrite of the original Qualcomm GPL driver, released as part
of Qualcomm's "Code Aurora" initiative. The driver has been transformed into
Linux kernel style and made to use kernel APIs where appropriate; some bugs have
also been fixed. Note that the device in question requires firmware and a
firmware loader; the latter has been written by mjg (see
http://www.codon.org.uk/~mjg59/gobi_loader/).
Special thanks go to Joe Perches <joe@perches.com> for major cleanup.
Signed-off-by: Elizabeth Jones <ellyjones@google.com>
Signed-off-by: Jason Glasgow <jglasgow@google.com>
---
drivers/net/usb/Kconfig | 6 +
drivers/net/usb/Makefile | 2 +-
drivers/net/usb/gobi/Makefile | 2 +
drivers/net/usb/gobi/README | 30 +
drivers/net/usb/gobi/qcusbnet.c | 649 ++++++++++++++++
drivers/net/usb/gobi/qcusbnet.h | 24 +
drivers/net/usb/gobi/qmi.c | 358 +++++++++
drivers/net/usb/gobi/qmi.h | 67 ++
drivers/net/usb/gobi/qmidevice.c | 1590 ++++++++++++++++++++++++++++++++++++++
drivers/net/usb/gobi/qmidevice.h | 35 +
drivers/net/usb/gobi/structs.h | 92 +++
11 files changed, 2854 insertions(+), 1 deletions(-)
create mode 100755 drivers/net/usb/gobi/Makefile
create mode 100755 drivers/net/usb/gobi/README
create mode 100644 drivers/net/usb/gobi/qcusbnet.c
create mode 100644 drivers/net/usb/gobi/qcusbnet.h
create mode 100755 drivers/net/usb/gobi/qmi.c
create mode 100755 drivers/net/usb/gobi/qmi.h
create mode 100755 drivers/net/usb/gobi/qmidevice.c
create mode 100755 drivers/net/usb/gobi/qmidevice.h
create mode 100755 drivers/net/usb/gobi/structs.h
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index d7b7018..8a210d9 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -406,4 +406,10 @@ config USB_SIERRA_NET
To compile this driver as a module, choose M here: the
module will be called sierra_net.
+config USB_NET_GOBI
+ tristate "Qualcomm Gobi"
+ depends on USB_USBNET
+ help
+ Choose this option if you have a Qualcomm Gobi 2000 cellular modem.
+
endmenu
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index b13a279..c80e037 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -25,4 +25,4 @@ obj-$(CONFIG_USB_NET_INT51X1) += int51x1.o
obj-$(CONFIG_USB_CDC_PHONET) += cdc-phonet.o
obj-$(CONFIG_USB_IPHETH) += ipheth.o
obj-$(CONFIG_USB_SIERRA_NET) += sierra_net.o
-
+obj-$(CONFIG_USB_NET_GOBI) += gobi/
diff --git a/drivers/net/usb/gobi/Makefile b/drivers/net/usb/gobi/Makefile
new file mode 100755
index 0000000..a6635f6
--- /dev/null
+++ b/drivers/net/usb/gobi/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_USB_NET_GOBI) += gobi.o
+gobi-objs += qcusbnet.o qmidevice.o qmi.o
diff --git a/drivers/net/usb/gobi/README b/drivers/net/usb/gobi/README
new file mode 100755
index 0000000..113c523
--- /dev/null
+++ b/drivers/net/usb/gobi/README
@@ -0,0 +1,30 @@
+Qualcomm Gobi 2000 driver
+-------------------------
+
+This directory contains a driver for the Qualcomm Gobi 2000 CDMA/GSM modem. The
+device speaks a protocol called QMI with its host; this driver's responsibility
+is to multiplex transactions over QMI connections. Note that the relatively
+simple encoding defined in qmi.h and qmi.c is only the encapsulating protocol;
+the device speaks a more complex set of protocols embodied in the closed-source
+Gobi SDK which are necessary to use advanced features of the device.
+Furthermore, firmware (also proprietary) is needed to make the device useful. An
+open-source firmware loader exists: http://www.codon.org.uk/~mjg59/gobi_loader/.
+
+Interfaces:
+This driver presents two separate interfaces to userspace:
+- usbN, an ordinary usb network interface
+- /dev/qcqmiN, a channel to speak to the device using QMI messages
+The latter is for use by the Gobi SDK.
+
+History:
+This driver's original incarnation (of which this is a rewrite) was released
+under GPLv2 by Qualcomm on their Code Aurora site:
+https://www.codeaurora.org/gitweb/quic/la/?p=kernel/msm.git;a=commit;h=b5135a880f8942f990e8c2e383f7f876beacc55b
+
+Issues:
+Help and commentary on any of the issues below would be appreciated.
+- The locking in devqmi_close() is not threadsafe: we're walking a list of tasks
+ without holding a lock on the list. The original driver did this and I have
+ left it intact because I do not fully understand what's going on here.
+- The driver seems to deadlock very occasionally (one in ~6000 reboots during
+ stress testing). I've had no luck tracking this problem down.
diff --git a/drivers/net/usb/gobi/qcusbnet.c b/drivers/net/usb/gobi/qcusbnet.c
new file mode 100644
index 0000000..7e218a8
--- /dev/null
+++ b/drivers/net/usb/gobi/qcusbnet.c
@@ -0,0 +1,649 @@
+/* qcusbnet.c - gobi network device
+ * 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 and
+ * only version 2 as published by the Free Software Foundation.
+
+ * 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 "structs.h"
+#include "qmidevice.h"
+#include "qmi.h"
+#include "qcusbnet.h"
+
+#define DRIVER_VERSION "1.0.110"
+#define DRIVER_AUTHOR "Qualcomm Innovation Center"
+#define DRIVER_DESC "QCUSBNet2k"
+
+int qcusbnet_debug;
+static struct class *devclass;
+
+int qc_suspend(struct usb_interface *iface, pm_message_t event)
+{
+ struct usbnet *usbnet;
+ struct qcusbnet *dev;
+
+ if (!iface)
+ return -ENOMEM;
+
+ usbnet = usb_get_intfdata(iface);
+
+ if (!usbnet || !usbnet->net) {
+ DBG("failed to get netdevice\n");
+ return -ENXIO;
+ }
+
+ dev = (struct qcusbnet *)usbnet->data[0];
+ if (!dev) {
+ DBG("failed to get QMIDevice\n");
+ return -ENXIO;
+ }
+
+ if (!(event.event & PM_EVENT_AUTO)) {
+ DBG("device suspended to power level %d\n",
+ event.event);
+ qc_setdown(dev, DOWN_DRIVER_SUSPENDED);
+ } else {
+ DBG("device autosuspend\n");
+ }
+
+ if (event.event & PM_EVENT_SUSPEND) {
+ qc_stopread(dev);
+ usbnet->udev->reset_resume = 0;
+ iface->dev.power.power_state.event = event.event;
+ } else {
+ usbnet->udev->reset_resume = 1;
+ }
+
+ return usbnet_suspend(iface, event);
+}
+
+static int qc_resume(struct usb_interface *iface)
+{
+ struct usbnet *usbnet;
+ struct qcusbnet *dev;
+ int ret;
+ int oldstate;
+
+ if (iface == 0)
+ return -ENOMEM;
+
+ usbnet = usb_get_intfdata(iface);
+
+ if (!usbnet || !usbnet->net) {
+ DBG("failed to get netdevice\n");
+ return -ENXIO;
+ }
+
+ dev = (struct qcusbnet *)usbnet->data[0];
+ if (!dev) {
+ DBG("failed to get QMIDevice\n");
+ return -ENXIO;
+ }
+
+ oldstate = iface->dev.power.power_state.event;
+ iface->dev.power.power_state.event = PM_EVENT_ON;
+ DBG("resuming from power mode %d\n", oldstate);
+
+ if (oldstate & PM_EVENT_SUSPEND) {
+ qc_cleardown(dev, DOWN_DRIVER_SUSPENDED);
+
+ ret = usbnet_resume(iface);
+ if (ret) {
+ DBG("usbnet_resume error %d\n", ret);
+ return ret;
+ }
+
+ ret = qc_startread(dev);
+ if (ret) {
+ DBG("qc_startread error %d\n", ret);
+ return ret;
+ }
+
+ complete(&dev->worker.work);
+ } else {
+ DBG("nothing to resume\n");
+ return 0;
+ }
+
+ return ret;
+}
+
+static int qcnet_bind(struct usbnet *usbnet, struct usb_interface *iface)
+{
+ int numends;
+ int i;
+ struct usb_host_endpoint *endpoint = NULL;
+ struct usb_host_endpoint *in = NULL;
+ struct usb_host_endpoint *out = NULL;
+
+ if (iface->num_altsetting != 1) {
+ DBG("invalid num_altsetting %u\n", iface->num_altsetting);
+ return -EINVAL;
+ }
+
+ if (iface->cur_altsetting->desc.bInterfaceNumber != 0) {
+ DBG("invalid interface %d\n",
+ iface->cur_altsetting->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ numends = iface->cur_altsetting->desc.bNumEndpoints;
+ for (i = 0; i < numends; i++) {
+ endpoint = iface->cur_altsetting->endpoint + i;
+ if (!endpoint) {
+ DBG("invalid endpoint %u\n", i);
+ return -EINVAL;
+ }
+
+ if (usb_endpoint_dir_in(&endpoint->desc)
+ && !usb_endpoint_xfer_int(&endpoint->desc)) {
+ in = endpoint;
+ } else if (!usb_endpoint_dir_out(&endpoint->desc)) {
+ out = endpoint;
+ }
+ }
+
+ if (!in || !out) {
+ DBG("invalid endpoints\n");
+ return -EINVAL;
+ }
+
+ if (usb_set_interface(usbnet->udev,
+ iface->cur_altsetting->desc.bInterfaceNumber, 0)) {
+ DBG("unable to set interface\n");
+ return -EINVAL;
+ }
+
+ usbnet->in = usb_rcvbulkpipe(usbnet->udev, in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+ usbnet->out = usb_sndbulkpipe(usbnet->udev, out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+
+ DBG("in %x, out %x\n",
+ in->desc.bEndpointAddress,
+ out->desc.bEndpointAddress);
+
+ return 0;
+}
+
+static void qcnet_unbind(struct usbnet *usbnet, struct usb_interface *iface)
+{
+ struct qcusbnet *dev = (struct qcusbnet *)usbnet->data[0];
+
+ netif_carrier_off(usbnet->net);
+ qc_deregister(dev);
+
+ kfree(usbnet->net->netdev_ops);
+ usbnet->net->netdev_ops = NULL;
+
+ kfree(dev);
+}
+
+static void qcnet_urbhook(struct urb *urb)
+{
+ unsigned long flags;
+ struct worker *worker = urb->context;
+ if (!worker) {
+ DBG("bad context\n");
+ return;
+ }
+
+ if (urb->status) {
+ DBG("urb finished with error %d\n", urb->status);
+ }
+
+ spin_lock_irqsave(&worker->active_lock, flags);
+ worker->active = ERR_PTR(-EAGAIN);
+ spin_unlock_irqrestore(&worker->active_lock, flags);
+ /* XXX-fix race against qcnet_stop()? */
+ complete(&worker->work);
+ usb_free_urb(urb);
+}
+
+static void qcnet_txtimeout(struct net_device *netdev)
+{
+ struct list_head *node, *tmp;
+ struct qcusbnet *dev;
+ struct worker *worker;
+ struct urbreq *req;
+ unsigned long activeflags, listflags;
+ struct usbnet *usbnet = netdev_priv(netdev);
+
+ if (!usbnet || !usbnet->net) {
+ DBG("failed to get usbnet device\n");
+ return;
+ }
+
+ dev = (struct qcusbnet *)usbnet->data[0];
+ if (!dev) {
+ DBG("failed to get QMIDevice\n");
+ return;
+ }
+ worker = &dev->worker;
+
+ DBG("\n");
+
+ spin_lock_irqsave(&worker->active_lock, activeflags);
+ if (worker->active)
+ usb_kill_urb(worker->active);
+ spin_unlock_irqrestore(&worker->active_lock, activeflags);
+
+ spin_lock_irqsave(&worker->urbs_lock, listflags);
+ list_for_each_safe(node, tmp, &worker->urbs) {
+ req = list_entry(node, struct urbreq, node);
+ usb_free_urb(req->urb);
+ list_del(&req->node);
+ kfree(req);
+ }
+ spin_unlock_irqrestore(&worker->urbs_lock, listflags);
+
+ complete(&worker->work);
+}
+
+static int qcnet_worker(void *arg)
+{
+ struct list_head *node, *tmp;
+ unsigned long activeflags, listflags;
+ struct urbreq *req;
+ int status;
+ struct usb_device *usbdev;
+ struct worker *worker = arg;
+ if (!worker) {
+ DBG("passed null pointer\n");
+ return -EINVAL;
+ }
+
+ usbdev = interface_to_usbdev(worker->iface);
+
+ DBG("traffic thread started\n");
+
+ while (!kthread_should_stop()) {
+ wait_for_completion_interruptible(&worker->work);
+
+ if (kthread_should_stop()) {
+ spin_lock_irqsave(&worker->active_lock, activeflags);
+ if (worker->active) {
+ usb_kill_urb(worker->active);
+ }
+ spin_unlock_irqrestore(&worker->active_lock, activeflags);
+
+ spin_lock_irqsave(&worker->urbs_lock, listflags);
+ list_for_each_safe(node, tmp, &worker->urbs) {
+ req = list_entry(node, struct urbreq, node);
+ usb_free_urb(req->urb);
+ list_del(&req->node);
+ kfree(req);
+ }
+ spin_unlock_irqrestore(&worker->urbs_lock, listflags);
+
+ break;
+ }
+
+ spin_lock_irqsave(&worker->active_lock, activeflags);
+ if (IS_ERR(worker->active) && PTR_ERR(worker->active) == -EAGAIN) {
+ worker->active = NULL;
+ spin_unlock_irqrestore(&worker->active_lock, activeflags);
+ usb_autopm_put_interface(worker->iface);
+ spin_lock_irqsave(&worker->active_lock, activeflags);
+ }
+
+ if (worker->active) {
+ spin_unlock_irqrestore(&worker->active_lock, activeflags);
+ continue;
+ }
+
+ spin_lock_irqsave(&worker->urbs_lock, listflags);
+ if (list_empty(&worker->urbs)) {
+ spin_unlock_irqrestore(&worker->urbs_lock, listflags);
+ spin_unlock_irqrestore(&worker->active_lock, activeflags);
+ continue;
+ }
+
+ req = list_first_entry(&worker->urbs, struct urbreq, node);
+ list_del(&req->node);
+ spin_unlock_irqrestore(&worker->urbs_lock, listflags);
+
+ worker->active = req->urb;
+ spin_unlock_irqrestore(&worker->active_lock, activeflags);
+
+ status = usb_autopm_get_interface(worker->iface);
+ if (status < 0) {
+ DBG("unable to autoresume interface: %d\n", status);
+ if (status == -EPERM) {
+ qc_suspend(worker->iface, PMSG_SUSPEND);
+ }
+
+ spin_lock_irqsave(&worker->urbs_lock, listflags);
+ list_add(&req->node, &worker->urbs);
+ spin_unlock_irqrestore(&worker->urbs_lock, listflags);
+
+ spin_lock_irqsave(&worker->active_lock, activeflags);
+ worker->active = NULL;
+ spin_unlock_irqrestore(&worker->active_lock, activeflags);
+
+ continue;
+ }
+
+ status = usb_submit_urb(worker->active, GFP_KERNEL);
+ if (status < 0) {
+ DBG("Failed to submit URB: %d. Packet dropped\n", status);
+ spin_lock_irqsave(&worker->active_lock, activeflags);
+ usb_free_urb(worker->active);
+ worker->active = NULL;
+ spin_unlock_irqrestore(&worker->active_lock, activeflags);
+ usb_autopm_put_interface(worker->iface);
+ complete(&worker->work);
+ }
+
+ kfree(req);
+ }
+
+ DBG("traffic thread exiting\n");
+ worker->thread = NULL;
+ return 0;
+}
+
+static int qcnet_startxmit(struct sk_buff *skb, struct net_device *netdev)
+{
+ unsigned long listflags;
+ struct qcusbnet *dev;
+ struct worker *worker;
+ struct urbreq *req;
+ void *data;
+ struct usbnet *usbnet = netdev_priv(netdev);
+
+ DBG("\n");
+
+ if (!usbnet || !usbnet->net) {
+ DBG("failed to get usbnet device\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ dev = (struct qcusbnet *)usbnet->data[0];
+ if (!dev) {
+ DBG("failed to get QMIDevice\n");
+ return NETDEV_TX_BUSY;
+ }
+ worker = &dev->worker;
+
+ if (qc_isdown(dev, DOWN_DRIVER_SUSPENDED)) {
+ DBG("device is suspended\n");
+ dump_stack();
+ return NETDEV_TX_BUSY;
+ }
+
+ req = kmalloc(sizeof(*req), GFP_ATOMIC);
+ if (!req) {
+ DBG("unable to allocate URBList memory\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ req->urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+ if (!req->urb) {
+ kfree(req);
+ DBG("unable to allocate URB\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ data = kmalloc(skb->len, GFP_ATOMIC);
+ if (!data) {
+ usb_free_urb(req->urb);
+ kfree(req);
+ DBG("unable to allocate URB data\n");
+ return NETDEV_TX_BUSY;
+ }
+ memcpy(data, skb->data, skb->len);
+
+ usb_fill_bulk_urb(req->urb, dev->usbnet->udev, dev->usbnet->out,
+ data, skb->len, qcnet_urbhook, worker);
+
+ spin_lock_irqsave(&worker->urbs_lock, listflags);
+ list_add_tail(&req->node, &worker->urbs);
+ spin_unlock_irqrestore(&worker->urbs_lock, listflags);
+
+ complete(&worker->work);
+
+ netdev->trans_start = jiffies;
+ dev_kfree_skb_any(skb);
+
+ return NETDEV_TX_OK;
+}
+
+static int qcnet_open(struct net_device *netdev)
+{
+ int status = 0;
+ struct qcusbnet *dev;
+ struct usbnet *usbnet = netdev_priv(netdev);
+
+ if (!usbnet) {
+ DBG("failed to get usbnet device\n");
+ return -ENXIO;
+ }
+
+ dev = (struct qcusbnet *)usbnet->data[0];
+ if (!dev) {
+ DBG("failed to get QMIDevice\n");
+ return -ENXIO;
+ }
+
+ DBG("\n");
+
+ dev->worker.iface = dev->iface;
+ INIT_LIST_HEAD(&dev->worker.urbs);
+ dev->worker.active = NULL;
+ spin_lock_init(&dev->worker.urbs_lock);
+ spin_lock_init(&dev->worker.active_lock);
+ init_completion(&dev->worker.work);
+
+ dev->worker.thread = kthread_run(qcnet_worker, &dev->worker, "qcnet_worker");
+ if (IS_ERR(dev->worker.thread)) {
+ DBG("AutoPM thread creation error\n");
+ return PTR_ERR(dev->worker.thread);
+ }
+
+ qc_cleardown(dev, DOWN_NET_IFACE_STOPPED);
+ if (dev->open) {
+ status = dev->open(netdev);
+ if (status == 0) {
+ usb_autopm_put_interface(dev->iface);
+ }
+ } else {
+ DBG("no USBNetOpen defined\n");
+ }
+
+ return status;
+}
+
+int qcnet_stop(struct net_device *netdev)
+{
+ struct qcusbnet *dev;
+ struct usbnet *usbnet = netdev_priv(netdev);
+
+ if (!usbnet || !usbnet->net) {
+ DBG("failed to get netdevice\n");
+ return -ENXIO;
+ }
+
+ dev = (struct qcusbnet *)usbnet->data[0];
+ if (!dev) {
+ DBG("failed to get QMIDevice\n");
+ return -ENXIO;
+ }
+
+ qc_setdown(dev, DOWN_NET_IFACE_STOPPED);
+ complete(&dev->worker.work);
+ kthread_stop(dev->worker.thread);
+ DBG("thread stopped\n");
+
+ if (dev->stop != NULL)
+ return dev->stop(netdev);
+ return 0;
+}
+
+static const struct driver_info qc_netinfo = {
+ .description = "QCUSBNet Ethernet Device",
+ .flags = FLAG_ETHER,
+ .bind = qcnet_bind,
+ .unbind = qcnet_unbind,
+ .data = 0,
+};
+
+#define MKVIDPID(v, p) \
+{ \
+ USB_DEVICE(v, p), \
+ .driver_info = (unsigned long)&qc_netinfo, \
+}
+
+static const struct usb_device_id qc_vidpids[] = {
+ MKVIDPID(0x05c6, 0x9215), /* Acer Gobi 2000 */
+ MKVIDPID(0x05c6, 0x9265), /* Asus Gobi 2000 */
+ MKVIDPID(0x16d8, 0x8002), /* CMOTech Gobi 2000 */
+ MKVIDPID(0x413c, 0x8186), /* Dell Gobi 2000 */
+ MKVIDPID(0x1410, 0xa010), /* Entourage Gobi 2000 */
+ MKVIDPID(0x1410, 0xa011), /* Entourage Gobi 2000 */
+ MKVIDPID(0x1410, 0xa012), /* Entourage Gobi 2000 */
+ MKVIDPID(0x1410, 0xa013), /* Entourage Gobi 2000 */
+ MKVIDPID(0x03f0, 0x251d), /* HP Gobi 2000 */
+ MKVIDPID(0x05c6, 0x9205), /* Lenovo Gobi 2000 */
+ MKVIDPID(0x05c6, 0x920b), /* Generic Gobi 2000 */
+ MKVIDPID(0x04da, 0x250f), /* Panasonic Gobi 2000 */
+ MKVIDPID(0x05c6, 0x9245), /* Samsung Gobi 2000 */
+ MKVIDPID(0x1199, 0x9001), /* Sierra Wireless Gobi 2000 */
+ MKVIDPID(0x1199, 0x9002), /* Sierra Wireless Gobi 2000 */
+ MKVIDPID(0x1199, 0x9003), /* Sierra Wireless Gobi 2000 */
+ MKVIDPID(0x1199, 0x9004), /* Sierra Wireless Gobi 2000 */
+ MKVIDPID(0x1199, 0x9005), /* Sierra Wireless Gobi 2000 */
+ MKVIDPID(0x1199, 0x9006), /* Sierra Wireless Gobi 2000 */
+ MKVIDPID(0x1199, 0x9007), /* Sierra Wireless Gobi 2000 */
+ MKVIDPID(0x1199, 0x9008), /* Sierra Wireless Gobi 2000 */
+ MKVIDPID(0x1199, 0x9009), /* Sierra Wireless Gobi 2000 */
+ MKVIDPID(0x1199, 0x900a), /* Sierra Wireless Gobi 2000 */
+ MKVIDPID(0x05c6, 0x9225), /* Sony Gobi 2000 */
+ MKVIDPID(0x05c6, 0x9235), /* Top Global Gobi 2000 */
+ MKVIDPID(0x05c6, 0x9275), /* iRex Technologies Gobi 2000 */
+ { }
+};
+
+MODULE_DEVICE_TABLE(usb, qc_vidpids);
+
+int qcnet_probe(struct usb_interface *iface, const struct usb_device_id *vidpids)
+{
+ int status;
+ struct usbnet *usbnet;
+ struct qcusbnet *dev;
+ struct net_device_ops *netdevops;
+
+ status = usbnet_probe(iface, vidpids);
+ if (status < 0) {
+ DBG("usbnet_probe failed %d\n", status);
+ return status;
+ }
+
+ usbnet = usb_get_intfdata(iface);
+
+ if (!usbnet || !usbnet->net) {
+ DBG("failed to get netdevice\n");
+ return -ENXIO;
+ }
+
+ dev = kmalloc(sizeof(struct qcusbnet), GFP_KERNEL);
+ if (!dev) {
+ DBG("failed to allocate device buffers\n");
+ return -ENOMEM;
+ }
+
+ usbnet->data[0] = (unsigned long)dev;
+
+ dev->usbnet = usbnet;
+
+ netdevops = kmalloc(sizeof(struct net_device_ops), GFP_KERNEL);
+ if (!netdevops) {
+ DBG("failed to allocate net device ops\n");
+ return -ENOMEM;
+ }
+ memcpy(netdevops, usbnet->net->netdev_ops, sizeof(struct net_device_ops));
+
+ dev->open = netdevops->ndo_open;
+ netdevops->ndo_open = qcnet_open;
+ dev->stop = netdevops->ndo_stop;
+ netdevops->ndo_stop = qcnet_stop;
+ netdevops->ndo_start_xmit = qcnet_startxmit;
+ netdevops->ndo_tx_timeout = qcnet_txtimeout;
+
+ usbnet->net->netdev_ops = netdevops;
+
+ memset(&(dev->usbnet->net->stats), 0, sizeof(struct net_device_stats));
+
+ dev->iface = iface;
+ memset(&(dev->meid), '0', 14);
+
+ DBG("Mac Address: %pM\n", dev->usbnet->net->dev_addr);
+
+ dev->valid = false;
+ memset(&dev->qmi, 0, sizeof(struct qmidev));
+
+ dev->qmi.devclass = devclass;
+
+ INIT_LIST_HEAD(&dev->qmi.clients);
+ init_completion(&dev->worker.work);
+ spin_lock_init(&dev->qmi.clients_lock);
+
+ dev->down = 0;
+ qc_setdown(dev, DOWN_NO_NDIS_CONNECTION);
+ qc_setdown(dev, DOWN_NET_IFACE_STOPPED);
+
+ status = qc_register(dev);
+ if (status) {
+ qc_deregister(dev);
+ }
+
+ return status;
+}
+EXPORT_SYMBOL_GPL(qcnet_probe);
+
+static struct usb_driver qcusbnet = {
+ .name = "QCUSBNet2k",
+ .id_table = qc_vidpids,
+ .probe = qcnet_probe,
+ .disconnect = usbnet_disconnect,
+ .suspend = qc_suspend,
+ .resume = qc_resume,
+ .supports_autosuspend = true,
+};
+
+static int __init modinit(void)
+{
+ devclass = class_create(THIS_MODULE, "QCQMI");
+ if (IS_ERR(devclass)) {
+ DBG("error at class_create %ld\n", PTR_ERR(devclass));
+ return -ENOMEM;
+ }
+ printk(KERN_INFO "%s: %s\n", DRIVER_DESC, DRIVER_VERSION);
+ return usb_register(&qcusbnet);
+}
+module_init(modinit);
+
+static void __exit modexit(void)
+{
+ usb_deregister(&qcusbnet);
+ class_destroy(devclass);
+}
+module_exit(modexit);
+
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("Dual BSD/GPL");
+
+module_param(qcusbnet_debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(qcusbnet_debug, "Debugging enabled or not");
diff --git a/drivers/net/usb/gobi/qcusbnet.h b/drivers/net/usb/gobi/qcusbnet.h
new file mode 100644
index 0000000..2f20868
--- /dev/null
+++ b/drivers/net/usb/gobi/qcusbnet.h
@@ -0,0 +1,24 @@
+/* qcusbnet.h - gobi network device header
+ * 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 and
+ * only version 2 as published by the Free Software Foundation.
+
+ * 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.
+ */
+
+#ifndef QCUSBNET_QCUSBNET_H
+#define QCUSBNET_QCUSBNET_H
+
+extern int qc_suspend(struct usb_interface *iface, pm_message_t event);
+
+#endif /* !QCUSBNET_QCUSBNET_H */
diff --git a/drivers/net/usb/gobi/qmi.c b/drivers/net/usb/gobi/qmi.c
new file mode 100755
index 0000000..cdbdbaf
--- /dev/null
+++ b/drivers/net/usb/gobi/qmi.c
@@ -0,0 +1,358 @@
+/* qmi.c - QMI protocol implementation
+ * 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 and
+ * only version 2 as published by the Free Software Foundation.
+
+ * 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 "qmi.h"
+
+#include <linux/slab.h>
+
+struct qmux {
+ u8 tf; /* always 1 */
+ u16 len;
+ u8 ctrl;
+ u8 service;
+ u8 qmicid;
+} __attribute__((__packed__));
+
+struct getcid_req {
+ struct qmux header;
+ u8 req;
+ u8 tid;
+ u16 msgid;
+ u16 tlvsize;
+ u8 service;
+ u16 size;
+ u8 qmisvc;
+} __attribute__((__packed__));
+
+struct releasecid_req {
+ struct qmux header;
+ u8 req;
+ u8 tid;
+ u16 msgid;
+ u16 tlvsize;
+ u8 rlscid;
+ u16 size;
+ u16 cid;
+} __attribute__((__packed__));
+
+struct ready_req {
+ struct qmux header;
+ u8 req;
+ u8 tid;
+ u16 msgid;
+ u16 tlvsize;
+} __attribute__((__packed__));
+
+struct seteventreport_req {
+ struct qmux header;
+ u8 req;
+ u16 tid;
+ u16 msgid;
+ u16 tlvsize;
+ u8 reportchanrate;
+ u16 size;
+ u8 period;
+ u32 mask;
+} __attribute__((__packed__));
+
+struct getpkgsrvcstatus_req {
+ struct qmux header;
+ u8 req;
+ u16 tid;
+ u16 msgid;
+ u16 tlvsize;
+} __attribute__((__packed__));
+
+struct getmeid_req {
+ struct qmux header;
+ u8 req;
+ u16 tid;
+ u16 msgid;
+ u16 tlvsize;
+} __attribute__((__packed__));
+
+const size_t qmux_size = sizeof(struct qmux);
+
+void *qmictl_new_getcid(u8 tid, u8 svctype, size_t *size)
+{
+ struct getcid_req *req = kmalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return NULL;
+ req->req = 0x00;
+ req->tid = tid;
+ req->msgid = 0x0022;
+ req->tlvsize = 0x0004;
+ req->service = 0x01;
+ req->size = 0x0001;
+ req->qmisvc = svctype;
+ *size = sizeof(*req);
+ return req;
+}
+
+void *qmictl_new_releasecid(u8 tid, u16 cid, size_t *size)
+{
+ struct releasecid_req *req = kmalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return NULL;
+ req->req = 0x00;
+ req->tid = tid;
+ req->msgid = 0x0023;
+ req->tlvsize = 0x05;
+ req->rlscid = 0x01;
+ req->size = 0x0002;
+ req->cid = cid;
+ *size = sizeof(*req);
+ return req;
+}
+
+void *qmictl_new_ready(u8 tid, size_t *size)
+{
+ struct ready_req *req = kmalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return NULL;
+ req->req = 0x00;
+ req->tid = tid;
+ req->msgid = 0x21;
+ req->tlvsize = 0;
+ *size = sizeof(*req);
+ return req;
+}
+
+void *qmiwds_new_seteventreport(u8 tid, size_t *size)
+{
+ struct seteventreport_req *req = kmalloc(sizeof(*req), GFP_KERNEL);
+ req->req = 0x00;
+ req->tid = tid;
+ req->msgid = 0x0001;
+ req->tlvsize = 0x0008;
+ req->reportchanrate = 0x11;
+ req->size = 0x0005;
+ req->period = 0x01;
+ req->mask = 0x000000ff;
+ *size = sizeof(*req);
+ return req;
+}
+
+void *qmiwds_new_getpkgsrvcstatus(u8 tid, size_t *size)
+{
+ struct getpkgsrvcstatus_req *req = kmalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return NULL;
+ req->req = 0x00;
+ req->tid = tid;
+ req->msgid = 0x22;
+ req->tlvsize = 0x0000;
+ *size = sizeof(*req);
+ return req;
+}
+
+void *qmidms_new_getmeid(u8 tid, size_t *size)
+{
+ struct getmeid_req *req = kmalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return NULL;
+ req->req = 0x00;
+ req->tid = tid;
+ req->msgid = 0x25;
+ req->tlvsize = 0x0000;
+ *size = sizeof(*req);
+ return req;
+}
+
+int qmux_parse(u16 *cid, void *buf, size_t size)
+{
+ struct qmux *qmux = buf;
+
+ if (!buf || size < 12)
+ return -ENOMEM;
+
+ if (qmux->tf != 1 || qmux->len != size - 1 || qmux->ctrl != 0x80)
+ return -EINVAL;
+
+ *cid = (qmux->qmicid << 8) + qmux->service;
+ return sizeof(*qmux);
+}
+
+int qmux_fill(u16 cid, void *buf, size_t size)
+{
+ struct qmux *qmux = buf;
+
+ if (!buf || size < sizeof(*qmux))
+ return -ENOMEM;
+
+ qmux->tf = 1;
+ qmux->len = size - 1;
+ qmux->ctrl = 0;
+ qmux->service = cid & 0xff;
+ qmux->qmicid = cid >> 8;
+ return 0;
+}
+
+static u16 tlv_get(void *msg, u16 msgsize, u8 type, void *buf, u16 bufsize)
+{
+ u16 pos;
+ u16 msize = 0;
+
+ if (!msg || !buf)
+ return -ENOMEM;
+
+ for (pos = 4; pos + 3 < msgsize; pos += msize + 3) {
+ msize = *(u16 *)(msg + pos + 1);
+ if (*(u8 *)(msg + pos) == type) {
+ if (bufsize < msize)
+ return -ENOMEM;
+
+ memcpy(buf, msg + pos + 3, msize);
+ return msize;
+ }
+ }
+
+ return -ENOMSG;
+}
+
+int qmi_msgisvalid(void *msg, u16 size)
+{
+ char tlv[4];
+
+ if (tlv_get(msg, size, 2, &tlv[0], 4) == 4) {
+ if (*(u16 *)&tlv[0] != 0)
+ return *(u16 *)&tlv[2];
+ else
+ return 0;
+ }
+ return -ENOMSG;
+}
+
+int qmi_msgid(void *msg, u16 size)
+{
+ return size < 2 ? -ENODATA : *(u16 *)msg;
+}
+
+int qmictl_alloccid_resp(void *buf, u16 size, u16 *cid)
+{
+ int result;
+ u8 offset = sizeof(struct qmux) + 2;
+
+ if (!buf || size < offset)
+ return -ENOMEM;
+
+ buf = buf + offset;
+ size -= offset;
+
+ result = qmi_msgid(buf, size);
+ if (result != 0x22)
+ return -EFAULT;
+
+ result = qmi_msgisvalid(buf, size);
+ if (result != 0)
+ return -EFAULT;
+
+ result = tlv_get(buf, size, 0x01, cid, 2);
+ if (result != 2)
+ return -EFAULT;
+
+ return 0;
+}
+
+int qmictl_freecid_resp(void *buf, u16 size)
+{
+ int result;
+ u8 offset = sizeof(struct qmux) + 2;
+
+ if (!buf || size < offset)
+ return -ENOMEM;
+
+ buf = buf + offset;
+ size -= offset;
+
+ result = qmi_msgid(buf, size);
+ if (result != 0x23)
+ return -EFAULT;
+
+ result = qmi_msgisvalid(buf, size);
+ if (result != 0)
+ return -EFAULT;
+
+ return 0;
+}
+
+int qmiwds_event_resp(void *buf, u16 size, struct qmiwds_stats *stats)
+{
+ int result;
+ u8 status[2];
+
+ u8 offset = sizeof(struct qmux) + 3;
+
+ if (!buf || size < offset || !stats)
+ return -ENOMEM;
+
+ buf = buf + offset;
+ size -= offset;
+
+ result = qmi_msgid(buf, size);
+ if (result == 0x01) {
+ tlv_get(buf, size, 0x10, &stats->txok, 4);
+ tlv_get(buf, size, 0x11, &stats->rxok, 4);
+ tlv_get(buf, size, 0x12, &stats->txerr, 4);
+ tlv_get(buf, size, 0x13, &stats->rxerr, 4);
+ tlv_get(buf, size, 0x14, &stats->txofl, 4);
+ tlv_get(buf, size, 0x15, &stats->rxofl, 4);
+ tlv_get(buf, size, 0x19, &stats->txbytesok, 8);
+ tlv_get(buf, size, 0x1A, &stats->rxbytesok, 8);
+ } else if (result == 0x22) {
+ result = tlv_get(buf, size, 0x01, &status[0], 2);
+ if (result >= 1)
+ stats->linkstate = status[0] == 0x02;
+ if (result == 2)
+ stats->reconfigure = status[1] == 0x01;
+
+ if (result < 0)
+ return result;
+ } else {
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+int qmidms_meid_resp(void *buf, u16 size, char *meid, int meidsize)
+{
+ int result;
+
+ u8 offset = sizeof(struct qmux) + 3;
+
+ if (!buf || size < offset || meidsize < 14)
+ return -ENOMEM;
+
+ buf = buf + offset;
+ size -= offset;
+
+ result = qmi_msgid(buf, size);
+ if (result != 0x25)
+ return -EFAULT;
+
+ result = qmi_msgisvalid(buf, size);
+ if (result)
+ return -EFAULT;
+
+ result = tlv_get(buf, size, 0x12, meid, 14);
+ if (result != 14)
+ return -EFAULT;
+
+ return 0;
+}
diff --git a/drivers/net/usb/gobi/qmi.h b/drivers/net/usb/gobi/qmi.h
new file mode 100755
index 0000000..7954790
--- /dev/null
+++ b/drivers/net/usb/gobi/qmi.h
@@ -0,0 +1,67 @@
+/* qmi.h - QMI protocol header
+ * 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 and
+ * only version 2 as published by the Free Software Foundation.
+
+ * 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.
+ */
+
+#ifndef QCUSBNET_QMI_H
+#define QCUSBNET_QMI_H
+
+#include <linux/types.h>
+
+#define QMICTL 0
+#define QMIWDS 1
+#define QMIDMS 2
+
+#define true 1
+#define false 0
+
+#define ENOMEM 12
+#define EFAULT 14
+#define EINVAL 22
+#define ENOMSG 42
+#define ENODATA 61
+
+int qmux_parse(u16 *cid, void *buf, size_t size);
+int qmux_fill(u16 cid, void *buf, size_t size);
+
+extern const size_t qmux_size;
+
+void *qmictl_new_getcid(u8 tid, u8 svctype, size_t *size);
+void *qmictl_new_releasecid(u8 tid, u16 cid, size_t *size);
+void *qmictl_new_ready(u8 tid, size_t *size);
+void *qmiwds_new_seteventreport(u8 tid, size_t *size);
+void *qmiwds_new_getpkgsrvcstatus(u8 tid, size_t *size);
+void *qmidms_new_getmeid(u8 tid, size_t *size);
+
+struct qmiwds_stats {
+ u32 txok;
+ u32 rxok;
+ u32 txerr;
+ u32 rxerr;
+ u32 txofl;
+ u32 rxofl;
+ u64 txbytesok;
+ u64 rxbytesok;
+ bool linkstate;
+ bool reconfigure;
+};
+
+int qmictl_alloccid_resp(void *buf, u16 size, u16 *cid);
+int qmictl_freecid_resp(void *buf, u16 size);
+int qmiwds_event_resp(void *buf, u16 size, struct qmiwds_stats *stats);
+int qmidms_meid_resp(void *buf, u16 size, char *meid, int meidsize);
+
+#endif /* !QCUSBNET_QMI_H */
diff --git a/drivers/net/usb/gobi/qmidevice.c b/drivers/net/usb/gobi/qmidevice.c
new file mode 100755
index 0000000..d1cb341
--- /dev/null
+++ b/drivers/net/usb/gobi/qmidevice.c
@@ -0,0 +1,1590 @@
+/* qmidevice.c - gobi QMI device
+ * 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 and
+ * only version 2 as published by the Free Software Foundation.
+
+ * 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 "qmidevice.h"
+#include "qcusbnet.h"
+
+struct readreq {
+ struct list_head node;
+ void *data;
+ u16 tid;
+ u16 size;
+};
+
+struct notifyreq {
+ struct list_head node;
+ void (*func)(struct qcusbnet *, u16, void *);
+ u16 tid;
+ void *data;
+};
+
+struct client {
+ struct list_head node;
+ u16 cid;
+ struct list_head reads;
+ struct list_head notifies;
+ struct list_head urbs;
+};
+
+struct urbsetup {
+ u8 type;
+ u8 code;
+ u16 value;
+ u16 index;
+ u16 len;
+};
+
+struct qmihandle {
+ u16 cid;
+ struct qcusbnet *dev;
+};
+
+extern int qcusbnet_debug;
+static int qcusbnet2k_fwdelay;
+
+static bool device_valid(struct qcusbnet *dev);
+static struct client *client_bycid(struct qcusbnet *dev, u16 cid);
+static bool client_addread(struct qcusbnet *dev, u16 cid, u16 tid, void *data, u16 size);
+static bool client_delread(struct qcusbnet *dev, u16 cid, u16 tid, void **data, u16 *size);
+static bool client_addnotify(struct qcusbnet *dev, u16 cid, u16 tid,
+ void (*hook)(struct qcusbnet *, u16 cid, void *),
+ void *data);
+static bool client_notify(struct qcusbnet *dev, u16 cid, u16 tid);
+static bool client_addurb(struct qcusbnet *dev, u16 cid, struct urb *urb);
+static struct urb *client_delurb(struct qcusbnet *dev, u16 cid);
+
+static int devqmi_open(struct inode *inode, struct file *file);
+static long devqmi_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+static int devqmi_close(struct file *file, fl_owner_t ftable);
+static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size,
+ loff_t *pos);
+static ssize_t devqmi_write(struct file *file, const char __user *buf,
+ size_t size, loff_t *pos);
+
+static bool qmi_ready(struct qcusbnet *dev, u16 timeout);
+static void wds_callback(struct qcusbnet *dev, u16 cid, void *data);
+static int setup_wds_callback(struct qcusbnet *dev);
+static int qmidms_getmeid(struct qcusbnet *dev);
+
+#define IOCTL_QMI_GET_SERVICE_FILE (0x8BE0 + 1)
+#define IOCTL_QMI_GET_DEVICE_VIDPID (0x8BE0 + 2)
+#define IOCTL_QMI_GET_DEVICE_MEID (0x8BE0 + 3)
+#define CDC_GET_ENCAPSULATED_RESPONSE 0x01A1ll
+#define CDC_CONNECTION_SPEED_CHANGE 0x08000000002AA1ll
+
+static const struct file_operations devqmi_fops = {
+ .owner = THIS_MODULE,
+ .read = devqmi_read,
+ .write = devqmi_write,
+ .unlocked_ioctl = devqmi_ioctl,
+ .open = devqmi_open,
+ .flush = devqmi_close,
+};
+
+#ifdef CONFIG_SMP
+static inline void assert_locked(struct qcusbnet *dev)
+{
+ BUG_ON(!spin_is_locked(&dev->qmi.clients_lock));
+}
+#else
+static inline void assert_locked(struct qcusbnet *dev)
+{
+
+}
+#endif
+
+static bool device_valid(struct qcusbnet *dev)
+{
+ return dev && dev->valid;
+}
+
+void qc_setdown(struct qcusbnet *dev, u8 reason)
+{
+ set_bit(reason, &dev->down);
+ netif_carrier_off(dev->usbnet->net);
+}
+
+void qc_cleardown(struct qcusbnet *dev, u8 reason)
+{
+ clear_bit(reason, &dev->down);
+ if (!dev->down)
+ netif_carrier_on(dev->usbnet->net);
+}
+
+bool qc_isdown(struct qcusbnet *dev, u8 reason)
+{
+ return test_bit(reason, &dev->down);
+}
+
+static void read_callback(struct urb *urb)
+{
+ struct list_head *node;
+ int result;
+ u16 cid;
+ struct client *client;
+ void *data;
+ void *copy;
+ u16 size;
+ struct qcusbnet *dev;
+ unsigned long flags;
+ u16 tid;
+
+ if (!urb) {
+ DBG("bad read URB\n");
+ return;
+ }
+
+ dev = urb->context;
+ if (!device_valid(dev)) {
+ DBG("Invalid device!\n");
+ return;
+ }
+
+ if (urb->status) {
+ DBG("Read status = %d\n", urb->status);
+ return;
+ }
+
+ DBG("Read %d bytes\n", urb->actual_length);
+
+ data = urb->transfer_buffer;
+ size = urb->actual_length;
+
+ print_hex_dump(KERN_INFO, "QCUSBNet2k: ", DUMP_PREFIX_OFFSET,
+ 16, 1, data, size, true);
+
+ result = qmux_parse(&cid, data, size);
+ if (result < 0) {
+ DBG("Read error parsing QMUX %d\n", result);
+ return;
+ }
+
+ if (size < result + 3) {
+ DBG("Data buffer too small to parse\n");
+ return;
+ }
+
+ if (cid == QMICTL)
+ tid = *(u8 *)(data + result + 1);
+ else
+ tid = *(u16 *)(data + result + 1);
+ spin_lock_irqsave(&dev->qmi.clients_lock, flags);
+
+ list_for_each(node, &dev->qmi.clients) {
+ client = list_entry(node, struct client, node);
+ if (client->cid == cid || (client->cid | 0xff00) == cid) {
+ copy = kmalloc(size, GFP_ATOMIC);
+ memcpy(copy, data, size);
+ if (!client_addread(dev, client->cid, tid, copy, size)) {
+ DBG("Error allocating pReadMemListEntry "
+ "read will be discarded\n");
+ kfree(copy);
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ return;
+ }
+
+ DBG("Creating new readListEntry for client 0x%04X, TID %x\n",
+ cid, tid);
+
+ client_notify(dev, client->cid, tid);
+
+ if (cid >> 8 != 0xff)
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+}
+
+static void int_callback(struct urb *urb)
+{
+ int status;
+ int interval;
+ struct qcusbnet *dev = (struct qcusbnet *)urb->context;
+
+ if (!device_valid(dev)) {
+ DBG("Invalid device!\n");
+ return;
+ }
+
+ if (urb->status) {
+ DBG("Int status = %d\n", urb->status);
+ if (urb->status != -EOVERFLOW)
+ return;
+ } else {
+ if ((urb->actual_length == 8) &&
+ (*(u64 *)urb->transfer_buffer == CDC_GET_ENCAPSULATED_RESPONSE)) {
+ usb_fill_control_urb(dev->qmi.readurb, dev->usbnet->udev,
+ usb_rcvctrlpipe(dev->usbnet->udev, 0),
+ (unsigned char *)dev->qmi.readsetup,
+ dev->qmi.readbuf,
+ DEFAULT_READ_URB_LENGTH,
+ read_callback, dev);
+ status = usb_submit_urb(dev->qmi.readurb, GFP_ATOMIC);
+ if (status) {
+ DBG("Error submitting Read URB %d\n", status);
+ return;
+ }
+ } else if ((urb->actual_length == 16) &&
+ (*(u64 *)urb->transfer_buffer == CDC_CONNECTION_SPEED_CHANGE)) {
+ /* if upstream or downstream is 0, stop traffic.
+ * Otherwise resume it */
+ if ((*(u32 *)(urb->transfer_buffer + 8) == 0) ||
+ (*(u32 *)(urb->transfer_buffer + 12) == 0)) {
+ qc_setdown(dev, DOWN_CDC_CONNECTION_SPEED);
+ DBG("traffic stopping due to CONNECTION_SPEED_CHANGE\n");
+ } else {
+ qc_cleardown(dev, DOWN_CDC_CONNECTION_SPEED);
+ DBG("resuming traffic due to CONNECTION_SPEED_CHANGE\n");
+ }
+ } else {
+ DBG("ignoring invalid interrupt in packet\n");
+ print_hex_dump(KERN_INFO, "QCUSBNet2k: ",
+ DUMP_PREFIX_OFFSET, 16, 1,
+ urb->transfer_buffer,
+ urb->actual_length, true);
+ }
+ }
+
+ interval = (dev->usbnet->udev->speed == USB_SPEED_HIGH) ? 7 : 3;
+
+ usb_fill_int_urb(urb, urb->dev, urb->pipe, urb->transfer_buffer,
+ urb->transfer_buffer_length, urb->complete,
+ urb->context, interval);
+ status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (status)
+ DBG("Error re-submitting Int URB %d\n", status);
+ return;
+}
+
+int qc_startread(struct qcusbnet *dev)
+{
+ int interval;
+
+ if (!device_valid(dev)) {
+ DBG("Invalid device!\n");
+ return -ENXIO;
+ }
+
+ dev->qmi.readurb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->qmi.readurb) {
+ DBG("Error allocating read urb\n");
+ return -ENOMEM;
+ }
+
+ dev->qmi.inturb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->qmi.inturb) {
+ usb_free_urb(dev->qmi.readurb);
+ DBG("Error allocating int urb\n");
+ return -ENOMEM;
+ }
+
+ dev->qmi.readbuf = kmalloc(DEFAULT_READ_URB_LENGTH, GFP_KERNEL);
+ if (!dev->qmi.readbuf) {
+ usb_free_urb(dev->qmi.readurb);
+ usb_free_urb(dev->qmi.inturb);
+ DBG("Error allocating read buffer\n");
+ return -ENOMEM;
+ }
+
+ dev->qmi.intbuf = kmalloc(DEFAULT_READ_URB_LENGTH, GFP_KERNEL);
+ if (!dev->qmi.intbuf) {
+ usb_free_urb(dev->qmi.readurb);
+ usb_free_urb(dev->qmi.inturb);
+ kfree(dev->qmi.readbuf);
+ DBG("Error allocating int buffer\n");
+ return -ENOMEM;
+ }
+
+ dev->qmi.readsetup = kmalloc(sizeof(*dev->qmi.readsetup), GFP_KERNEL);
+ if (!dev->qmi.readsetup) {
+ usb_free_urb(dev->qmi.readurb);
+ usb_free_urb(dev->qmi.inturb);
+ kfree(dev->qmi.readbuf);
+ kfree(dev->qmi.intbuf);
+ DBG("Error allocating setup packet buffer\n");
+ return -ENOMEM;
+ }
+
+ dev->qmi.readsetup->type = 0xA1;
+ dev->qmi.readsetup->code = 1;
+ dev->qmi.readsetup->value = 0;
+ dev->qmi.readsetup->index = 0;
+ dev->qmi.readsetup->len = DEFAULT_READ_URB_LENGTH;
+
+ interval = (dev->usbnet->udev->speed == USB_SPEED_HIGH) ? 7 : 3;
+
+ usb_fill_int_urb(dev->qmi.inturb, dev->usbnet->udev,
+ usb_rcvintpipe(dev->usbnet->udev, 0x81),
+ dev->qmi.intbuf, DEFAULT_READ_URB_LENGTH,
+ int_callback, dev, interval);
+ return usb_submit_urb(dev->qmi.inturb, GFP_KERNEL);
+}
+
+void qc_stopread(struct qcusbnet *dev)
+{
+ if (dev->qmi.readurb) {
+ DBG("Killing read URB\n");
+ usb_kill_urb(dev->qmi.readurb);
+ }
+
+ if (dev->qmi.inturb) {
+ DBG("Killing int URB\n");
+ usb_kill_urb(dev->qmi.inturb);
+ }
+
+ kfree(dev->qmi.readsetup);
+ dev->qmi.readsetup = NULL;
+ kfree(dev->qmi.readbuf);
+ dev->qmi.readbuf = NULL;
+ kfree(dev->qmi.intbuf);
+ dev->qmi.intbuf = NULL;
+
+ usb_free_urb(dev->qmi.readurb);
+ dev->qmi.readurb = NULL;
+ usb_free_urb(dev->qmi.inturb);
+ dev->qmi.inturb = NULL;
+}
+
+static int read_async(struct qcusbnet *dev, u16 cid, u16 tid,
+ void (*hook)(struct qcusbnet *, u16, void *),
+ void *data)
+{
+ struct list_head *node;
+ struct client *client;
+ struct readreq *readreq;
+
+ unsigned long flags;
+
+ if (!device_valid(dev)) {
+ DBG("Invalid device!\n");
+ return -ENXIO;
+ }
+
+ spin_lock_irqsave(&dev->qmi.clients_lock, flags);
+
+ client = client_bycid(dev, cid);
+ if (!client) {
+ DBG("Could not find matching client ID 0x%04X\n", cid);
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ return -ENXIO;
+ }
+
+ list_for_each(node, &client->reads) {
+ readreq = list_entry(node, struct readreq, node);
+ if (!tid || tid == readreq->tid) {
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ hook(dev, cid, data);
+ return 0;
+ }
+ }
+
+ if (!client_addnotify(dev, cid, tid, hook, data))
+ DBG("Unable to register for notification\n");
+
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ return 0;
+}
+
+static void upsem(struct qcusbnet *dev, u16 cid, void *data)
+{
+ DBG("0x%04X\n", cid);
+ up((struct semaphore *)data);
+}
+
+static int read_sync(struct qcusbnet *dev, void **buf, u16 cid, u16 tid)
+{
+ struct list_head *node;
+ int result;
+ struct client *client;
+ struct notifyreq *notify;
+ struct semaphore sem;
+ void *data;
+ unsigned long flags;
+ u16 size;
+
+ if (!device_valid(dev)) {
+ DBG("Invalid device!\n");
+ return -ENXIO;
+ }
+
+ spin_lock_irqsave(&dev->qmi.clients_lock, flags);
+
+ client = client_bycid(dev, cid);
+ if (!client) {
+ DBG("Could not find matching client ID 0x%04X\n", cid);
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ return -ENXIO;
+ }
+
+ while (!client_delread(dev, cid, tid, &data, &size)) {
+ sema_init(&sem, 0);
+ if (!client_addnotify(dev, cid, tid, upsem, &sem)) {
+ DBG("unable to register for notification\n");
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ return -EFAULT;
+ }
+
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+
+ result = down_interruptible(&sem);
+ if (result) {
+ DBG("Interrupted %d\n", result);
+ spin_lock_irqsave(&dev->qmi.clients_lock, flags);
+ list_for_each(node, &client->notifies) {
+ notify = list_entry(node, struct notifyreq, node);
+ if (notify->data == &sem) {
+ list_del(¬ify->node);
+ kfree(notify);
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ return -EINTR;
+ }
+
+ if (!device_valid(dev)) {
+ DBG("Invalid device!\n");
+ return -ENXIO;
+ }
+
+ spin_lock_irqsave(&dev->qmi.clients_lock, flags);
+ }
+
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ *buf = data;
+ return size;
+}
+
+static void write_callback(struct urb *urb)
+{
+ if (!urb) {
+ DBG("null urb\n");
+ return;
+ }
+
+ DBG("Write status/size %d/%d\n", urb->status, urb->actual_length);
+ up((struct semaphore *)urb->context);
+}
+
+static int write_sync(struct qcusbnet *dev, char *buf, int size, u16 cid)
+{
+ int result;
+ struct semaphore sem;
+ struct urb *urb;
+ struct urbsetup setup;
+ unsigned long flags;
+
+ if (!device_valid(dev)) {
+ DBG("Invalid device!\n");
+ return -ENXIO;
+ }
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ DBG("URB mem error\n");
+ return -ENOMEM;
+ }
+
+ result = qmux_fill(cid, buf, size);
+ if (result < 0) {
+ usb_free_urb(urb);
+ return result;
+ }
+
+ /* CDC Send Encapsulated Request packet */
+ setup.type = 0x21;
+ setup.code = 0;
+ setup.value = 0;
+ setup.index = 0;
+ setup.len = 0;
+ setup.len = size;
+
+ usb_fill_control_urb(urb, dev->usbnet->udev,
+ usb_sndctrlpipe(dev->usbnet->udev, 0),
+ (unsigned char *)&setup, (void *)buf, size,
+ NULL, dev);
+
+ DBG("Actual Write:\n");
+ print_hex_dump(KERN_INFO, "QCUSBNet2k: ", DUMP_PREFIX_OFFSET,
+ 16, 1, buf, size, true);
+
+ sema_init(&sem, 0);
+
+ urb->complete = write_callback;
+ urb->context = &sem;
+
+ result = usb_autopm_get_interface(dev->iface);
+ if (result < 0) {
+ DBG("unable to resume interface: %d\n", result);
+ if (result == -EPERM) {
+ qc_suspend(dev->iface, PMSG_SUSPEND);
+ }
+ return result;
+ }
+
+ spin_lock_irqsave(&dev->qmi.clients_lock, flags);
+
+ if (!client_addurb(dev, cid, urb)) {
+ usb_free_urb(urb);
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ usb_autopm_put_interface(dev->iface);
+ return -EINVAL;
+ }
+
+ result = usb_submit_urb(urb, GFP_KERNEL);
+ if (result < 0) {
+ DBG("submit URB error %d\n", result);
+ if (client_delurb(dev, cid) != urb) {
+ DBG("Didn't get write URB back\n");
+ }
+
+ usb_free_urb(urb);
+
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ usb_autopm_put_interface(dev->iface);
+ return result;
+ }
+
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ result = down_interruptible(&sem);
+ if (!device_valid(dev)) {
+ DBG("Invalid device!\n");
+ return -ENXIO;
+ }
+
+ usb_autopm_put_interface(dev->iface);
+ spin_lock_irqsave(&dev->qmi.clients_lock, flags);
+ if (client_delurb(dev, cid) != urb) {
+ DBG("Didn't get write URB back\n");
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ return -EINVAL;
+ }
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+
+ if (!result) {
+ if (!urb->status) {
+ result = size;
+ } else {
+ DBG("bad status = %d\n", urb->status);
+ result = urb->status;
+ }
+ } else {
+ DBG("Interrupted %d !!!\n", result);
+ DBG("Device may be in bad state and need reset !!!\n");
+ usb_kill_urb(urb);
+ }
+
+ usb_free_urb(urb);
+ return result;
+}
+
+static int client_alloc(struct qcusbnet *dev, u8 type)
+{
+ u16 cid;
+ struct client *client;
+ int result;
+ void *wbuf;
+ size_t wbufsize;
+ void *rbuf;
+ u16 rbufsize;
+ unsigned long flags;
+ u8 tid;
+
+ if (!device_valid(dev)) {
+ DBG("Invalid device!\n");
+ return -ENXIO;
+ }
+
+ if (type) {
+ tid = atomic_add_return(1, &dev->qmi.qmitid);
+ if (!tid)
+ atomic_add_return(1, &dev->qmi.qmitid);
+ wbuf = qmictl_new_getcid(tid, type, &wbufsize);
+ if (!wbuf)
+ return -ENOMEM;
+ result = write_sync(dev, wbuf, wbufsize, QMICTL);
+ kfree(wbuf);
+
+ if (result < 0)
+ return result;
+
+ result = read_sync(dev, &rbuf, QMICTL, tid);
+ if (result < 0) {
+ DBG("bad read data %d\n", result);
+ return result;
+ }
+ rbufsize = result;
+
+ result = qmictl_alloccid_resp(rbuf, rbufsize, &cid);
+ kfree(rbuf);
+
+ if (result < 0)
+ return result;
+ } else {
+ cid = 0;
+ }
+
+ spin_lock_irqsave(&dev->qmi.clients_lock, flags);
+ if (client_bycid(dev, cid)) {
+ DBG("Client memory already exists\n");
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ return -ETOOMANYREFS;
+ }
+
+ client = kmalloc(sizeof(*client), GFP_ATOMIC);
+ if (!client) {
+ DBG("Error allocating read list\n");
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ return -ENOMEM;
+ }
+
+ list_add_tail(&client->node, &dev->qmi.clients);
+ client->cid = cid;
+ INIT_LIST_HEAD(&client->reads);
+ INIT_LIST_HEAD(&client->notifies);
+ INIT_LIST_HEAD(&client->urbs);
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ return cid;
+}
+
+static void client_free(struct qcusbnet *dev, u16 cid)
+{
+ struct list_head *node, *tmp;
+ int result;
+ struct client *client;
+ struct urb *urb;
+ void *data;
+ u16 size;
+ void *wbuf;
+ size_t wbufsize;
+ void *rbuf;
+ u16 rbufsize;
+ unsigned long flags;
+ u8 tid;
+
+ if (!device_valid(dev)) {
+ DBG("invalid device\n");
+ return;
+ }
+
+ DBG("releasing 0x%04X\n", cid);
+
+ if (cid != QMICTL) {
+ tid = atomic_add_return(1, &dev->qmi.qmitid);
+ if (!tid)
+ tid = atomic_add_return(1, &dev->qmi.qmitid);
+ wbuf = qmictl_new_releasecid(tid, cid, &wbufsize);
+ if (!wbuf) {
+ DBG("memory error\n");
+ } else {
+ result = write_sync(dev, wbuf, wbufsize, QMICTL);
+ kfree(wbuf);
+
+ if (result < 0) {
+ DBG("bad write status %d\n", result);
+ } else {
+ result = read_sync(dev, &rbuf, QMICTL, tid);
+ if (result < 0) {
+ DBG("bad read status %d\n", result);
+ } else {
+ rbufsize = result;
+ result = qmictl_freecid_resp(rbuf, rbufsize);
+ kfree(rbuf);
+ if (result < 0)
+ DBG("error %d parsing response\n", result);
+ }
+ }
+ }
+ }
+
+ spin_lock_irqsave(&dev->qmi.clients_lock, flags);
+ list_for_each_safe(node, tmp, &dev->qmi.clients) {
+ client = list_entry(node, struct client, node);
+ if (client->cid == cid) {
+ while (client_notify(dev, cid, 0)) {
+ ;
+ }
+
+ urb = client_delurb(dev, cid);
+ while (urb != NULL) {
+ usb_kill_urb(urb);
+ usb_free_urb(urb);
+ urb = client_delurb(dev, cid);
+ }
+
+ while (client_delread(dev, cid, 0, &data, &size))
+ kfree(data);
+
+ list_del(&client->node);
+ kfree(client);
+ }
+ }
+
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+}
+
+struct client *client_bycid(struct qcusbnet *dev, u16 cid)
+{
+ struct list_head *node;
+ struct client *client;
+
+ if (!device_valid(dev)) {
+ DBG("Invalid device\n");
+ return NULL;
+ }
+
+ assert_locked(dev);
+
+ list_for_each(node, &dev->qmi.clients) {
+ client = list_entry(node, struct client, node);
+ if (client->cid == cid)
+ return client;
+ }
+
+ DBG("Could not find client mem 0x%04X\n", cid);
+ return NULL;
+}
+
+static bool client_addread(struct qcusbnet *dev, u16 cid, u16 tid, void *data,
+ u16 size)
+{
+ struct client *client;
+ struct readreq *req;
+
+ assert_locked(dev);
+
+ client = client_bycid(dev, cid);
+ if (!client) {
+ DBG("Could not find this client's memory 0x%04X\n", cid);
+ return false;
+ }
+
+ req = kmalloc(sizeof(*req), GFP_ATOMIC);
+ if (!req) {
+ DBG("Mem error\n");
+ return false;
+ }
+
+ req->data = data;
+ req->size = size;
+ req->tid = tid;
+
+ list_add_tail(&req->node, &client->reads);
+
+ return true;
+}
+
+static bool client_delread(struct qcusbnet *dev, u16 cid, u16 tid, void **data,
+ u16 *size)
+{
+ struct client *client;
+ struct readreq *req;
+ struct list_head *node;
+
+ assert_locked(dev);
+
+ client = client_bycid(dev, cid);
+ if (!client) {
+ DBG("Could not find this client's memory 0x%04X\n", cid);
+ return false;
+ }
+
+ list_for_each(node, &client->reads) {
+ req = list_entry(node, struct readreq, node);
+ if (!tid || tid == req->tid) {
+ *data = req->data;
+ *size = req->size;
+ list_del(&req->node);
+ kfree(req);
+ return true;
+ }
+
+ DBG("skipping 0x%04X data TID = %x\n", cid, req->tid);
+ }
+
+ DBG("No read memory to pop, Client 0x%04X, TID = %x\n", cid, tid);
+ return false;
+}
+
+static bool client_addnotify(struct qcusbnet *dev, u16 cid, u16 tid,
+ void (*hook)(struct qcusbnet *, u16, void *),
+ void *data)
+{
+ struct client *client;
+ struct notifyreq *req;
+
+ assert_locked(dev);
+
+ client = client_bycid(dev, cid);
+ if (!client) {
+ DBG("Could not find this client's memory 0x%04X\n", cid);
+ return false;
+ }
+
+ req = kmalloc(sizeof(*req), GFP_ATOMIC);
+ if (!req) {
+ DBG("Mem error\n");
+ return false;
+ }
+
+ list_add_tail(&req->node, &client->notifies);
+ req->func = hook;
+ req->data = data;
+ req->tid = tid;
+
+ return true;
+}
+
+static bool client_notify(struct qcusbnet *dev, u16 cid, u16 tid)
+{
+ struct client *client;
+ struct notifyreq *delnotify, *notify;
+ struct list_head *node;
+
+ assert_locked(dev);
+
+ client = client_bycid(dev, cid);
+ if (!client) {
+ DBG("Could not find this client's memory 0x%04X\n", cid);
+ return false;
+ }
+
+ delnotify = NULL;
+
+ list_for_each(node, &client->notifies) {
+ notify = list_entry(node, struct notifyreq, node);
+ if (!tid || !notify->tid || tid == notify->tid) {
+ delnotify = notify;
+ break;
+ }
+
+ DBG("skipping data TID = %x\n", notify->tid);
+ }
+
+ if (delnotify) {
+ list_del(&delnotify->node);
+ if (delnotify->func) {
+ spin_unlock(&dev->qmi.clients_lock);
+ delnotify->func(dev, cid, delnotify->data);
+ spin_lock(&dev->qmi.clients_lock);
+ }
+ kfree(delnotify);
+ return true;
+ }
+
+ DBG("no one to notify for TID %x\n", tid);
+ return false;
+}
+
+static bool client_addurb(struct qcusbnet *dev, u16 cid, struct urb *urb)
+{
+ struct client *client;
+ struct urbreq *req;
+
+ assert_locked(dev);
+
+ client = client_bycid(dev, cid);
+ if (!client) {
+ DBG("Could not find this client's memory 0x%04X\n", cid);
+ return false;
+ }
+
+ req = kmalloc(sizeof(*req), GFP_ATOMIC);
+ if (!req) {
+ DBG("Mem error\n");
+ return false;
+ }
+
+ req->urb = urb;
+ list_add_tail(&req->node, &client->urbs);
+
+ return true;
+}
+
+static struct urb *client_delurb(struct qcusbnet *dev, u16 cid)
+{
+ struct client *client;
+ struct urbreq *req;
+ struct urb *urb;
+
+ assert_locked(dev);
+
+ client = client_bycid(dev, cid);
+ if (!client) {
+ DBG("Could not find this client's memory 0x%04X\n", cid);
+ return NULL;
+ }
+
+ if (list_empty(&client->urbs)) {
+ DBG("No URB's to pop\n");
+ return NULL;
+ }
+
+ req = list_first_entry(&client->urbs, struct urbreq, node);
+ list_del(&req->node);
+ urb = req->urb;
+ kfree(req);
+ return urb;
+}
+
+static int devqmi_open(struct inode *inode, struct file *file)
+{
+ struct qmihandle *handle;
+ struct qmidev *qmidev = container_of(inode->i_cdev, struct qmidev, cdev);
+ struct qcusbnet *dev = container_of(qmidev, struct qcusbnet, qmi);
+
+ if (!device_valid(dev)) {
+ DBG("Invalid device\n");
+ return -ENXIO;
+ }
+
+ file->private_data = kmalloc(sizeof(struct qmihandle), GFP_KERNEL);
+ if (!file->private_data) {
+ DBG("Mem error\n");
+ return -ENOMEM;
+ }
+
+ handle = (struct qmihandle *)file->private_data;
+ handle->cid = (u16)-1;
+ handle->dev = dev;
+
+ return 0;
+}
+
+static long devqmi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int result;
+ u32 vidpid;
+
+ struct qmihandle *handle = (struct qmihandle *)file->private_data;
+
+ if (!handle) {
+ DBG("Bad file data\n");
+ return -EBADF;
+ }
+
+ if (!device_valid(handle->dev)) {
+ DBG("Invalid device! Updating f_ops\n");
+ file->f_op = file->f_dentry->d_inode->i_fop;
+ return -ENXIO;
+ }
+
+ switch (cmd) {
+ case IOCTL_QMI_GET_SERVICE_FILE:
+
+ DBG("Setting up QMI for service %lu\n", arg);
+ if (!(u8)arg) {
+ DBG("Cannot use QMICTL from userspace\n");
+ return -EINVAL;
+ }
+
+ if (handle->cid != (u16)-1) {
+ DBG("Close the current connection before opening a new one\n");
+ return -EBADR;
+ }
+
+ result = client_alloc(handle->dev, (u8)arg);
+ if (result < 0)
+ return result;
+ handle->cid = result;
+
+ return 0;
+ break;
+
+
+ case IOCTL_QMI_GET_DEVICE_VIDPID:
+ if (!arg) {
+ DBG("Bad VIDPID buffer\n");
+ return -EINVAL;
+ }
+
+ if (!handle->dev->usbnet) {
+ DBG("Bad usbnet\n");
+ return -ENOMEM;
+ }
+
+ if (!handle->dev->usbnet->udev) {
+ DBG("Bad udev\n");
+ return -ENOMEM;
+ }
+
+ vidpid = ((le16_to_cpu(handle->dev->usbnet->udev->descriptor.idVendor) << 16)
+ + le16_to_cpu(handle->dev->usbnet->udev->descriptor.idProduct));
+
+ result = copy_to_user((unsigned int *)arg, &vidpid, 4);
+ if (result)
+ DBG("Copy to userspace failure\n");
+
+ return result;
+ break;
+
+ case IOCTL_QMI_GET_DEVICE_MEID:
+ if (!arg) {
+ DBG("Bad MEID buffer\n");
+ return -EINVAL;
+ }
+
+ result = copy_to_user((unsigned int *)arg, &handle->dev->meid[0], 14);
+ if (result)
+ DBG("copy to userspace failure\n");
+
+ return result;
+ break;
+ default:
+ return -EBADRQC;
+ }
+}
+
+static int devqmi_close(struct file *file, fl_owner_t ftable)
+{
+ struct qmihandle *handle = (struct qmihandle *)file->private_data;
+ struct list_head *tasks;
+ struct task_struct *task;
+ struct fdtable *fdtable;
+ int count = 0;
+ int used = 0;
+ unsigned long flags;
+
+ if (!handle) {
+ DBG("bad file data\n");
+ return -EBADF;
+ }
+
+ if (file_count(file) != 1) {
+ /* XXX: This can't possibly be safe. We don't hold any sort of
+ * lock here, and we're walking a list of threads... */
+ list_for_each(tasks, ¤t->group_leader->tasks) {
+ task = container_of(tasks, struct task_struct, tasks);
+ if (!task || !task->files)
+ continue;
+ spin_lock_irqsave(&task->files->file_lock, flags);
+ fdtable = files_fdtable(task->files);
+ for (count = 0; count < fdtable->max_fds; count++) {
+ /* Before this function was called, this file was removed
+ * from our task's file table so if we find it in a file
+ * table then it is being used by another task
+ */
+ if (fdtable->fd[count] == file) {
+ used++;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&task->files->file_lock, flags);
+ }
+
+ if (used > 0) {
+ DBG("not closing, as this FD is open by %d other process\n", used);
+ return 0;
+ }
+ }
+
+ if (!device_valid(handle->dev)) {
+ DBG("Invalid device! Updating f_ops\n");
+ file->f_op = file->f_dentry->d_inode->i_fop;
+ return -ENXIO;
+ }
+
+ DBG("0x%04X\n", handle->cid);
+
+ file->private_data = NULL;
+
+ if (handle->cid != (u16)-1)
+ client_free(handle->dev, handle->cid);
+
+ kfree(handle);
+ return 0;
+}
+
+static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size,
+ loff_t *pos)
+{
+ int result;
+ void *data = NULL;
+ void *smalldata;
+ struct qmihandle *handle = (struct qmihandle *)file->private_data;
+
+ if (!handle) {
+ DBG("Bad file data\n");
+ return -EBADF;
+ }
+
+ if (!device_valid(handle->dev)) {
+ DBG("Invalid device! Updating f_ops\n");
+ file->f_op = file->f_dentry->d_inode->i_fop;
+ return -ENXIO;
+ }
+
+ if (handle->cid == (u16)-1) {
+ DBG("Client ID must be set before reading 0x%04X\n",
+ handle->cid);
+ return -EBADR;
+ }
+
+ result = read_sync(handle->dev, &data, handle->cid, 0);
+ if (result <= 0)
+ return result;
+
+ result -= qmux_size;
+ smalldata = data + qmux_size;
+
+ if (result > size) {
+ DBG("Read data is too large for amount user has requested\n");
+ kfree(data);
+ return -EOVERFLOW;
+ }
+
+ if (copy_to_user(buf, smalldata, result)) {
+ DBG("Error copying read data to user\n");
+ result = -EFAULT;
+ }
+
+ kfree(data);
+ return result;
+}
+
+static ssize_t devqmi_write(struct file *file, const char __user * buf,
+ size_t size, loff_t *pos)
+{
+ int status;
+ void *wbuf;
+ struct qmihandle *handle = (struct qmihandle *)file->private_data;
+
+ if (!handle) {
+ DBG("Bad file data\n");
+ return -EBADF;
+ }
+
+ if (!device_valid(handle->dev)) {
+ DBG("Invalid device! Updating f_ops\n");
+ file->f_op = file->f_dentry->d_inode->i_fop;
+ return -ENXIO;
+ }
+
+ if (handle->cid == (u16)-1) {
+ DBG("Client ID must be set before writing 0x%04X\n",
+ handle->cid);
+ return -EBADR;
+ }
+
+ wbuf = kmalloc(size + qmux_size, GFP_KERNEL);
+ if (!wbuf)
+ return -ENOMEM;
+ status = copy_from_user(wbuf + qmux_size, buf, size);
+ if (status) {
+ DBG("Unable to copy data from userspace %d\n", status);
+ kfree(wbuf);
+ return status;
+ }
+
+ status = write_sync(handle->dev, wbuf, size + qmux_size,
+ handle->cid);
+
+ kfree(wbuf);
+ if (status == size + qmux_size)
+ return size;
+ return status;
+}
+
+int qc_register(struct qcusbnet *dev)
+{
+ int result;
+ int qmiidx = 0;
+ dev_t devno;
+ char *name;
+
+ dev->valid = true;
+ result = client_alloc(dev, QMICTL);
+ if (result) {
+ dev->valid = false;
+ return result;
+ }
+ atomic_set(&dev->qmi.qmitid, 1);
+
+ result = qc_startread(dev);
+ if (result) {
+ dev->valid = false;
+ return result;
+ }
+
+ if (!qmi_ready(dev, 30000)) {
+ DBG("Device unresponsive to QMI\n");
+ return -ETIMEDOUT;
+ }
+
+ result = setup_wds_callback(dev);
+ if (result) {
+ dev->valid = false;
+ return result;
+ }
+
+ result = qmidms_getmeid(dev);
+ if (result) {
+ dev->valid = false;
+ return result;
+ }
+
+ result = alloc_chrdev_region(&devno, 0, 1, "qcqmi");
+ if (result < 0)
+ return result;
+
+ cdev_init(&dev->qmi.cdev, &devqmi_fops);
+ dev->qmi.cdev.owner = THIS_MODULE;
+ dev->qmi.cdev.ops = &devqmi_fops;
+
+ result = cdev_add(&dev->qmi.cdev, devno, 1);
+ if (result) {
+ DBG("error adding cdev\n");
+ return result;
+ }
+
+ name = strstr(dev->usbnet->net->name, "usb");
+ if (!name) {
+ DBG("Bad net name: %s\n", dev->usbnet->net->name);
+ return -ENXIO;
+ }
+ name += strlen("usb");
+ qmiidx = simple_strtoul(name, NULL, 10);
+ if (qmiidx < 0) {
+ DBG("Bad minor number\n");
+ return -ENXIO;
+ }
+
+ printk(KERN_INFO "creating qcqmi%d\n", qmiidx);
+ device_create(dev->qmi.devclass, NULL, devno, NULL, "qcqmi%d", qmiidx);
+
+ dev->qmi.devnum = devno;
+ return 0;
+}
+
+void qc_deregister(struct qcusbnet *dev)
+{
+ struct list_head *node;
+ struct client *client;
+ struct inode *inode;
+ struct list_head *inodes;
+ struct list_head *tasks;
+ struct task_struct *task;
+ struct fdtable *fdtable;
+ struct file *file;
+ unsigned long flags;
+ int count = 0;
+
+ if (!device_valid(dev)) {
+ DBG("wrong device\n");
+ return;
+ }
+
+ list_for_each(node, &dev->qmi.clients) {
+ client = list_entry(node, struct client, node);
+ DBG("release 0x%04X\n", client->cid);
+ client_free(dev, client->cid);
+ }
+
+ qc_stopread(dev);
+ dev->valid = false;
+ list_for_each(inodes, &dev->qmi.cdev.list) {
+ inode = container_of(inodes, struct inode, i_devices);
+ if (inode != NULL && !IS_ERR(inode)) {
+ list_for_each(tasks, ¤t->group_leader->tasks) {
+ task = container_of(tasks, struct task_struct, tasks);
+ if (!task || !task->files)
+ continue;
+ spin_lock_irqsave(&task->files->file_lock, flags);
+ fdtable = files_fdtable(task->files);
+ for (count = 0; count < fdtable->max_fds; count++) {
+ file = fdtable->fd[count];
+ if (file != NULL && file->f_dentry != NULL) {
+ if (file->f_dentry->d_inode == inode) {
+ rcu_assign_pointer(fdtable->fd[count], NULL);
+ spin_unlock_irqrestore(&task->files->file_lock, flags);
+ DBG("forcing close of open file handle\n");
+ filp_close(file, task->files);
+ spin_lock_irqsave(&task->files->file_lock, flags);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&task->files->file_lock, flags);
+ }
+ }
+ }
+
+ if (!IS_ERR(dev->qmi.devclass))
+ device_destroy(dev->qmi.devclass, dev->qmi.devnum);
+ cdev_del(&dev->qmi.cdev);
+ unregister_chrdev_region(dev->qmi.devnum, 1);
+}
+
+static bool qmi_ready(struct qcusbnet *dev, u16 timeout)
+{
+ int result;
+ void *wbuf;
+ size_t wbufsize;
+ void *rbuf;
+ u16 rbufsize;
+ struct semaphore sem;
+ u16 now;
+ unsigned long flags;
+ u8 tid;
+
+ if (!device_valid(dev)) {
+ DBG("Invalid device\n");
+ return -EFAULT;
+ }
+
+
+ for (now = 0; now < timeout; now += 100) {
+ sema_init(&sem, 0);
+
+ tid = atomic_add_return(1, &dev->qmi.qmitid);
+ if (!tid)
+ tid = atomic_add_return(1, &dev->qmi.qmitid);
+ kfree(wbuf);
+ wbuf = qmictl_new_ready(tid, &wbufsize);
+ if (!wbuf)
+ return -ENOMEM;
+
+ result = read_async(dev, QMICTL, tid, upsem, &sem);
+ if (result) {
+ kfree(wbuf);
+ return false;
+ }
+
+ write_sync(dev, wbuf, wbufsize, QMICTL);
+
+ msleep(100);
+ if (!down_trylock(&sem)) {
+ spin_lock_irqsave(&dev->qmi.clients_lock, flags);
+ if (client_delread(dev, QMICTL, tid, &rbuf, &rbufsize)) {
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ kfree(rbuf);
+ break;
+ }
+ } else {
+ spin_lock_irqsave(&dev->qmi.clients_lock, flags);
+ client_notify(dev, QMICTL, tid);
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ }
+ }
+
+ kfree(wbuf);
+
+ if (now >= timeout)
+ return false;
+
+ DBG("QMI Ready after %u milliseconds\n", now);
+
+ /* 3580 and newer doesn't need a delay; older needs 5000ms */
+ if (qcusbnet2k_fwdelay)
+ msleep(qcusbnet2k_fwdelay * 1000);
+
+ return true;
+}
+
+static void wds_callback(struct qcusbnet *dev, u16 cid, void *data)
+{
+ bool ret;
+ int result;
+ void *rbuf;
+ u16 rbufsize;
+
+ struct net_device_stats *stats = &(dev->usbnet->net->stats);
+
+ struct qmiwds_stats dstats = {
+ .txok = (u32)-1,
+ .rxok = (u32)-1,
+ .txerr = (u32)-1,
+ .rxerr = (u32)-1,
+ .txofl = (u32)-1,
+ .rxofl = (u32)-1,
+ .txbytesok = (u64)-1,
+ .rxbytesok = (u64)-1,
+ };
+ unsigned long flags;
+
+ if (!device_valid(dev)) {
+ DBG("Invalid device\n");
+ return;
+ }
+
+ spin_lock_irqsave(&dev->qmi.clients_lock, flags);
+ ret = client_delread(dev, cid, 0, &rbuf, &rbufsize);
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+
+ if (!ret) {
+ DBG("WDS callback failed to get data\n");
+ return;
+ }
+
+ dstats.linkstate = !qc_isdown(dev, DOWN_NO_NDIS_CONNECTION);
+ dstats.reconfigure = false;
+
+ result = qmiwds_event_resp(rbuf, rbufsize, &dstats);
+ if (result < 0) {
+ DBG("bad WDS packet\n");
+ } else {
+ if (dstats.txofl != (u32)-1)
+ stats->tx_fifo_errors = dstats.txofl;
+
+ if (dstats.rxofl != (u32)-1)
+ stats->rx_fifo_errors = dstats.rxofl;
+
+ if (dstats.txerr != (u32)-1)
+ stats->tx_errors = dstats.txerr;
+
+ if (dstats.rxerr != (u32)-1)
+ stats->rx_errors = dstats.rxerr;
+
+ if (dstats.txok != (u32)-1)
+ stats->tx_packets = dstats.txok + stats->tx_errors;
+
+ if (dstats.rxok != (u32)-1)
+ stats->rx_packets = dstats.rxok + stats->rx_errors;
+
+ if (dstats.txbytesok != (u64)-1)
+ stats->tx_bytes = dstats.txbytesok;
+
+ if (dstats.rxbytesok != (u64)-1)
+ stats->rx_bytes = dstats.rxbytesok;
+
+ if (dstats.reconfigure) {
+ DBG("Net device link reset\n");
+ qc_setdown(dev, DOWN_NO_NDIS_CONNECTION);
+ qc_cleardown(dev, DOWN_NO_NDIS_CONNECTION);
+ } else {
+ if (dstats.linkstate) {
+ DBG("Net device link is connected\n");
+ qc_cleardown(dev, DOWN_NO_NDIS_CONNECTION);
+ } else {
+ DBG("Net device link is disconnected\n");
+ qc_setdown(dev, DOWN_NO_NDIS_CONNECTION);
+ }
+ }
+ }
+
+ kfree(rbuf);
+
+ result = read_async(dev, cid, 0, wds_callback, data);
+ if (result != 0)
+ DBG("unable to setup next async read\n");
+}
+
+static int setup_wds_callback(struct qcusbnet *dev)
+{
+ int result;
+ void *buf;
+ size_t size;
+ u16 cid;
+
+ if (!device_valid(dev)) {
+ DBG("Invalid device\n");
+ return -EFAULT;
+ }
+
+ result = client_alloc(dev, QMIWDS);
+ if (result < 0)
+ return result;
+ cid = result;
+
+ buf = qmiwds_new_seteventreport(1, &size);
+ if (!buf)
+ return -ENOMEM;
+
+ result = write_sync(dev, buf, size, cid);
+ kfree(buf);
+
+ if (result < 0) {
+ return result;
+ }
+
+ buf = qmiwds_new_getpkgsrvcstatus(2, &size);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ result = write_sync(dev, buf, size, cid);
+ kfree(buf);
+
+ if (result < 0)
+ return result;
+
+ result = read_async(dev, cid, 0, wds_callback, NULL);
+ if (result) {
+ DBG("unable to setup async read\n");
+ return result;
+ }
+
+ result = usb_control_msg(dev->usbnet->udev,
+ usb_sndctrlpipe(dev->usbnet->udev, 0),
+ 0x22, 0x21, 1, 0, NULL, 0, 100);
+ if (result < 0) {
+ DBG("Bad SetControlLineState status %d\n", result);
+ return result;
+ }
+
+ return 0;
+}
+
+static int qmidms_getmeid(struct qcusbnet *dev)
+{
+ int result;
+ void *wbuf;
+ size_t wbufsize;
+ void *rbuf;
+ u16 rbufsize;
+ u16 cid;
+
+ if (!device_valid(dev)) {
+ DBG("Invalid device\n");
+ return -EFAULT;
+ }
+
+ result = client_alloc(dev, QMIDMS);
+ if (result < 0)
+ return result;
+ cid = result;
+
+ wbuf = qmidms_new_getmeid(1, &wbufsize);
+ if (!wbuf)
+ return -ENOMEM;
+
+ result = write_sync(dev, wbuf, wbufsize, cid);
+ kfree(wbuf);
+
+ if (result < 0)
+ return result;
+
+ result = read_sync(dev, &rbuf, cid, 1);
+ if (result < 0)
+ return result;
+ rbufsize = result;
+
+ result = qmidms_meid_resp(rbuf, rbufsize, &dev->meid[0], 14);
+ kfree(rbuf);
+
+ if (result < 0) {
+ DBG("bad get MEID resp\n");
+ memset(&dev->meid[0], '0', 14);
+ }
+
+ client_free(dev, cid);
+ return 0;
+}
+
+module_param(qcusbnet2k_fwdelay, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(qcusbnet2k_fwdelay, "Delay for old firmware");
diff --git a/drivers/net/usb/gobi/qmidevice.h b/drivers/net/usb/gobi/qmidevice.h
new file mode 100755
index 0000000..5274a0d
--- /dev/null
+++ b/drivers/net/usb/gobi/qmidevice.h
@@ -0,0 +1,35 @@
+/* qmidevice.h - gobi QMI device header
+ * 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 and
+ * only version 2 as published by the Free Software Foundation.
+
+ * 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.
+ */
+
+#ifndef QCUSBNET_QMIDEVICE_H
+#define QCUSBNET_QMIDEVICE_H
+
+#include "structs.h"
+#include "qmi.h"
+
+void qc_setdown(struct qcusbnet *dev, u8 reason);
+void qc_cleardown(struct qcusbnet *dev, u8 reason);
+bool qc_isdown(struct qcusbnet *dev, u8 reason);
+
+int qc_startread(struct qcusbnet *dev);
+void qc_stopread(struct qcusbnet *dev);
+
+int qc_register(struct qcusbnet *dev);
+void qc_deregister(struct qcusbnet *dev);
+
+#endif /* !QCUSBNET_QMIDEVICE_H */
diff --git a/drivers/net/usb/gobi/structs.h b/drivers/net/usb/gobi/structs.h
new file mode 100755
index 0000000..7a89c5c
--- /dev/null
+++ b/drivers/net/usb/gobi/structs.h
@@ -0,0 +1,92 @@
+/* structs.h - shared structures
+ * 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 and
+ * only version 2 as published by the Free Software Foundation.
+
+ * 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.
+ */
+
+#ifndef QCUSBNET_STRUCTS_H
+#define QCUSBNET_STRUCTS_H
+
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/version.h>
+#include <linux/cdev.h>
+#include <linux/kthread.h>
+
+#include <linux/usb/usbnet.h>
+
+#include <linux/fdtable.h>
+
+#define DBG(fmt, arg...) \
+do { \
+ if (qcusbnet_debug == 1) \
+ printk(KERN_INFO "QCUSBNet2k::%s " fmt, __func__, ##arg); \
+} while (0)
+
+struct qcusbnet;
+
+struct urbreq {
+ struct list_head node;
+ struct urb *urb;
+};
+
+#define DEFAULT_READ_URB_LENGTH 0x1000
+
+struct worker {
+ struct task_struct *thread;
+ struct completion work;
+ struct list_head urbs;
+ spinlock_t urbs_lock;
+ struct urb *active;
+ spinlock_t active_lock;
+ struct usb_interface *iface;
+};
+
+struct qmidev {
+ dev_t devnum;
+ struct class *devclass;
+ struct cdev cdev;
+ struct urb *readurb;
+ struct urbsetup *readsetup;
+ void *readbuf;
+ struct urb *inturb;
+ void *intbuf;
+ struct list_head clients;
+ spinlock_t clients_lock;
+ atomic_t qmitid;
+};
+
+enum {
+ DOWN_NO_NDIS_CONNECTION = 0,
+ DOWN_CDC_CONNECTION_SPEED = 1,
+ DOWN_DRIVER_SUSPENDED = 2,
+ DOWN_NET_IFACE_STOPPED = 3,
+};
+
+struct qcusbnet {
+ struct usbnet *usbnet;
+ struct usb_interface *iface;
+ int (*open)(struct net_device *);
+ int (*stop)(struct net_device *);
+ unsigned long down;
+ bool valid;
+ struct qmidev qmi;
+ char meid[14];
+ struct worker worker;
+};
+
+#endif /* !QCUSBNET_STRUCTS_H */
--
1.7.1
^ permalink raw reply related
* Re: [net-next-2.6 PATCH] net: netif_set_real_num_rx_queues may cap num_rx_queues at init time
From: Ben Hutchings @ 2010-10-06 15:07 UTC (permalink / raw)
To: John Fastabend; +Cc: Eric Dumazet, netdev@vger.kernel.org, therbert@google.com
In-Reply-To: <4CAC8D11.2060604@intel.com>
On Wed, 2010-10-06 at 07:52 -0700, John Fastabend wrote:
> On 10/5/2010 10:45 AM, John Fastabend wrote:
> > On 10/5/2010 9:34 AM, Ben Hutchings wrote:
> >> On Tue, 2010-10-05 at 09:08 -0700, John Fastabend wrote:
> >>> On 10/4/2010 10:35 PM, Eric Dumazet wrote:
[...]
> >>>> Why should we keep num_rx_queues > real_num_rx_queues ?
> >>>>
> >>>
> >>> If we do not ever need them then we should not keep them I agree.
> >>> But having netif_set_real_num_rx_queues set something other then
> >>> 'real_num_rx_queues' does not seem right to me at least. Also
> >>> netif_set_real_num_tx_queues and netif_set_real_num_rx_queues have
> >>> different behavior. It would be nice if this weren't the case but
> >>> they allocate queues in two places.
> >> [...]
> >>
> >> I only did this to satisfy Eric's desire to reduce memory usage.
> >> However, I believe that there are currently no drivers that dynamically
> >> increase numbers of RX or TX queues. Until there are, there is not much
> >> point in removing this assignment to num_rx_queues.
> >>
> >> Ben.
> >>
> >
> > ixgbe increases the real_num_[rx|tx]_queues when FCoE or DCB is enabled.
> > Also many of the drivers could increase the number of queues if they were
> > given more interrupt vectors at some point.
>
>
> If I update the handful drivers that use netif_set_real_num_rx_queues()
> before the netdevice is registered to explicitly set num_rx_queues this
> would address Eric's concerns and fix drivers that really only want to set
> real_num_rx_queue.
>
> Any thoughts?
Don't add assignments to num_rx_queues. If it's useful to increase the
number of RX queues later then just remove the assignment to
num_rx_queues from netif_set_real_num_rx_queues() and be done with it.
The waste of memory is minimal now that we only allocate kobjects for
real_num_rx_queues.
Ben.
--
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply
* Re: [net-next-2.6 PATCH] net: netif_set_real_num_rx_queues may cap num_rx_queues at init time
From: John Fastabend @ 2010-10-06 14:52 UTC (permalink / raw)
To: Ben Hutchings; +Cc: Eric Dumazet, netdev@vger.kernel.org, therbert@google.com
In-Reply-To: <4CAB6447.6040407@intel.com>
On 10/5/2010 10:45 AM, John Fastabend wrote:
> On 10/5/2010 9:34 AM, Ben Hutchings wrote:
>> On Tue, 2010-10-05 at 09:08 -0700, John Fastabend wrote:
>>> On 10/4/2010 10:35 PM, Eric Dumazet wrote:
>>>> Le lundi 04 octobre 2010 à 15:00 -0700, John Fastabend a écrit :
>>>>> The logic for netif_set_real_num_rx_queues is the following,
>>>>>
>>>>> netif_set_real_num_rx_queues(dev, rxq)
>>>>> {
>>>>> ...
>>>>> if (dev->reg_state == NETREG_REGISTERED) {
>>>>> ...
>>>>> } else {
>>>>> dev->num_rx_queues = rxq;
>>>>> }
>>>>>
>>>>> dev->real_num_rx_queues = rxq;
>>>>> return 0;
>>>>> }
>>>>>
>>>>> Some drivers init path looks like the following,
>>>>>
>>>>> alloc_etherdev_mq(priv_sz, max_num_queues_ever);
>>>>> ...
>>>>> netif_set_real_num_rx_queues(dev, queues_to_use_now);
>>>>> ...
>>>>> register_netdev(dev);
>>>>> ...
>>>>>
>>>>> Because netif_set_real_num_rx_queues sets num_rx_queues if the
>>>>> reg state is not NETREG_REGISTERED we end up with the incorrect
>>>>> max number of rx queues. This patch proposes to remove the else
>>>>> clause above so this does not occur. Also just reading the
>>>>> function set_real_num it seems a bit unexpected that num_rx_queues
>>>>> gets set.
>>>>>
>>>>
>>>> You dont tell why its "incorrect".
>>>>
>>>
>>> OK that is a poor description.
>>>
>>>> Why should we keep num_rx_queues > real_num_rx_queues ?
>>>>
>>>
>>> If we do not ever need them then we should not keep them I agree.
>>> But having netif_set_real_num_rx_queues set something other then
>>> 'real_num_rx_queues' does not seem right to me at least. Also
>>> netif_set_real_num_tx_queues and netif_set_real_num_rx_queues have
>>> different behavior. It would be nice if this weren't the case but
>>> they allocate queues in two places.
>> [...]
>>
>> I only did this to satisfy Eric's desire to reduce memory usage.
>> However, I believe that there are currently no drivers that dynamically
>> increase numbers of RX or TX queues. Until there are, there is not much
>> point in removing this assignment to num_rx_queues.
>>
>> Ben.
>>
>
> ixgbe increases the real_num_[rx|tx]_queues when FCoE or DCB is enabled.
> Also many of the drivers could increase the number of queues if they were
> given more interrupt vectors at some point.
If I update the handful drivers that use netif_set_real_num_rx_queues()
before the netdevice is registered to explicitly set num_rx_queues this
would address Eric's concerns and fix drivers that really only want to set
real_num_rx_queue.
Any thoughts?
-- John
^ permalink raw reply
* Re: [PATCH] SIW: Module initialization
From: Bernard Metzler @ 2010-10-06 14:11 UTC (permalink / raw)
To: Stephen Hemminger
Cc: Bart Van Assche, linux-rdma-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20101006070242.3ca5a6d9@s6510>
Stephen Hemminger <shemminger-ZtmgI6mnKB3QT0dZR+AlfA@public.gmane.org> wrote on 10/06/2010 12:02:42 AM:
> On Tue, 5 Oct 2010 12:57:21 +0200
> Bart Van Assche <bvanassche-HInyCGIudOg@public.gmane.org> wrote:
>
> > > + * TODO: Dynamic device management (network device
> registration/removal).
> >
> > The current implementation is such that one siw device is created for
> > each network device found at kernel module load time. That means that
> > you force the user to load the siw kernel module after all other
> > kernel modules that register a network device. I'm not sure that's a
> > good idea.
>
> Then device should be controlled by a netlink (rtnl_link_ops) style
> interface see vlan_netlink.c. Using netlink is extensible and provides
> a cleaner interface than all these other parameterization methods.
makes sense to me. Bart?
thanks,
bernard.
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH -next] sundance: Drop legacy _COMPAT_WITH_OLD_KERNEL includes
From: Denis Kirjanov @ 2010-10-06 13:59 UTC (permalink / raw)
To: davem; +Cc: netdev
Drop legacy includes since 2.4.x
Signed-off-by: Denis Kirjanov <dkirjanov@kernel.org>
---
drivers/net/sundance.c | 7 -------
1 files changed, 0 insertions(+), 7 deletions(-)
diff --git a/drivers/net/sundance.c b/drivers/net/sundance.c
index 8b5aeca..27d69aa 100644
--- a/drivers/net/sundance.c
+++ b/drivers/net/sundance.c
@@ -97,16 +97,9 @@ static char *media[MAX_UNITS];
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/dma-mapping.h>
-#ifndef _COMPAT_WITH_OLD_KERNEL
#include <linux/crc32.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
-#else
-#include "crc32.h"
-#include "ethtool.h"
-#include "mii.h"
-#include "compat.h"
-#endif
/* These identify the driver base version and may not be removed. */
static const char version[] __devinitconst =
--
1.7.0
^ permalink raw reply related
* [PATCH 2/2] vhost-net: batch use/unuse mm
From: Michael S. Tsirkin @ 2010-10-06 13:34 UTC (permalink / raw)
To: Krishna Kumar; +Cc: rusty, davem, kvm, arnd, netdev, avi, anthony
In-Reply-To: <cover.1286372004.git.mst@redhat.com>
Move use/unuse mm to vhost.c which makes it possible to batch these
operations.
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
drivers/vhost/net.c | 7 -------
drivers/vhost/vhost.c | 7 ++++++-
2 files changed, 6 insertions(+), 8 deletions(-)
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index 271678e..ff02ea4 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -10,7 +10,6 @@
#include <linux/eventfd.h>
#include <linux/vhost.h>
#include <linux/virtio_net.h>
-#include <linux/mmu_context.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/mutex.h>
@@ -136,7 +135,6 @@ static void handle_tx(struct vhost_net *net)
return;
}
- use_mm(net->dev.mm);
mutex_lock(&vq->mutex);
vhost_disable_notify(vq);
@@ -197,7 +195,6 @@ static void handle_tx(struct vhost_net *net)
}
mutex_unlock(&vq->mutex);
- unuse_mm(net->dev.mm);
}
static int peek_head_len(struct sock *sk)
@@ -302,7 +299,6 @@ static void handle_rx_big(struct vhost_net *net)
if (!sock || skb_queue_empty(&sock->sk->sk_receive_queue))
return;
- use_mm(net->dev.mm);
mutex_lock(&vq->mutex);
vhost_disable_notify(vq);
hdr_size = vq->vhost_hlen;
@@ -381,7 +377,6 @@ static void handle_rx_big(struct vhost_net *net)
}
mutex_unlock(&vq->mutex);
- unuse_mm(net->dev.mm);
}
/* Expects to be always run from workqueue - which acts as
@@ -413,7 +408,6 @@ static void handle_rx_mergeable(struct vhost_net *net)
if (!sock || skb_queue_empty(&sock->sk->sk_receive_queue))
return;
- use_mm(net->dev.mm);
mutex_lock(&vq->mutex);
vhost_disable_notify(vq);
vhost_hlen = vq->vhost_hlen;
@@ -490,7 +484,6 @@ static void handle_rx_mergeable(struct vhost_net *net)
}
mutex_unlock(&vq->mutex);
- unuse_mm(net->dev.mm);
}
static void handle_rx(struct vhost_net *net)
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index 8b9d474..c83d1c2 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -15,6 +15,7 @@
#include <linux/vhost.h>
#include <linux/virtio_net.h>
#include <linux/mm.h>
+#include <linux/mmu_context.h>
#include <linux/miscdevice.h>
#include <linux/mutex.h>
#include <linux/rcupdate.h>
@@ -179,6 +180,8 @@ static int vhost_worker(void *data)
unsigned uninitialized_var(seq);
int n = 0;
+ use_mm(dev->mm);
+
for (;;) {
/* mb paired w/ kthread_stop */
set_current_state(TASK_INTERRUPTIBLE);
@@ -193,7 +196,7 @@ static int vhost_worker(void *data)
if (kthread_should_stop()) {
spin_unlock_irq(&dev->work_lock);
__set_current_state(TASK_RUNNING);
- return 0;
+ break;
}
if (!list_empty(&dev->work_list)) {
work = list_first_entry(&dev->work_list,
@@ -218,6 +221,8 @@ static int vhost_worker(void *data)
}
}
+ unuse_mm(dev->mm);
+ return 0;
}
/* Helper to allocate iovec buffers for all vqs. */
--
1.7.3-rc1
^ permalink raw reply related
* [PATCH net-next 19/19] bnx2x: update version to 1.60.00-1
From: Dmitry Kravkov @ 2010-10-06 13:35 UTC (permalink / raw)
To: davem, netdev; +Cc: eilong, mchan
Signed-off-by: Dmitry Kravkov <dmitry@broadcom.com>
Signed-off-by: Eilon Greenstein <eilong@broadcom.com>
---
drivers/net/bnx2x/bnx2x.h | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/bnx2x/bnx2x.h b/drivers/net/bnx2x/bnx2x.h
index 6fc77a4..5e94446 100644
--- a/drivers/net/bnx2x/bnx2x.h
+++ b/drivers/net/bnx2x/bnx2x.h
@@ -20,8 +20,8 @@
* (you will need to reboot afterwards) */
/* #define BNX2X_STOP_ON_ERROR */
-#define DRV_MODULE_VERSION "1.52.53-7"
-#define DRV_MODULE_RELDATE "2010/09/12"
+#define DRV_MODULE_VERSION "1.60.00-1"
+#define DRV_MODULE_RELDATE "2010/10/06"
#define BNX2X_BC_VER 0x040200
#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
--
1.7.1
^ permalink raw reply related
* [PATCH net-next 18/19] bnx2x: properly initialize FW stats
From: Dmitry Kravkov @ 2010-10-06 13:35 UTC (permalink / raw)
To: davem, netdev; +Cc: eilong, mchan
Client statistics need to be initialized to -1
Signed-off-by: Dmitry Kravkov <dmitry@broadcom.com>
Signed-off-by: Eilon Greenstein <eilong@broadcom.com>
---
drivers/net/bnx2x/bnx2x_stats.c | 13 +++++++++++++
1 files changed, 13 insertions(+), 0 deletions(-)
diff --git a/drivers/net/bnx2x/bnx2x_stats.c b/drivers/net/bnx2x/bnx2x_stats.c
index 5644bdd..4733c83 100644
--- a/drivers/net/bnx2x/bnx2x_stats.c
+++ b/drivers/net/bnx2x/bnx2x_stats.c
@@ -1339,6 +1339,7 @@ void bnx2x_stats_init(struct bnx2x *bp)
int port = BP_PORT(bp);
int mb_idx = BP_FW_MB_IDX(bp);
int i;
+ struct eth_stats_query *stats = bnx2x_sp(bp, fw_stats);
bp->stats_pending = 0;
bp->executer_idx = 0;
@@ -1380,6 +1381,18 @@ void bnx2x_stats_init(struct bnx2x *bp)
memset(&fp->eth_q_stats, 0, sizeof(struct bnx2x_eth_q_stats));
}
+ for_each_queue(bp, i) {
+ /* Set initial stats counter in the stats ramrod data to -1 */
+ int cl_id = bp->fp[i].cl_id;
+
+ stats->xstorm_common.client_statistics[cl_id].
+ stats_counter = 0xffff;
+ stats->ustorm_common.client_statistics[cl_id].
+ stats_counter = 0xffff;
+ stats->tstorm_common.client_statistics[cl_id].
+ stats_counter = 0xffff;
+ }
+
memset(&bp->dev->stats, 0, sizeof(struct net_device_stats));
memset(&bp->eth_stats, 0, sizeof(struct bnx2x_eth_stats));
--
1.7.1
^ permalink raw reply related
* [PATCH net-next 17/19] bnx2x: code beautify
From: Dmitry Kravkov @ 2010-10-06 13:34 UTC (permalink / raw)
To: davem, netdev; +Cc: eilong, mchan
This patch does not include any functional changes.
The changes are: empty lines, indentation and comments.
Signed-off-by: Dmitry Kravkov <dmitry@broadcom.com>
Signed-off-by: Eilon Greenstein <eilong@broadcom.com>
---
drivers/net/bnx2x/bnx2x.h | 54 ++++----
drivers/net/bnx2x/bnx2x_cmn.c | 60 +++++----
drivers/net/bnx2x/bnx2x_cmn.h | 145 ++++++++++++++-----
drivers/net/bnx2x/bnx2x_ethtool.c | 5 +-
drivers/net/bnx2x/bnx2x_main.c | 275 +++++++++++++++++++------------------
drivers/net/bnx2x/bnx2x_stats.c | 4 +-
drivers/net/bnx2x/bnx2x_stats.h | 8 +-
7 files changed, 318 insertions(+), 233 deletions(-)
diff --git a/drivers/net/bnx2x/bnx2x.h b/drivers/net/bnx2x/bnx2x.h
index d80809f..6fc77a4 100644
--- a/drivers/net/bnx2x/bnx2x.h
+++ b/drivers/net/bnx2x/bnx2x.h
@@ -180,13 +180,14 @@ void bnx2x_panic_dump(struct bnx2x *bp);
#define SHMEM2_WR(bp, field, val) REG_WR(bp, SHMEM2_ADDR(bp, field), val)
#define MF_CFG_ADDR(bp, field) (bp->common.mf_cfg_base + \
offsetof(struct mf_cfg, field))
-#define MF2_CFG_ADDR(bp, field) (bp->common.mf2_cfg_base + \
+#define MF2_CFG_ADDR(bp, field) (bp->common.mf2_cfg_base + \
offsetof(struct mf2_cfg, field))
#define MF_CFG_RD(bp, field) REG_RD(bp, MF_CFG_ADDR(bp, field))
#define MF_CFG_WR(bp, field, val) REG_WR(bp,\
MF_CFG_ADDR(bp, field), (val))
#define MF2_CFG_RD(bp, field) REG_RD(bp, MF2_CFG_ADDR(bp, field))
+
#define SHMEM2_HAS(bp, field) ((bp)->common.shmem2_base && \
(SHMEM2_RD((bp), size) > \
offsetof(struct shmem2_region, field)))
@@ -310,7 +311,7 @@ struct bnx2x_fastpath {
#define BNX2X_NAPI_WEIGHT 128
struct napi_struct napi;
- union host_hc_status_block status_blk;
+ union host_hc_status_block status_blk;
/* chip independed shortcuts into sb structure */
__le16 *sb_index_values;
__le16 *sb_running_index;
@@ -349,8 +350,8 @@ struct bnx2x_fastpath {
#define BNX2X_FP_STATE_TERMINATING 0xd0000
#define BNX2X_FP_STATE_TERMINATED 0xe0000
- u8 index; /* number in fp array */
- u8 cl_id; /* eth client id */
+ u8 index; /* number in fp array */
+ u8 cl_id; /* eth client id */
u8 cl_qzone_id;
u8 fw_sb_id; /* status block number in FW */
u8 igu_sb_id; /* status block number in HW */
@@ -375,8 +376,6 @@ struct bnx2x_fastpath {
u16 last_max_sge;
__le16 *rx_cons_sb;
-
-
unsigned long tx_pkt,
rx_pkt,
rx_calls;
@@ -977,7 +976,7 @@ struct bnx2x {
u32 mf2_config[E2_FUNC_MAX];
u16 mf_ov;
u8 mf_mode;
-#define IS_MF(bp) (bp->mf_mode != 0)
+#define IS_MF(bp) (bp->mf_mode != 0)
u8 wol;
@@ -1302,21 +1301,35 @@ struct bnx2x_func_init_params {
for (var = 1; var < BNX2X_NUM_QUEUES(bp); var++)
+#define WAIT_RAMROD_POLL 0x01
+#define WAIT_RAMROD_COMMON 0x02
+int bnx2x_wait_ramrod(struct bnx2x *bp, int state, int idx,
+ int *state_p, int flags);
+
+/* dmae */
void bnx2x_read_dmae(struct bnx2x *bp, u32 src_addr, u32 len32);
void bnx2x_write_dmae(struct bnx2x *bp, dma_addr_t dma_addr, u32 dst_addr,
u32 len32);
+void bnx2x_write_dmae_phys_len(struct bnx2x *bp, dma_addr_t phys_addr,
+ u32 addr, u32 len);
+void bnx2x_post_dmae(struct bnx2x *bp, struct dmae_command *dmae, int idx);
+u32 bnx2x_dmae_opcode_add_comp(u32 opcode, u8 comp_type);
+u32 bnx2x_dmae_opcode_clr_src_reset(u32 opcode);
+u32 bnx2x_dmae_opcode(struct bnx2x *bp, u8 src_type, u8 dst_type,
+ bool with_comp, u8 comp_type);
+
int bnx2x_get_gpio(struct bnx2x *bp, int gpio_num, u8 port);
int bnx2x_set_gpio(struct bnx2x *bp, int gpio_num, u32 mode, u8 port);
int bnx2x_set_gpio_int(struct bnx2x *bp, int gpio_num, u32 mode, u8 port);
u32 bnx2x_fw_command(struct bnx2x *bp, u32 command, u32 param);
void bnx2x_reg_wr_ind(struct bnx2x *bp, u32 addr, u32 val);
-void bnx2x_write_dmae_phys_len(struct bnx2x *bp, dma_addr_t phys_addr,
- u32 addr, u32 len);
+
void bnx2x_calc_fc_adv(struct bnx2x *bp);
int bnx2x_sp_post(struct bnx2x *bp, int command, int cid,
u32 data_hi, u32 data_lo, int common);
void bnx2x_update_coalesce(struct bnx2x *bp);
int bnx2x_get_link_cfg_idx(struct bnx2x *bp);
+
static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms,
int wait)
{
@@ -1333,6 +1346,7 @@ static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms,
return val;
}
+
#define BNX2X_ILT_ZALLOC(x, y, size) \
do { \
x = pci_alloc_consistent(bp->pdev, size, y); \
@@ -1353,6 +1367,8 @@ static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms,
#define ILT_NUM_PAGE_ENTRIES (3072)
/* In 57710/11 we use whole table since we have 8 func
+ * In 57712 we have only 4 func, but use same size per func, then only half of
+ * the table in use
*/
#define ILT_PER_FUNC (ILT_NUM_PAGE_ENTRIES/8)
@@ -1366,14 +1382,13 @@ static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms,
#define ONCHIP_ADDR1(x) ((u32)(((u64)x >> 12) & 0xFFFFFFFF))
#define ONCHIP_ADDR2(x) ((u32)((1 << 20) | ((u64)x >> 44)))
-
/* load/unload mode */
#define LOAD_NORMAL 0
#define LOAD_OPEN 1
#define LOAD_DIAG 2
#define UNLOAD_NORMAL 0
#define UNLOAD_CLOSE 1
-#define UNLOAD_RECOVERY 2
+#define UNLOAD_RECOVERY 2
/* DMAE command defines */
@@ -1447,7 +1462,6 @@ static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms,
#define PMF_DMAE_C(bp) (BP_PORT(bp) * MAX_DMAE_C_PER_PORT + \
E1HVN_MAX)
-
/* PCIE link and speed */
#define PCICFG_LINK_WIDTH 0x1f00000
#define PCICFG_LINK_WIDTH_SHIFT 20
@@ -1596,6 +1610,7 @@ static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms,
#define BNX2X_SP_DSB_INDEX \
(&bp->def_status_blk->sp_sb.\
index_values[HC_SP_INDEX_ETH_DEF_CONS])
+
#define SET_FLAG(value, mask, flag) \
do {\
(value) &= ~(mask);\
@@ -1630,6 +1645,7 @@ static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms,
#ifndef ETH_MAX_RX_CLIENTS_E2
#define ETH_MAX_RX_CLIENTS_E2 ETH_MAX_RX_CLIENTS_E1H
#endif
+
#define BNX2X_VPD_LEN 128
#define VENDOR_ID_LEN 4
@@ -1649,20 +1665,6 @@ static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms,
BNX2X_EXTERN int load_count[2][3]; /* per path: 0-common, 1-port0, 2-port1 */
-/* MISC_REG_RESET_REG - this is here for the hsi to work don't touch */
-
extern void bnx2x_set_ethtool_ops(struct net_device *netdev);
-void bnx2x_post_dmae(struct bnx2x *bp, struct dmae_command *dmae, int idx);
-u32 bnx2x_dmae_opcode_add_comp(u32 opcode, u8 comp_type);
-u32 bnx2x_dmae_opcode_clr_src_reset(u32 opcode);
-u32 bnx2x_dmae_opcode(struct bnx2x *bp, u8 src_type, u8 dst_type,
- bool with_comp, u8 comp_type);
-
-
-#define WAIT_RAMROD_POLL 0x01
-#define WAIT_RAMROD_COMMON 0x02
-
-int bnx2x_wait_ramrod(struct bnx2x *bp, int state, int idx,
- int *state_p, int flags);
#endif /* bnx2x.h */
diff --git a/drivers/net/bnx2x/bnx2x_cmn.c b/drivers/net/bnx2x/bnx2x_cmn.c
index a43cbc4..b5e15cf 100644
--- a/drivers/net/bnx2x/bnx2x_cmn.c
+++ b/drivers/net/bnx2x/bnx2x_cmn.c
@@ -15,7 +15,6 @@
*
*/
-
#include <linux/etherdevice.h>
#include <linux/ip.h>
#include <net/ipv6.h>
@@ -136,7 +135,6 @@ int bnx2x_tx_int(struct bnx2x_fastpath *fp)
*/
smp_mb();
- /* TBD need a thresh? */
if (unlikely(netif_tx_queue_stopped(txq))) {
/* Taking tx_lock() is needed to prevent reenabling the queue
* while it's empty. This could have happen if rx_action() gets
@@ -623,6 +621,7 @@ reuse_rx:
bnx2x_set_skb_rxhash(bp, cqe, skb);
skb_checksum_none_assert(skb);
+
if (bp->rx_csum) {
if (likely(BNX2X_RX_CSUM_OK(cqe)))
skb->ip_summed = CHECKSUM_UNNECESSARY;
@@ -704,7 +703,6 @@ static irqreturn_t bnx2x_msix_fp_int(int irq, void *fp_cookie)
return IRQ_HANDLED;
}
-
/* HW Lock for shared dual port PHYs */
void bnx2x_acquire_phy_lock(struct bnx2x *bp)
{
@@ -916,6 +914,7 @@ void bnx2x_init_rx_rings(struct bnx2x *bp)
}
}
}
+
static void bnx2x_free_tx_skbs(struct bnx2x *bp)
{
int i;
@@ -1185,6 +1184,7 @@ void bnx2x_set_num_queues(struct bnx2x *bp)
case ETH_RSS_MODE_REGULAR:
bp->num_queues = bnx2x_calc_num_queues(bp);
break;
+
default:
bp->num_queues = 1;
break;
@@ -1347,6 +1347,7 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode)
/* Enable Timer scan */
REG_WR(bp, TM_REG_EN_LINEAR0_TIMER + BP_PORT(bp)*4, 1);
#endif
+
for_each_nondefault_queue(bp, i) {
rc = bnx2x_setup_client(bp, &bp->fp[i], 0);
if (rc)
@@ -1465,11 +1466,13 @@ int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode)
/* Stop Tx */
bnx2x_tx_disable(bp);
+
del_timer_sync(&bp->timer);
+
SHMEM_WR(bp, func_mb[BP_FW_MB_IDX(bp)].drv_pulse_mb,
(DRV_PULSE_ALWAYS_ALIVE | bp->fw_drv_pulse_wr_seq));
- bnx2x_stats_handle(bp, STATS_EVENT_STOP);
+ bnx2x_stats_handle(bp, STATS_EVENT_STOP);
/* Cleanup the chip if needed */
if (unload_mode != UNLOAD_RECOVERY)
@@ -1506,6 +1509,7 @@ int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode)
return 0;
}
+
int bnx2x_set_power_state(struct bnx2x *bp, pci_power_t state)
{
u16 pmcsr;
@@ -1552,12 +1556,9 @@ int bnx2x_set_power_state(struct bnx2x *bp, pci_power_t state)
return 0;
}
-
-
/*
* net_device service functions
*/
-
int bnx2x_poll(struct napi_struct *napi, int budget)
{
int work_done = 0;
@@ -1587,19 +1588,19 @@ int bnx2x_poll(struct napi_struct *napi, int budget)
/* Fall out from the NAPI loop if needed */
if (!(bnx2x_has_rx_work(fp) || bnx2x_has_tx_work(fp))) {
bnx2x_update_fpsb_idx(fp);
- /* bnx2x_has_rx_work() reads the status block,
- * thus we need to ensure that status block indices
- * have been actually read (bnx2x_update_fpsb_idx)
- * prior to this check (bnx2x_has_rx_work) so that
- * we won't write the "newer" value of the status block
- * to IGU (if there was a DMA right after
- * bnx2x_has_rx_work and if there is no rmb, the memory
- * reading (bnx2x_update_fpsb_idx) may be postponed
- * to right before bnx2x_ack_sb). In this case there
- * will never be another interrupt until there is
- * another update of the status block, while there
- * is still unhandled work.
- */
+ /* bnx2x_has_rx_work() reads the status block,
+ * thus we need to ensure that status block indices
+ * have been actually read (bnx2x_update_fpsb_idx)
+ * prior to this check (bnx2x_has_rx_work) so that
+ * we won't write the "newer" value of the status block
+ * to IGU (if there was a DMA right after
+ * bnx2x_has_rx_work and if there is no rmb, the memory
+ * reading (bnx2x_update_fpsb_idx) may be postponed
+ * to right before bnx2x_ack_sb). In this case there
+ * will never be another interrupt until there is
+ * another update of the status block, while there
+ * is still unhandled work.
+ */
rmb();
if (!(bnx2x_has_rx_work(fp) || bnx2x_has_tx_work(fp))) {
@@ -1618,7 +1619,6 @@ int bnx2x_poll(struct napi_struct *napi, int budget)
return work_done;
}
-
/* we split the first BD into headers and data BDs
* to ease the pain of our fellow microcode engineers
* we use one mapping for both BDs
@@ -1834,6 +1834,7 @@ static inline void bnx2x_set_pbd_gso(struct sk_buff *skb,
pbd->global_data |= ETH_TX_PARSE_BD_E1X_PSEUDO_CS_WITHOUT_LEN;
}
+
/**
*
* @param skb
@@ -1906,6 +1907,7 @@ static inline u8 bnx2x_set_pbd_csum(struct bnx2x *bp, struct sk_buff *skb,
return hlen;
}
+
/* called with netif_tx_lock
* bnx2x_tx_int() runs without netif_tx_lock unless it needs to call
* netif_wake_queue()
@@ -1995,13 +1997,11 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
tx_start_bd = &fp->tx_desc_ring[bd_prod].start_bd;
tx_start_bd->bd_flags.as_bitfield = ETH_TX_BD_FLAGS_START_BD;
- SET_FLAG(tx_start_bd->general_data,
- ETH_TX_START_BD_ETH_ADDR_TYPE,
- mac_type);
+ SET_FLAG(tx_start_bd->general_data, ETH_TX_START_BD_ETH_ADDR_TYPE,
+ mac_type);
+
/* header nbd */
- SET_FLAG(tx_start_bd->general_data,
- ETH_TX_START_BD_HDR_NBDS,
- 1);
+ SET_FLAG(tx_start_bd->general_data, ETH_TX_START_BD_HDR_NBDS, 1);
/* remember the first BD of the packet */
tx_buf->first_bd = fp->tx_bd_prod;
@@ -2057,9 +2057,11 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
}
+ /* Map skb linear data for DMA */
mapping = dma_map_single(&bp->pdev->dev, skb->data,
skb_headlen(skb), DMA_TO_DEVICE);
+ /* Setup the data pointer of the first BD of the packet */
tx_start_bd->addr_hi = cpu_to_le32(U64_HI(mapping));
tx_start_bd->addr_lo = cpu_to_le32(U64_LO(mapping));
nbd = skb_shinfo(skb)->nr_frags + 2; /* start_bd + pbd + frags */
@@ -2093,6 +2095,7 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
}
tx_data_bd = (struct eth_tx_bd *)tx_start_bd;
+ /* Handle fragmented skb */
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
@@ -2157,6 +2160,7 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
fp->tx_db.data.prod += nbd;
barrier();
+
DOORBELL(bp, fp->cid, fp->tx_db.raw);
mmiowb();
@@ -2179,6 +2183,7 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_OK;
}
+
/* called with rtnl_lock */
int bnx2x_change_mac_addr(struct net_device *dev, void *p)
{
@@ -2311,6 +2316,7 @@ void bnx2x_vlan_rx_register(struct net_device *dev,
}
#endif
+
int bnx2x_suspend(struct pci_dev *pdev, pm_message_t state)
{
struct net_device *dev = pci_get_drvdata(pdev);
diff --git a/drivers/net/bnx2x/bnx2x_cmn.h b/drivers/net/bnx2x/bnx2x_cmn.h
index 1d9686e..7f52cec 100644
--- a/drivers/net/bnx2x/bnx2x_cmn.h
+++ b/drivers/net/bnx2x/bnx2x_cmn.h
@@ -64,6 +64,15 @@ u8 bnx2x_link_test(struct bnx2x *bp, u8 is_serdes);
void bnx2x__link_status_update(struct bnx2x *bp);
/**
+ * Report link status to upper layer
+ *
+ * @param bp
+ *
+ * @return int
+ */
+void bnx2x_link_report(struct bnx2x *bp);
+
+/**
* MSI-X slowpath interrupt handler
*
* @param irq
@@ -234,7 +243,7 @@ int bnx2x_release_hw_lock(struct bnx2x *bp, u32 resource);
/**
* Configure eth MAC address in the HW according to the value in
- * netdev->dev_addr for 57711
+ * netdev->dev_addr.
*
* @param bp driver handle
* @param set
@@ -270,10 +279,11 @@ void bnx2x_init_sb(struct bnx2x *bp, dma_addr_t mapping, int vfid,
u8 vf_valid, int fw_sb_id, int igu_sb_id);
/**
- * Reconfigure FW/HW according to dev->flags rx mode
+ * Set MAC filtering configurations.
*
- * @param dev net_device
+ * @remarks called with netif_tx_lock from dev_mcast.c
*
+ * @param dev net_device
*/
void bnx2x_set_rx_mode(struct net_device *dev);
@@ -295,17 +305,17 @@ void bnx2x_disable_close_the_gate(struct bnx2x *bp);
* Perform statistics handling according to event
*
* @param bp driver handle
- * @param even tbnx2x_stats_event
+ * @param event bnx2x_stats_event
*/
void bnx2x_stats_handle(struct bnx2x *bp, enum bnx2x_stats_event event);
/**
- * Handle sp events
+ * Handle ramrods completion
*
* @param fp fastpath handle for the event
* @param rr_cqe eth_rx_cqe
*/
-void bnx2x_sp_event(struct bnx2x_fastpath *fp, union eth_rx_cqe *rr_cqe);
+void bnx2x_sp_event(struct bnx2x_fastpath *fp, union eth_rx_cqe *rr_cqe);
/**
* Init/halt function before/after sending
@@ -327,6 +337,46 @@ int bnx2x_func_stop(struct bnx2x *bp);
void bnx2x_ilt_set_info(struct bnx2x *bp);
/**
+ * Set power state to the requested value. Currently only D0 and
+ * D3hot are supported.
+ *
+ * @param bp
+ * @param state D0 or D3hot
+ *
+ * @return int
+ */
+int bnx2x_set_power_state(struct bnx2x *bp, pci_power_t state);
+
+/* dev_close main block */
+int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode);
+
+/* dev_open main block */
+int bnx2x_nic_load(struct bnx2x *bp, int load_mode);
+
+/* hard_xmit callback */
+netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev);
+
+int bnx2x_change_mac_addr(struct net_device *dev, void *p);
+
+/* NAPI poll Rx part */
+int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget);
+
+/* NAPI poll Tx part */
+int bnx2x_tx_int(struct bnx2x_fastpath *fp);
+
+/* suspend/resume callbacks */
+int bnx2x_suspend(struct pci_dev *pdev, pm_message_t state);
+int bnx2x_resume(struct pci_dev *pdev);
+
+/* Release IRQ vectors */
+void bnx2x_free_irq(struct bnx2x *bp);
+
+void bnx2x_init_rx_rings(struct bnx2x *bp);
+void bnx2x_free_skbs(struct bnx2x *bp);
+void bnx2x_netif_stop(struct bnx2x *bp, int disable_hw);
+void bnx2x_netif_start(struct bnx2x *bp);
+
+/**
* Fill msix_table, request vectors, update num_queues according
* to number of available vectors
*
@@ -362,6 +412,51 @@ int bnx2x_setup_irqs(struct bnx2x *bp);
* @return int
*/
int bnx2x_poll(struct napi_struct *napi, int budget);
+
+/**
+ * Allocate/release memories outsize main driver structure
+ *
+ * @param bp
+ *
+ * @return int
+ */
+int __devinit bnx2x_alloc_mem_bp(struct bnx2x *bp);
+void bnx2x_free_mem_bp(struct bnx2x *bp);
+
+/**
+ * Change mtu netdev callback
+ *
+ * @param dev
+ * @param new_mtu
+ *
+ * @return int
+ */
+int bnx2x_change_mtu(struct net_device *dev, int new_mtu);
+
+/**
+ * tx timeout netdev callback
+ *
+ * @param dev
+ * @param new_mtu
+ *
+ * @return int
+ */
+void bnx2x_tx_timeout(struct net_device *dev);
+
+#ifdef BCM_VLAN
+/**
+ * vlan rx register netdev callback
+ *
+ * @param dev
+ * @param new_mtu
+ *
+ * @return int
+ */
+void bnx2x_vlan_rx_register(struct net_device *dev,
+ struct vlan_group *vlgrp);
+
+#endif
+
static inline void bnx2x_update_fpsb_idx(struct bnx2x_fastpath *fp)
{
barrier(); /* status block is written to by the chip */
@@ -558,9 +653,6 @@ static inline u16 bnx2x_ack_int(struct bnx2x *bp)
return bnx2x_igu_ack_int(bp);
}
-/*
- * fast path service functions
- */
static inline int bnx2x_has_tx_work_unload(struct bnx2x_fastpath *fp)
{
/* Tell compiler that consumer and producer can change */
@@ -611,6 +703,7 @@ static inline int bnx2x_has_rx_work(struct bnx2x_fastpath *fp)
rx_cons_sb++;
return (fp->rx_comp_cons != rx_cons_sb);
}
+
/**
* disables tx from stack point of view
*
@@ -731,6 +824,7 @@ static inline int bnx2x_alloc_rx_sge(struct bnx2x *bp,
return 0;
}
+
static inline int bnx2x_alloc_rx_skb(struct bnx2x *bp,
struct bnx2x_fastpath *fp, u16 index)
{
@@ -782,6 +876,7 @@ static inline void bnx2x_reuse_rx_skb(struct bnx2x_fastpath *fp,
dma_unmap_addr(cons_rx_buf, mapping));
*prod_bd = *cons_bd;
}
+
static inline void bnx2x_free_rx_sge_range(struct bnx2x *bp,
struct bnx2x_fastpath *fp, int last)
{
@@ -846,6 +941,7 @@ static inline void bnx2x_init_tx_rings(struct bnx2x *bp)
fp->tx_pkt = 0;
}
}
+
static inline void bnx2x_set_next_page_rx_bd(struct bnx2x_fastpath *fp)
{
int i;
@@ -931,40 +1027,11 @@ static inline void storm_memset_cmng(struct bnx2x *bp,
__storm_memset_struct(bp, addr, size, (u32 *)cmng);
}
+
/* HW Lock for shared dual port PHYs */
void bnx2x_acquire_phy_lock(struct bnx2x *bp);
void bnx2x_release_phy_lock(struct bnx2x *bp);
-void bnx2x_link_report(struct bnx2x *bp);
-int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget);
-int bnx2x_tx_int(struct bnx2x_fastpath *fp);
-void bnx2x_init_rx_rings(struct bnx2x *bp);
-netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev);
-
-int bnx2x_change_mac_addr(struct net_device *dev, void *p);
-void bnx2x_tx_timeout(struct net_device *dev);
-void bnx2x_vlan_rx_register(struct net_device *dev, struct vlan_group *vlgrp);
-void bnx2x_netif_start(struct bnx2x *bp);
-void bnx2x_netif_stop(struct bnx2x *bp, int disable_hw);
-void bnx2x_free_irq(struct bnx2x *bp);
-int bnx2x_suspend(struct pci_dev *pdev, pm_message_t state);
-int bnx2x_resume(struct pci_dev *pdev);
-void bnx2x_free_skbs(struct bnx2x *bp);
-int bnx2x_change_mtu(struct net_device *dev, int new_mtu);
-int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode);
-int bnx2x_nic_load(struct bnx2x *bp, int load_mode);
-int bnx2x_set_power_state(struct bnx2x *bp, pci_power_t state);
-
-/**
- * Allocate/release memories outsize main driver structure
- *
- * @param bp
- *
- * @return int
- */
-int __devinit bnx2x_alloc_mem_bp(struct bnx2x *bp);
-void bnx2x_free_mem_bp(struct bnx2x *bp);
-
#define BNX2X_FW_IP_HDR_ALIGN_PAD 2 /* FW places hdr with this padding */
#endif /* BNX2X_CMN_H */
diff --git a/drivers/net/bnx2x/bnx2x_ethtool.c b/drivers/net/bnx2x/bnx2x_ethtool.c
index 8fb0027..54fe061 100644
--- a/drivers/net/bnx2x/bnx2x_ethtool.c
+++ b/drivers/net/bnx2x/bnx2x_ethtool.c
@@ -25,7 +25,6 @@
#include "bnx2x_cmn.h"
#include "bnx2x_dump.h"
-
static int bnx2x_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct bnx2x *bp = netdev_priv(dev);
@@ -963,6 +962,7 @@ static int bnx2x_set_eeprom(struct net_device *dev,
return rc;
}
+
static int bnx2x_get_coalesce(struct net_device *dev,
struct ethtool_coalesce *coal)
{
@@ -1288,6 +1288,7 @@ static int bnx2x_test_registers(struct bnx2x *bp)
save_val = REG_RD(bp, offset);
REG_WR(bp, offset, (wr_val & mask));
+
val = REG_RD(bp, offset);
/* Restore the original register's value */
@@ -1471,6 +1472,7 @@ static int bnx2x_run_loopback(struct bnx2x *bp, int loopback_mode, u8 link_up)
/* turn on parsing and get a BD */
bd_prod = TX_BD(NEXT_TX_IDX(bd_prod));
+
pbd_e1x = &fp_tx->tx_desc_ring[bd_prod].parse_bd_e1x;
pbd_e2 = &fp_tx->tx_desc_ring[bd_prod].parse_bd_e2;
@@ -1714,6 +1716,7 @@ static void bnx2x_self_test(struct net_device *dev,
buf[1] = 1;
etest->flags |= ETH_TEST_FL_FAILED;
}
+
buf[2] = bnx2x_test_loopback(bp, link_up);
if (buf[2] != 0)
etest->flags |= ETH_TEST_FL_FAILED;
diff --git a/drivers/net/bnx2x/bnx2x_main.c b/drivers/net/bnx2x/bnx2x_main.c
index dcc4900..46210ed 100644
--- a/drivers/net/bnx2x/bnx2x_main.c
+++ b/drivers/net/bnx2x/bnx2x_main.c
@@ -56,7 +56,6 @@
#include "bnx2x_init_ops.h"
#include "bnx2x_cmn.h"
-
#include <linux/firmware.h>
#include "bnx2x_fw_file_hdr.h"
/* FW files */
@@ -1325,7 +1324,6 @@ static bool bnx2x_trylock_hw_lock(struct bnx2x *bp, u32 resource)
return false;
}
-
#ifdef BCM_CNIC
static void bnx2x_cnic_cfc_comp(struct bnx2x *bp, int cid);
#endif
@@ -1754,12 +1752,12 @@ void bnx2x_calc_fc_adv(struct bnx2x *bp)
MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_MASK) {
case MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_NONE:
bp->port.advertising[cfg_idx] &= ~(ADVERTISED_Asym_Pause |
- ADVERTISED_Pause);
+ ADVERTISED_Pause);
break;
case MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH:
bp->port.advertising[cfg_idx] |= (ADVERTISED_Asym_Pause |
- ADVERTISED_Pause);
+ ADVERTISED_Pause);
break;
case MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC:
@@ -1768,12 +1766,11 @@ void bnx2x_calc_fc_adv(struct bnx2x *bp)
default:
bp->port.advertising[cfg_idx] &= ~(ADVERTISED_Asym_Pause |
- ADVERTISED_Pause);
+ ADVERTISED_Pause);
break;
}
}
-
u8 bnx2x_initial_phy_init(struct bnx2x *bp, int load_mode)
{
if (!BP_NOMCP(bp)) {
@@ -1952,6 +1949,7 @@ static void bnx2x_init_vn_minmax(struct bnx2x *bp, int vn)
vn_max_rate = ((vn_cfg & FUNC_MF_CFG_MAX_BW_MASK) >>
FUNC_MF_CFG_MAX_BW_SHIFT) * 100;
}
+
DP(NETIF_MSG_IFUP,
"func %d: vn_min_rate %d vn_max_rate %d vn_weight_sum %d\n",
func, vn_min_rate, vn_max_rate, bp->vn_weight_sum);
@@ -1991,6 +1989,7 @@ static void bnx2x_init_vn_minmax(struct bnx2x *bp, int vn)
XSTORM_FAIRNESS_PER_VN_VARS_OFFSET(func) + i * 4,
((u32 *)(&m_fair_vn))[i]);
}
+
static int bnx2x_get_cmng_fns_mode(struct bnx2x *bp)
{
if (CHIP_REV_IS_SLOW(bp))
@@ -2625,13 +2624,13 @@ static inline void bnx2x_sp_prod_update(struct bnx2x *bp)
wmb();
REG_WR16(bp, BAR_XSTRORM_INTMEM + XSTORM_SPQ_PROD_OFFSET(func),
- bp->spq_prod_idx);
+ bp->spq_prod_idx);
mmiowb();
}
/* the slow path queue is odd since completions arrive on the fastpath ring */
int bnx2x_sp_post(struct bnx2x *bp, int command, int cid,
- u32 data_hi, u32 data_lo, int common)
+ u32 data_hi, u32 data_lo, int common)
{
struct eth_spe *spe;
u16 type;
@@ -3055,6 +3054,7 @@ static inline void bnx2x_attn_int_deasserted3(struct bnx2x *bp, u32 attn)
#define RESET_DONE_FLAG_MASK (~LOAD_COUNTER_MASK)
#define RESET_DONE_FLAG_SHIFT LOAD_COUNTER_BITS
#define CHIP_PARITY_SUPPORTED(bp) (CHIP_IS_E1(bp) || CHIP_IS_E1H(bp))
+
/*
* should be run under rtnl lock
*/
@@ -4376,7 +4376,6 @@ gunzip_nomem1:
static void bnx2x_gunzip_end(struct bnx2x *bp)
{
kfree(bp->strm->workspace);
-
kfree(bp->strm);
bp->strm = NULL;
@@ -4641,6 +4640,7 @@ static void enable_blocks_attention(struct bnx2x *bp)
REG_WR(bp, CCM_REG_CCM_INT_MASK, 0);
/* REG_WR(bp, CSEM_REG_CSEM_INT_MASK_0, 0); */
/* REG_WR(bp, CSEM_REG_CSEM_INT_MASK_1, 0); */
+
if (CHIP_REV_IS_FPGA(bp))
REG_WR(bp, PXP2_REG_PXP2_INT_MASK_0, 0x580000);
else if (CHIP_IS_E2(bp))
@@ -4672,29 +4672,29 @@ static const struct {
{PXP2_REG_PXP2_PRTY_MASK_1, 0x7f},
{HC_REG_HC_PRTY_MASK, 0x7},
{MISC_REG_MISC_PRTY_MASK, 0x1},
- {QM_REG_QM_PRTY_MASK, 0x0},
- {DORQ_REG_DORQ_PRTY_MASK, 0x0},
+ {QM_REG_QM_PRTY_MASK, 0x0},
+ {DORQ_REG_DORQ_PRTY_MASK, 0x0},
{GRCBASE_UPB + PB_REG_PB_PRTY_MASK, 0x0},
{GRCBASE_XPB + PB_REG_PB_PRTY_MASK, 0x0},
- {SRC_REG_SRC_PRTY_MASK, 0x4}, /* bit 2 */
- {CDU_REG_CDU_PRTY_MASK, 0x0},
- {CFC_REG_CFC_PRTY_MASK, 0x0},
- {DBG_REG_DBG_PRTY_MASK, 0x0},
- {DMAE_REG_DMAE_PRTY_MASK, 0x0},
- {BRB1_REG_BRB1_PRTY_MASK, 0x0},
- {PRS_REG_PRS_PRTY_MASK, (1<<6)},/* bit 6 */
- {TSDM_REG_TSDM_PRTY_MASK, 0x18},/* bit 3,4 */
- {CSDM_REG_CSDM_PRTY_MASK, 0x8}, /* bit 3 */
- {USDM_REG_USDM_PRTY_MASK, 0x38},/* bit 3,4,5 */
- {XSDM_REG_XSDM_PRTY_MASK, 0x8}, /* bit 3 */
- {TSEM_REG_TSEM_PRTY_MASK_0, 0x0},
- {TSEM_REG_TSEM_PRTY_MASK_1, 0x0},
- {USEM_REG_USEM_PRTY_MASK_0, 0x0},
- {USEM_REG_USEM_PRTY_MASK_1, 0x0},
- {CSEM_REG_CSEM_PRTY_MASK_0, 0x0},
- {CSEM_REG_CSEM_PRTY_MASK_1, 0x0},
- {XSEM_REG_XSEM_PRTY_MASK_0, 0x0},
- {XSEM_REG_XSEM_PRTY_MASK_1, 0x0}
+ {SRC_REG_SRC_PRTY_MASK, 0x4}, /* bit 2 */
+ {CDU_REG_CDU_PRTY_MASK, 0x0},
+ {CFC_REG_CFC_PRTY_MASK, 0x0},
+ {DBG_REG_DBG_PRTY_MASK, 0x0},
+ {DMAE_REG_DMAE_PRTY_MASK, 0x0},
+ {BRB1_REG_BRB1_PRTY_MASK, 0x0},
+ {PRS_REG_PRS_PRTY_MASK, (1<<6)},/* bit 6 */
+ {TSDM_REG_TSDM_PRTY_MASK, 0x18}, /* bit 3,4 */
+ {CSDM_REG_CSDM_PRTY_MASK, 0x8}, /* bit 3 */
+ {USDM_REG_USDM_PRTY_MASK, 0x38}, /* bit 3,4,5 */
+ {XSDM_REG_XSDM_PRTY_MASK, 0x8}, /* bit 3 */
+ {TSEM_REG_TSEM_PRTY_MASK_0, 0x0},
+ {TSEM_REG_TSEM_PRTY_MASK_1, 0x0},
+ {USEM_REG_USEM_PRTY_MASK_0, 0x0},
+ {USEM_REG_USEM_PRTY_MASK_1, 0x0},
+ {CSEM_REG_CSEM_PRTY_MASK_0, 0x0},
+ {CSEM_REG_CSEM_PRTY_MASK_1, 0x0},
+ {XSEM_REG_XSEM_PRTY_MASK_0, 0x0},
+ {XSEM_REG_XSEM_PRTY_MASK_1, 0x0}
};
static void enable_blocks_parity(struct bnx2x *bp)
@@ -4906,7 +4906,6 @@ static int bnx2x_init_hw_common(struct bnx2x *bp, u32 load_code)
bnx2x_ilt_init_page_size(bp, INITOP_SET);
-
if (CHIP_REV_IS_FPGA(bp) && CHIP_IS_E1H(bp))
REG_WR(bp, PXP2_REG_PGL_TAGS_LIMIT, 0x1);
@@ -5003,6 +5002,7 @@ static int bnx2x_init_hw_common(struct bnx2x *bp, u32 load_code)
if (CHIP_MODE_IS_4_PORT(bp))
bnx2x_init_block(bp, QM_4PORT_BLOCK, COMMON_STAGE);
+
/* QM queues pointers table */
bnx2x_qm_init_ptr_table(bp, bp->qm_cid_count, INITOP_SET);
@@ -5036,6 +5036,7 @@ static int bnx2x_init_hw_common(struct bnx2x *bp, u32 load_code)
#endif
if (!CHIP_IS_E1(bp))
REG_WR(bp, PRS_REG_E1HOV_MODE, IS_MF(bp));
+
if (CHIP_IS_E2(bp)) {
/* Bit-map indicating which L2 hdrs may appear after the
basic Ethernet header */
@@ -5081,6 +5082,7 @@ static int bnx2x_init_hw_common(struct bnx2x *bp, u32 load_code)
REG_WR(bp, SRC_REG_SOFT_RST, 1);
for (i = SRC_REG_KEYRSS0_0; i <= SRC_REG_KEYRSS1_9; i += 4)
REG_WR(bp, i, random32());
+
bnx2x_init_block(bp, SRCH_BLOCK, COMMON_STAGE);
#ifdef BCM_CNIC
REG_WR(bp, SRC_REG_KEYSEARCH_0, 0x63285672);
@@ -5467,6 +5469,7 @@ static int bnx2x_init_hw_func(struct bnx2x *bp)
set the size */
}
bnx2x_ilt_init_op(bp, INITOP_SET);
+
#ifdef BCM_CNIC
bnx2x_src_init_t2(bp, bp->t2, bp->t2_mapping, SRC_CONN_NUM);
@@ -5692,6 +5695,7 @@ static int bnx2x_init_hw_func(struct bnx2x *bp)
bnx2x_init_block(bp, DMAE_BLOCK, FUNC0_STAGE + func);
bnx2x_phy_probe(&bp->link_params);
+
return 0;
}
@@ -5826,6 +5830,7 @@ void bnx2x_free_mem(struct bnx2x *bp)
bnx2x_ilt_mem_op(bp, ILT_MEMOP_FREE);
BNX2X_FREE(bp->ilt->lines);
+
#ifdef BCM_CNIC
if (CHIP_IS_E2(bp))
BNX2X_PCI_FREE(bp->cnic_sb.e2_sb, bp->cnic_sb_mapping,
@@ -5833,8 +5838,10 @@ void bnx2x_free_mem(struct bnx2x *bp)
else
BNX2X_PCI_FREE(bp->cnic_sb.e1x_sb, bp->cnic_sb_mapping,
sizeof(struct host_hc_status_block_e1x));
+
BNX2X_PCI_FREE(bp->t2, bp->t2_mapping, SRC_T2_SZ);
#endif
+
BNX2X_PCI_FREE(bp->spq, bp->spq_mapping, BCM_PAGE_SIZE);
BNX2X_PCI_FREE(bp->eq_ring, bp->eq_mapping,
@@ -5862,7 +5869,6 @@ static inline void set_sb_shortcuts(struct bnx2x *bp, int index)
int bnx2x_alloc_mem(struct bnx2x *bp)
{
-
#define BNX2X_PCI_ALLOC(x, y, size) \
do { \
x = dma_alloc_coherent(&bp->pdev->dev, size, y, GFP_KERNEL); \
@@ -5951,6 +5957,7 @@ int bnx2x_alloc_mem(struct bnx2x *bp)
sizeof(struct bnx2x_slowpath));
bp->context.size = sizeof(union cdu_context) * bp->l2_cid_count;
+
BNX2X_PCI_ALLOC(bp->context.vcxt, &bp->context.cxt_mapping,
bp->context.size);
@@ -5997,7 +6004,7 @@ int bnx2x_func_stop(struct bnx2x *bp)
}
/**
- * Sets a MAC in a CAM for a few L2 Clients for E1x chip
+ * Sets a MAC in a CAM for a few L2 Clients for E1x chips
*
* @param bp driver descriptor
* @param set set or clear an entry (1 or 0)
@@ -6007,8 +6014,8 @@ int bnx2x_func_stop(struct bnx2x *bp)
* @param is_bcast is the set MAC a broadcast address (for E1 only)
*/
static void bnx2x_set_mac_addr_gen(struct bnx2x *bp, int set, u8 *mac,
- u32 cl_bit_vec, u8 cam_offset,
- u8 is_bcast)
+ u32 cl_bit_vec, u8 cam_offset,
+ u8 is_bcast)
{
struct mac_configuration_cmd *config =
(struct mac_configuration_cmd *)bnx2x_sp(bp, mac_config);
@@ -6060,9 +6067,8 @@ static void bnx2x_set_mac_addr_gen(struct bnx2x *bp, int set, u8 *mac,
bnx2x_wait_ramrod(bp, 0, 0, &bp->set_mac_pending, ramrod_flags);
}
-
int bnx2x_wait_ramrod(struct bnx2x *bp, int state, int idx,
- int *state_p, int flags)
+ int *state_p, int flags)
{
/* can take a while if any port is running */
int cnt = 5000;
@@ -6220,7 +6226,6 @@ static void bnx2x_invlidate_e1_mc_list(struct bnx2x *bp)
}
-
#ifdef BCM_CNIC
/**
* Set iSCSI MAC(s) at the next enties in the CAM after the ETH
@@ -6567,6 +6572,7 @@ void bnx2x_ilt_set_info(struct bnx2x *bp)
ilt_client->flags = (ILT_CLIENT_SKIP_INIT | ILT_CLIENT_SKIP_MEM);
#endif
}
+
int bnx2x_setup_client(struct bnx2x *bp, struct bnx2x_fastpath *fp,
int is_leading)
{
@@ -6952,7 +6958,6 @@ void bnx2x_disable_close_the_gate(struct bnx2x *bp)
}
}
-
/* Close gates #2, #3 and #4: */
static void bnx2x_set_234_gates(struct bnx2x *bp, bool close)
{
@@ -6998,15 +7003,13 @@ static void bnx2x_clp_reset_prep(struct bnx2x *bp, u32 *magic_val)
static void bnx2x_clp_reset_done(struct bnx2x *bp, u32 magic_val)
{
/* Restore the `magic' bit value... */
- /* u32 val = SHMEM_RD(bp, mf_cfg.shared_mf_config.clp_mb);
- SHMEM_WR(bp, mf_cfg.shared_mf_config.clp_mb,
- (val & (~SHARED_MF_CLP_MAGIC)) | magic_val); */
u32 val = MF_CFG_RD(bp, shared_mf_config.clp_mb);
MF_CFG_WR(bp, shared_mf_config.clp_mb,
(val & (~SHARED_MF_CLP_MAGIC)) | magic_val);
}
-/* Prepares for MCP reset: takes care of CLP configurations.
+/**
+ * Prepares for MCP reset: takes care of CLP configurations.
*
* @param bp
* @param magic_val Old value of 'magic' bit.
@@ -7535,7 +7538,6 @@ static void __devinit bnx2x_undi_unload(struct bnx2x *bp)
bp->fw_seq =
(SHMEM_RD(bp, func_mb[bp->pf_num].drv_mb_header) &
DRV_MSG_SEQ_NUMBER_MASK);
-
} else
bnx2x_release_hw_lock(bp, HW_LOCK_RESOURCE_UNDI);
}
@@ -7654,7 +7656,8 @@ static void __devinit bnx2x_get_common_hwinfo(struct bnx2x *bp)
}
bp->link_params.feature_config_flags |=
(val >= REQ_BC_VER_4_VRFY_FIRST_PHY_OPT_MDL) ?
- FEATURE_CONFIG_BC_SUPPORTS_OPT_MDL_VRFY : 0;
+ FEATURE_CONFIG_BC_SUPPORTS_OPT_MDL_VRFY : 0;
+
bp->link_params.feature_config_flags |=
(val >= REQ_BC_VER_4_VRFY_SPECIFIC_PHY_OPT_MDL) ?
FEATURE_CONFIG_BC_SUPPORTS_DUAL_PHY_OPT_MDL_VRFY : 0;
@@ -7771,7 +7774,7 @@ static void __devinit bnx2x_link_settings_supported(struct bnx2x *bp,
SHMEM_RD(bp,
dev_info.port_hw_config[port].external_phy_config2));
return;
- }
+ }
switch (switch_cfg) {
case SWITCH_CFG_1G:
@@ -7784,7 +7787,6 @@ static void __devinit bnx2x_link_settings_supported(struct bnx2x *bp,
bp->port.phy_addr = REG_RD(bp, NIG_REG_XGXS0_CTRL_PHY_ADDR +
port*0x18);
BNX2X_DEV_INFO("phy_addr 0x%x\n", bp->port.phy_addr);
-
break;
default:
@@ -7813,7 +7815,7 @@ static void __devinit bnx2x_link_settings_supported(struct bnx2x *bp,
if (!(bp->link_params.speed_cap_mask[idx] &
PORT_HW_CFG_SPEED_CAPABILITY_D0_1G))
bp->port.supported[idx] &= ~(SUPPORTED_1000baseT_Half |
- SUPPORTED_1000baseT_Full);
+ SUPPORTED_1000baseT_Full);
if (!(bp->link_params.speed_cap_mask[idx] &
PORT_HW_CFG_SPEED_CAPABILITY_D0_2_5G))
@@ -7847,41 +7849,41 @@ static void __devinit bnx2x_link_settings_requested(struct bnx2x *bp)
bp->link_params.req_duplex[idx] = DUPLEX_FULL;
link_config = bp->port.link_config[idx];
switch (link_config & PORT_FEATURE_LINK_SPEED_MASK) {
- case PORT_FEATURE_LINK_SPEED_AUTO:
+ case PORT_FEATURE_LINK_SPEED_AUTO:
if (bp->port.supported[idx] & SUPPORTED_Autoneg) {
bp->link_params.req_line_speed[idx] =
SPEED_AUTO_NEG;
bp->port.advertising[idx] |=
bp->port.supported[idx];
- } else {
- /* force 10G, no AN */
+ } else {
+ /* force 10G, no AN */
bp->link_params.req_line_speed[idx] =
SPEED_10000;
bp->port.advertising[idx] |=
(ADVERTISED_10000baseT_Full |
- ADVERTISED_FIBRE);
+ ADVERTISED_FIBRE);
continue;
- }
- break;
+ }
+ break;
- case PORT_FEATURE_LINK_SPEED_10M_FULL:
+ case PORT_FEATURE_LINK_SPEED_10M_FULL:
if (bp->port.supported[idx] & SUPPORTED_10baseT_Full) {
bp->link_params.req_line_speed[idx] =
SPEED_10;
bp->port.advertising[idx] |=
(ADVERTISED_10baseT_Full |
- ADVERTISED_TP);
- } else {
- BNX2X_ERROR("NVRAM config error. "
- "Invalid link_config 0x%x"
- " speed_cap_mask 0x%x\n",
- link_config,
+ ADVERTISED_TP);
+ } else {
+ BNX2X_ERROR("NVRAM config error. "
+ "Invalid link_config 0x%x"
+ " speed_cap_mask 0x%x\n",
+ link_config,
bp->link_params.speed_cap_mask[idx]);
- return;
- }
- break;
+ return;
+ }
+ break;
- case PORT_FEATURE_LINK_SPEED_10M_HALF:
+ case PORT_FEATURE_LINK_SPEED_10M_HALF:
if (bp->port.supported[idx] & SUPPORTED_10baseT_Half) {
bp->link_params.req_line_speed[idx] =
SPEED_10;
@@ -7889,70 +7891,74 @@ static void __devinit bnx2x_link_settings_requested(struct bnx2x *bp)
DUPLEX_HALF;
bp->port.advertising[idx] |=
(ADVERTISED_10baseT_Half |
- ADVERTISED_TP);
- } else {
- BNX2X_ERROR("NVRAM config error. "
- "Invalid link_config 0x%x"
- " speed_cap_mask 0x%x\n",
- link_config,
- bp->link_params.speed_cap_mask[idx]);
- return;
- }
- break;
+ ADVERTISED_TP);
+ } else {
+ BNX2X_ERROR("NVRAM config error. "
+ "Invalid link_config 0x%x"
+ " speed_cap_mask 0x%x\n",
+ link_config,
+ bp->link_params.speed_cap_mask[idx]);
+ return;
+ }
+ break;
- case PORT_FEATURE_LINK_SPEED_100M_FULL:
- if (bp->port.supported[idx] & SUPPORTED_100baseT_Full) {
+ case PORT_FEATURE_LINK_SPEED_100M_FULL:
+ if (bp->port.supported[idx] &
+ SUPPORTED_100baseT_Full) {
bp->link_params.req_line_speed[idx] =
SPEED_100;
bp->port.advertising[idx] |=
(ADVERTISED_100baseT_Full |
- ADVERTISED_TP);
- } else {
- BNX2X_ERROR("NVRAM config error. "
- "Invalid link_config 0x%x"
- " speed_cap_mask 0x%x\n",
- link_config,
- bp->link_params.speed_cap_mask[idx]);
- return;
- }
- break;
+ ADVERTISED_TP);
+ } else {
+ BNX2X_ERROR("NVRAM config error. "
+ "Invalid link_config 0x%x"
+ " speed_cap_mask 0x%x\n",
+ link_config,
+ bp->link_params.speed_cap_mask[idx]);
+ return;
+ }
+ break;
- case PORT_FEATURE_LINK_SPEED_100M_HALF:
- if (bp->port.supported[idx] & SUPPORTED_100baseT_Half) {
- bp->link_params.req_line_speed[idx] = SPEED_100;
- bp->link_params.req_duplex[idx] = DUPLEX_HALF;
+ case PORT_FEATURE_LINK_SPEED_100M_HALF:
+ if (bp->port.supported[idx] &
+ SUPPORTED_100baseT_Half) {
+ bp->link_params.req_line_speed[idx] =
+ SPEED_100;
+ bp->link_params.req_duplex[idx] =
+ DUPLEX_HALF;
bp->port.advertising[idx] |=
(ADVERTISED_100baseT_Half |
- ADVERTISED_TP);
- } else {
- BNX2X_ERROR("NVRAM config error. "
+ ADVERTISED_TP);
+ } else {
+ BNX2X_ERROR("NVRAM config error. "
"Invalid link_config 0x%x"
" speed_cap_mask 0x%x\n",
link_config,
bp->link_params.speed_cap_mask[idx]);
- return;
- }
- break;
+ return;
+ }
+ break;
- case PORT_FEATURE_LINK_SPEED_1G:
+ case PORT_FEATURE_LINK_SPEED_1G:
if (bp->port.supported[idx] &
SUPPORTED_1000baseT_Full) {
bp->link_params.req_line_speed[idx] =
SPEED_1000;
bp->port.advertising[idx] |=
(ADVERTISED_1000baseT_Full |
- ADVERTISED_TP);
- } else {
- BNX2X_ERROR("NVRAM config error. "
+ ADVERTISED_TP);
+ } else {
+ BNX2X_ERROR("NVRAM config error. "
"Invalid link_config 0x%x"
" speed_cap_mask 0x%x\n",
link_config,
bp->link_params.speed_cap_mask[idx]);
- return;
- }
- break;
+ return;
+ }
+ break;
- case PORT_FEATURE_LINK_SPEED_2_5G:
+ case PORT_FEATURE_LINK_SPEED_2_5G:
if (bp->port.supported[idx] &
SUPPORTED_2500baseX_Full) {
bp->link_params.req_line_speed[idx] =
@@ -7960,19 +7966,19 @@ static void __devinit bnx2x_link_settings_requested(struct bnx2x *bp)
bp->port.advertising[idx] |=
(ADVERTISED_2500baseX_Full |
ADVERTISED_TP);
- } else {
- BNX2X_ERROR("NVRAM config error. "
+ } else {
+ BNX2X_ERROR("NVRAM config error. "
"Invalid link_config 0x%x"
" speed_cap_mask 0x%x\n",
link_config,
- bp->link_params.speed_cap_mask[idx]);
- return;
- }
- break;
+ bp->link_params.speed_cap_mask[idx]);
+ return;
+ }
+ break;
- case PORT_FEATURE_LINK_SPEED_10G_CX4:
- case PORT_FEATURE_LINK_SPEED_10G_KX4:
- case PORT_FEATURE_LINK_SPEED_10G_KR:
+ case PORT_FEATURE_LINK_SPEED_10G_CX4:
+ case PORT_FEATURE_LINK_SPEED_10G_KX4:
+ case PORT_FEATURE_LINK_SPEED_10G_KR:
if (bp->port.supported[idx] &
SUPPORTED_10000baseT_Full) {
bp->link_params.req_line_speed[idx] =
@@ -7980,24 +7986,26 @@ static void __devinit bnx2x_link_settings_requested(struct bnx2x *bp)
bp->port.advertising[idx] |=
(ADVERTISED_10000baseT_Full |
ADVERTISED_FIBRE);
- } else {
- BNX2X_ERROR("NVRAM config error. "
+ } else {
+ BNX2X_ERROR("NVRAM config error. "
"Invalid link_config 0x%x"
" speed_cap_mask 0x%x\n",
link_config,
- bp->link_params.speed_cap_mask[idx]);
- return;
- }
- break;
+ bp->link_params.speed_cap_mask[idx]);
+ return;
+ }
+ break;
- default:
- BNX2X_ERROR("NVRAM config error. "
- "BAD link speed link_config 0x%x\n",
- link_config);
- bp->link_params.req_line_speed[idx] = SPEED_AUTO_NEG;
- bp->port.advertising[idx] = bp->port.supported[idx];
- break;
- }
+ default:
+ BNX2X_ERROR("NVRAM config error. "
+ "BAD link speed link_config 0x%x\n",
+ link_config);
+ bp->link_params.req_line_speed[idx] =
+ SPEED_AUTO_NEG;
+ bp->port.advertising[idx] =
+ bp->port.supported[idx];
+ break;
+ }
bp->link_params.req_flow_ctrl[idx] = (link_config &
PORT_FEATURE_FLOW_CONTROL_MASK);
@@ -8059,14 +8067,14 @@ static void __devinit bnx2x_get_port_hwinfo(struct bnx2x *bp)
bp->wol = (!(bp->flags & NO_WOL_FLAG) &&
(config & PORT_FEATURE_WOL_ENABLED));
- BNX2X_DEV_INFO("lane_config 0x%08x"
+ BNX2X_DEV_INFO("lane_config 0x%08x "
"speed_cap_mask0 0x%08x link_config0 0x%08x\n",
bp->link_params.lane_config,
bp->link_params.speed_cap_mask[0],
bp->port.link_config[0]);
bp->link_params.switch_cfg = (bp->port.link_config[0] &
- PORT_FEATURE_CONNECTED_SWITCH_MASK);
+ PORT_FEATURE_CONNECTED_SWITCH_MASK);
bnx2x_phy_probe(&bp->link_params);
bnx2x_link_settings_supported(bp, bp->link_params.switch_cfg);
@@ -8461,12 +8469,10 @@ void bnx2x_set_rx_mode(struct net_device *dev)
if (dev->flags & IFF_PROMISC)
rx_mode = BNX2X_RX_MODE_PROMISC;
-
else if ((dev->flags & IFF_ALLMULTI) ||
((netdev_mc_count(dev) > BNX2X_MAX_MULTICAST) &&
CHIP_IS_E1(bp)))
rx_mode = BNX2X_RX_MODE_ALLMULTI;
-
else { /* some multicasts */
if (CHIP_IS_E1(bp)) {
/*
@@ -8506,12 +8512,10 @@ void bnx2x_set_rx_mode(struct net_device *dev)
}
}
-
bp->rx_mode = rx_mode;
bnx2x_set_storm_rx_mode(bp);
}
-
/* called with rtnl_lock */
static int bnx2x_mdio_read(struct net_device *netdev, int prtad,
int devad, u16 addr)
@@ -9002,6 +9006,7 @@ static inline int bnx2x_set_qm_cid_count(struct bnx2x *bp, int l2_cid_count)
#endif
return roundup(cid_count, QM_CID_ROUND);
}
+
static int __devinit bnx2x_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
@@ -9029,6 +9034,7 @@ static int __devinit bnx2x_init_one(struct pci_dev *pdev,
}
cid_count += CNIC_CONTEXT_USE;
+
/* dev zeroed in init_etherdev */
dev = alloc_etherdev_mq(sizeof(*bp), cid_count);
if (!dev) {
@@ -9120,6 +9126,7 @@ static void __devexit bnx2x_remove_one(struct pci_dev *pdev)
/* Disable MSI/MSI-X */
bnx2x_disable_msi(bp);
+
/* Make sure RESET task is not scheduled before continuing */
cancel_delayed_work_sync(&bp->reset_task);
diff --git a/drivers/net/bnx2x/bnx2x_stats.c b/drivers/net/bnx2x/bnx2x_stats.c
index ad7aa55..5644bdd 100644
--- a/drivers/net/bnx2x/bnx2x_stats.c
+++ b/drivers/net/bnx2x/bnx2x_stats.c
@@ -14,8 +14,8 @@
* Statistics and Link management by Yitchak Gertner
*
*/
- #include "bnx2x_cmn.h"
- #include "bnx2x_stats.h"
+#include "bnx2x_cmn.h"
+#include "bnx2x_stats.h"
/* Statistics */
diff --git a/drivers/net/bnx2x/bnx2x_stats.h b/drivers/net/bnx2x/bnx2x_stats.h
index 38a4e90..afd15ef 100644
--- a/drivers/net/bnx2x/bnx2x_stats.h
+++ b/drivers/net/bnx2x/bnx2x_stats.h
@@ -9,6 +9,10 @@
* Maintained by: Eilon Greenstein <eilong@broadcom.com>
* Written by: Eliezer Tamir
* Based on code from Michael Chan's bnx2 driver
+ * UDP CSUM errata workaround by Arik Gendelman
+ * Slowpath and fastpath rework by Vladislav Zolotarov
+ * Statistics and Link management by Yitchak Gertner
+ *
*/
#ifndef BNX2X_STATS_H
@@ -228,12 +232,8 @@ struct bnx2x_eth_stats {
/* Forward declaration */
struct bnx2x;
-
void bnx2x_stats_init(struct bnx2x *bp);
extern const u32 dmae_reg_go_c[];
-extern int bnx2x_sp_post(struct bnx2x *bp, int command, int cid,
- u32 data_hi, u32 data_lo, int common);
-
#endif /* BNX2X_STATS_H */
--
1.7.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox