* pktgen: tricks
From: Stephen Hemminger @ 2009-09-23 5:49 UTC (permalink / raw)
To: Jesper Dangaard Brouer, Robert Olsson; +Cc: netdev
I thought others want to know how to get maximum speed of pktgen.
1. Run nothing else (even X11), just a command line
2. Make sure ethernet flow control is disabled
ethtool -A eth0 autoneg off rx off tx off
3. Make sure clocksource is TSC. On my old SMP Opteron's
needed to get patch since in 2.6.30 or later, the clock guru's
decided to remove it on all non Intel machines. Look for patch
than enables "tsc=reliable"
4. Compile Ethernet drivers in, the overhead of the indirect
function call required for modules (or cache footprint),
slows things down.
5. Increase transmit ring size to 1000
ethtool -G eth0 tx 1000
Result: OK: 70408581(c70405979+d2602) nsec, 100000000 (60byte,0frags)
1420281pps 681Mb/sec (681734880bps) errors: 0
^ permalink raw reply
* Re: [RFC] skb align patch
From: Thomas Graf @ 2009-09-23 5:47 UTC (permalink / raw)
To: David Miller; +Cc: eric.dumazet, shemminger, jesse.brandeburg, hawk, netdev
In-Reply-To: <20090921.222940.258576265.davem@davemloft.net>
On Mon, Sep 21, 2009 at 10:29:40PM -0700, David Miller wrote:
> The alignment in this patch is a real big deal for 64 byte forwarding
> tests, where the entire packet is a whole PCI-E cacheline. But not
> if it isn't aligned properly.
As I pointed out to Herbert already, this alignment change may actually
make things worse or even break things as long as compare_ether_header()
used in __napi_gro_receive() expects the IP header to be aligned to 4
bytes. That can be fixed of course, just wanted to mention it.
^ permalink raw reply
* [PATCH 1/2] pktgen: T_TERMINATE flag is unused
From: Stephen Hemminger @ 2009-09-23 5:41 UTC (permalink / raw)
To: David S. Miller; +Cc: netdev
In-Reply-To: <20090923054141.932043798@vyatta.com>
[-- Attachment #1: pktgen-terminate.patch --]
[-- Type: text/plain, Size: 872 bytes --]
Get rid of unused flag bit.
Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
--- a/net/core/pktgen.c 2009-09-21 20:35:50.106198752 -0700
+++ b/net/core/pktgen.c 2009-09-21 20:36:03.349310985 -0700
@@ -192,11 +192,10 @@
#define F_QUEUE_MAP_CPU (1<<14) /* queue map mirrors smp_processor_id() */
/* Thread control flag bits */
-#define T_TERMINATE (1<<0)
-#define T_STOP (1<<1) /* Stop run */
-#define T_RUN (1<<2) /* Start run */
-#define T_REMDEVALL (1<<3) /* Remove all devs */
-#define T_REMDEV (1<<4) /* Remove one dev */
+#define T_STOP (1<<0) /* Stop run */
+#define T_RUN (1<<1) /* Start run */
+#define T_REMDEVALL (1<<2) /* Remove all devs */
+#define T_REMDEV (1<<3) /* Remove one dev */
/* If lock -- can be removed after some work */
#define if_lock(t) spin_lock(&(t->if_lock));
--
^ permalink raw reply
* [PATCH 2/2] pktgen: better scheduler friendliness
From: Stephen Hemminger @ 2009-09-23 5:41 UTC (permalink / raw)
To: David S. Miller; +Cc: netdev
In-Reply-To: <20090923054141.932043798@vyatta.com>
[-- Attachment #1: pktgen-fix.patch --]
[-- Type: text/plain, Size: 6846 bytes --]
Previous update did not resched in inner loop causing watchdogs.
Rewrite inner loop to:
* account for delays better with less clock calls
* more accurate timing of delay:
- only delay if packet was successfully sent
- if delay is 100ns and it takes 10ns to build packet then
account for that
* use wait_event_interruptible_timeout rather than open coding it.
Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
--- a/net/core/pktgen.c 2009-09-21 20:36:03.349310985 -0700
+++ b/net/core/pktgen.c 2009-09-21 20:49:53.647508802 -0700
@@ -2104,7 +2104,7 @@ static void pktgen_setup_inject(struct p
static void spin(struct pktgen_dev *pkt_dev, ktime_t spin_until)
{
- ktime_t start;
+ ktime_t start_time, end_time;
s32 remaining;
struct hrtimer_sleeper t;
@@ -2115,7 +2115,7 @@ static void spin(struct pktgen_dev *pkt_
if (remaining <= 0)
return;
- start = ktime_now();
+ start_time = ktime_now();
if (remaining < 100)
udelay(remaining); /* really small just spin */
else {
@@ -2134,7 +2134,10 @@ static void spin(struct pktgen_dev *pkt_
} while (t.task && pkt_dev->running && !signal_pending(current));
__set_current_state(TASK_RUNNING);
}
- pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_now(), start));
+ end_time = ktime_now();
+
+ pkt_dev->idle_acc += ktime_to_ns(ktime_sub(end_time, start_time));
+ pkt_dev->next_tx = ktime_add_ns(end_time, pkt_dev->delay);
}
static inline void set_pkt_overhead(struct pktgen_dev *pkt_dev)
@@ -3364,19 +3367,29 @@ static void pktgen_rem_thread(struct pkt
mutex_unlock(&pktgen_thread_lock);
}
-static void idle(struct pktgen_dev *pkt_dev)
+static void pktgen_resched(struct pktgen_dev *pkt_dev)
{
ktime_t idle_start = ktime_now();
+ schedule();
+ pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_now(), idle_start));
+}
- if (need_resched())
- schedule();
- else
- cpu_relax();
+static void pktgen_wait_for_skb(struct pktgen_dev *pkt_dev)
+{
+ ktime_t idle_start = ktime_now();
+ while (atomic_read(&(pkt_dev->skb->users)) != 1) {
+ if (signal_pending(current))
+ break;
+
+ if (need_resched())
+ pktgen_resched(pkt_dev);
+ else
+ cpu_relax();
+ }
pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_now(), idle_start));
}
-
static void pktgen_xmit(struct pktgen_dev *pkt_dev)
{
struct net_device *odev = pkt_dev->odev;
@@ -3386,36 +3399,21 @@ static void pktgen_xmit(struct pktgen_de
u16 queue_map;
int ret;
- if (pkt_dev->delay) {
- spin(pkt_dev, pkt_dev->next_tx);
-
- /* This is max DELAY, this has special meaning of
- * "never transmit"
- */
- if (pkt_dev->delay == ULLONG_MAX) {
- pkt_dev->next_tx = ktime_add_ns(ktime_now(), ULONG_MAX);
- return;
- }
- }
-
- if (!pkt_dev->skb) {
- set_cur_queue_map(pkt_dev);
- queue_map = pkt_dev->cur_queue_map;
- } else {
- queue_map = skb_get_queue_mapping(pkt_dev->skb);
+ /* If device is offline, then don't send */
+ if (unlikely(!netif_running(odev) || !netif_carrier_ok(odev))) {
+ pktgen_stop_device(pkt_dev);
+ return;
}
- txq = netdev_get_tx_queue(odev, queue_map);
- /* Did we saturate the queue already? */
- if (netif_tx_queue_stopped(txq) || netif_tx_queue_frozen(txq)) {
- /* If device is down, then all queues are permnantly frozen */
- if (netif_running(odev))
- idle(pkt_dev);
- else
- pktgen_stop_device(pkt_dev);
+ /* This is max DELAY, this has special meaning of
+ * "never transmit"
+ */
+ if (unlikely(pkt_dev->delay == ULLONG_MAX)) {
+ pkt_dev->next_tx = ktime_add_ns(ktime_now(), ULONG_MAX);
return;
}
+ /* If no skb or clone count exhausted then get new one */
if (!pkt_dev->skb || (pkt_dev->last_ok &&
++pkt_dev->clone_count >= pkt_dev->clone_skb)) {
/* build a new pkt */
@@ -3434,54 +3432,45 @@ static void pktgen_xmit(struct pktgen_de
pkt_dev->clone_count = 0; /* reset counter */
}
- /* fill_packet() might have changed the queue */
+ if (pkt_dev->delay && pkt_dev->last_ok)
+ spin(pkt_dev, pkt_dev->next_tx);
+
queue_map = skb_get_queue_mapping(pkt_dev->skb);
txq = netdev_get_tx_queue(odev, queue_map);
__netif_tx_lock_bh(txq);
- if (unlikely(netif_tx_queue_stopped(txq) || netif_tx_queue_frozen(txq)))
- pkt_dev->last_ok = 0;
- else {
- atomic_inc(&(pkt_dev->skb->users));
+ atomic_inc(&(pkt_dev->skb->users));
- retry_now:
+ if (unlikely(netif_tx_queue_stopped(txq) || netif_tx_queue_frozen(txq)))
+ ret = NETDEV_TX_BUSY;
+ else
ret = (*xmit)(pkt_dev->skb, odev);
- switch (ret) {
- case NETDEV_TX_OK:
- txq_trans_update(txq);
- pkt_dev->last_ok = 1;
- pkt_dev->sofar++;
- pkt_dev->seq_num++;
- pkt_dev->tx_bytes += pkt_dev->cur_pkt_size;
- break;
- case NETDEV_TX_LOCKED:
- cpu_relax();
- goto retry_now;
- default: /* Drivers are not supposed to return other values! */
- if (net_ratelimit())
- pr_info("pktgen: %s xmit error: %d\n",
- odev->name, ret);
- pkt_dev->errors++;
- /* fallthru */
- case NETDEV_TX_BUSY:
- /* Retry it next time */
- atomic_dec(&(pkt_dev->skb->users));
- pkt_dev->last_ok = 0;
- }
-
- if (pkt_dev->delay)
- pkt_dev->next_tx = ktime_add_ns(ktime_now(),
- pkt_dev->delay);
+
+ switch (ret) {
+ case NETDEV_TX_OK:
+ txq_trans_update(txq);
+ pkt_dev->last_ok = 1;
+ pkt_dev->sofar++;
+ pkt_dev->seq_num++;
+ pkt_dev->tx_bytes += pkt_dev->cur_pkt_size;
+ break;
+ default: /* Drivers are not supposed to return other values! */
+ if (net_ratelimit())
+ pr_info("pktgen: %s xmit error: %d\n",
+ odev->name, ret);
+ pkt_dev->errors++;
+ /* fallthru */
+ case NETDEV_TX_LOCKED:
+ case NETDEV_TX_BUSY:
+ /* Retry it next time */
+ atomic_dec(&(pkt_dev->skb->users));
+ pkt_dev->last_ok = 0;
}
__netif_tx_unlock_bh(txq);
/* If pkt_dev->count is zero, then run forever */
if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) {
- while (atomic_read(&(pkt_dev->skb->users)) != 1) {
- if (signal_pending(current))
- break;
- idle(pkt_dev);
- }
+ pktgen_wait_for_skb(pkt_dev);
/* Done with this */
pktgen_stop_device(pkt_dev);
@@ -3514,20 +3503,24 @@ static int pktgen_thread_worker(void *ar
while (!kthread_should_stop()) {
pkt_dev = next_to_run(t);
- if (!pkt_dev &&
- (t->control & (T_STOP | T_RUN | T_REMDEVALL | T_REMDEV))
- == 0) {
- prepare_to_wait(&(t->queue), &wait,
- TASK_INTERRUPTIBLE);
- schedule_timeout(HZ / 10);
- finish_wait(&(t->queue), &wait);
+ if (unlikely(!pkt_dev && t->control == 0)) {
+ wait_event_interruptible_timeout(t->queue,
+ t->control != 0,
+ HZ/10);
+ continue;
}
__set_current_state(TASK_RUNNING);
- if (pkt_dev)
+ if (likely(pkt_dev)) {
pktgen_xmit(pkt_dev);
+ if (need_resched())
+ pktgen_resched(pkt_dev);
+ else
+ cpu_relax();
+ }
+
if (t->control & T_STOP) {
pktgen_stop(t);
t->control &= ~(T_STOP);
--
^ permalink raw reply
* Re: [PATCH 3/3] i2400m-sdio: select IWMC3200TOP in Kconfig
From: Tomas Winkler @ 2009-09-23 5:36 UTC (permalink / raw)
To: Marcel Holtmann
Cc: davem-fT/PcQaiUtIeIZ0/mPfg9Q, linville-2XuSBdqkA4R54TAoqtyWWQ,
netdev-u79uwXL29TY76Z2rM5mHXA,
linux-wireless-u79uwXL29TY76Z2rM5mHXA,
linux-mmc-u79uwXL29TY76Z2rM5mHXA, yi.zhu-ral2JQCrhuEAvxtiuMwx3w,
inaky.perez-gonzalez-ral2JQCrhuEAvxtiuMwx3w,
cindy.h.kao-ral2JQCrhuEAvxtiuMwx3w,
guy.cohen-ral2JQCrhuEAvxtiuMwx3w,
ron.rindjunsky-ral2JQCrhuEAvxtiuMwx3w
In-Reply-To: <1253666644.2931.7.camel-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
On Wed, Sep 23, 2009 at 3:44 AM, Marcel Holtmann <marcel-kz+m5ild9QBg9hUCZPvPmw@public.gmane.org> wrote:
> Hi Tomas,
>
>> i2400m-sdio requires iwmc3200top for its operation
>>
>> Signed-off-by: Tomas Winkler <tomas.winkler-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
>> ---
>> drivers/net/wimax/i2400m/Kconfig | 1 +
>> 1 files changed, 1 insertions(+), 0 deletions(-)
>>
>> diff --git a/drivers/net/wimax/i2400m/Kconfig b/drivers/net/wimax/i2400m/Kconfig
>> index d623b3d..7368ad5 100644
>> --- a/drivers/net/wimax/i2400m/Kconfig
>> +++ b/drivers/net/wimax/i2400m/Kconfig
>> @@ -25,6 +25,7 @@ config WIMAX_I2400M_SDIO
>> tristate "Intel Wireless WiMAX Connection 2400 over SDIO"
>> depends on WIMAX && MMC
>> select WIMAX_I2400M
>> + select IWMC3200TOP
>> help
>> Select if you have a device based on the Intel WiMAX
>> Connection 2400 over SDIO.
>
> this is not true actually. Since the WiMAX hardware in my laptop doesn't
> require the top driver.
>
SDIO?
Thanks
Tomas
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: igb VF allocation with quirk_i82576_sriov
From: Chris Wright @ 2009-09-23 5:12 UTC (permalink / raw)
To: Alexander Duyck
Cc: Chris Wright, netdev@vger.kernel.org, Ronciak, John,
e1000-devel@lists.sourceforge.net
In-Reply-To: <4AB8F042.4030306@intel.com>
* Alexander Duyck (alexander.h.duyck@intel.com) wrote:
> Chris Wright wrote:
>> Is this known to work? During recent virt testing for upcoming Fedora 12,
>> a box w/out SR-IOV support in BIOS was using quirk to create VF BAR space,
>> VF allocation worked enough to assign a device to the guest, but igbvf
>> was not actually functioning properly in the guest.
>>
>> Is it worth debugging this further, or is it already a known issue?
>
> You could be experiencing one of a couple different issues.
>
> First when you say you started SR-IOV on a box w/out SR-IOV support I
> assume you are using "pci=assign-busses" in order to reserve the bus
> space for the VFs, is that correct? Also while your system may not
> support SR-IOV does it at least support VT-d? Without VT-d support you
> won't be able to assign a device to the guest.
VT-d was definitely there, as for the rest I'll have to ask the tester
for more details. I just wanted to verify that it's a known working
combo before spending more time on it.
Regarding the bus numbering, I don't think there's a bus issue.
The PF+VFs all stay w/in same bus segment despite large offset and the
stride (IIRC, this was only device on bus 2, a dual port igb on .0 and .1.
the offset is 128 and stride is 2, so even w/ 8 VFs the max device would
be something like 2:11.7 or 2:12.0).
> My recommendations for further testing would be to test a VF on the host
> kernel to see if that works. If it does then you could also try direct
> assigning an entire port to see if that works. If the entire port
> doesn't work then you probably don't have VT-d enabled.
Yeah, IIRC, igbvf at least loaded on the host (on the guest too, after
unbinding host driver). I didn't get a chance to see if VF passed
traffic on the host, and from the report, it wasn't able to get a dhcp
address in the guest. Will dig into it a bit more after plumbers.
thanks,
-chris
------------------------------------------------------------------------------
Come build with us! The BlackBerry® Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay
ahead of the curve. Join us from November 9-12, 2009. Register now!
http://p.sf.net/sfu/devconf
^ permalink raw reply
* Re: [PATCH][RESEND 3] IPv6: 6rd tunnel mode
From: Brian Haley @ 2009-09-23 1:47 UTC (permalink / raw)
To: Alexandre Cassen; +Cc: netdev
In-Reply-To: <20090922220251.GA22874@lnxos.staff.proxad.net>
Alexandre Cassen wrote:
>
> +/* 6RD parms */
> +struct ip_tunnel_6rd {
> + struct in6_addr addr;
> + __u8 prefixlen;
> +};
Are you sure you're not going to want to add anything to this struct in
the future like ifindex or flags? Since it's part of the API you'd want
to do that now.
-Brian
^ permalink raw reply
* Re: [PATCH 1/2] ipv4: fix do_ip_setsockopt optlen check for IP_MULTICAST_IF
From: Shan Wei @ 2009-09-23 1:41 UTC (permalink / raw)
To: David Miller
Cc: dfeng, kaber, yoshfuji, jmorris, pekkas, kuznet, netdev,
linux-kernel
In-Reply-To: <20090922.133819.173686372.davem@davemloft.net>
David Miller wrote, at 09/23/2009 04:38 AM:
> From: Shan Wei <shanwei@cn.fujitsu.com>
> Date: Thu, 17 Sep 2009 17:15:22 +0800
>
>> Xiaotian Feng wrote, at 09/17/2009 01:19 PM:
>>> Due to man page of setsockopt, if optlen is not valid, kernel should return
>>> -EINVAL. But a simple testcase as following, errno is 0, which means setsockopt
>>> is successful.
>>>
>>> addr.s_addr = inet_addr("192.1.2.3");
>>> setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &addr, 1);
>>> printf("errno is %d\n", errno);
>>>
>>> This patch fixes the optlen check part, with the patch, we got errno EINVAL.
>>>
>> I also think it's a bug, the freebsd also does the optlen check.
>> But the style should be coincident with other option: firstly check the
>> availability of optlen, then copy option value from user and deal with it.
>>
>> How about this one:
>
> This definitely is better and cleaner, but please don't post such
> things without proper signoffs and commit messages because now
> I have to ask you to do that instead of me just applying your
> patch :-/
>
I'm so sorry about that. The whole patch is below.
[PATCH BUGFIX] ipv4: check optlen for IP_MULTICAST_IF option
Due to man page of setsockopt, if optlen is not valid, kernel should return
-EINVAL. But a simple testcase as following, errno is 0, which means setsockopt
is successful.
addr.s_addr = inet_addr("192.1.2.3");
setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &addr, 1);
printf("errno is %d\n", errno);
Xiaotian Feng(dfeng@redhat.com) caught the bug. We fix it firstly checking
the availability of optlen and then dealing with the logic like other options.
Reported-by: Xiaotian Feng <dfeng@redhat.com>
Signed-off-by: Shan Wei <shanwei@cn.fujitsu.com>
Acked-by: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
---
net/ipv4/ip_sockglue.c | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index fc7993e..5a06935 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -611,6 +611,9 @@ static int do_ip_setsockopt(struct sock *sk, int level,
* Check the arguments are allowable
*/
+ if (optlen < sizeof(struct in_addr))
+ goto e_inval;
+
err = -EFAULT;
if (optlen >= sizeof(struct ip_mreqn)) {
if (copy_from_user(&mreq, optval, sizeof(mreq)))
--
1.6.0.4
^ permalink raw reply related
* Re: [PATCH 3/3] i2400m-sdio: select IWMC3200TOP in Kconfig
From: Marcel Holtmann @ 2009-09-23 0:44 UTC (permalink / raw)
To: Tomas Winkler
Cc: davem-fT/PcQaiUtIeIZ0/mPfg9Q, linville-2XuSBdqkA4R54TAoqtyWWQ,
netdev-u79uwXL29TY76Z2rM5mHXA,
linux-wireless-u79uwXL29TY76Z2rM5mHXA,
linux-mmc-u79uwXL29TY76Z2rM5mHXA, yi.zhu-ral2JQCrhuEAvxtiuMwx3w,
inaky.perez-gonzalez-ral2JQCrhuEAvxtiuMwx3w,
cindy.h.kao-ral2JQCrhuEAvxtiuMwx3w,
guy.cohen-ral2JQCrhuEAvxtiuMwx3w,
ron.rindjunsky-ral2JQCrhuEAvxtiuMwx3w
In-Reply-To: <1253662724-16497-4-git-send-email-tomas.winkler-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
Hi Tomas,
> i2400m-sdio requires iwmc3200top for its operation
>
> Signed-off-by: Tomas Winkler <tomas.winkler-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> ---
> drivers/net/wimax/i2400m/Kconfig | 1 +
> 1 files changed, 1 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/net/wimax/i2400m/Kconfig b/drivers/net/wimax/i2400m/Kconfig
> index d623b3d..7368ad5 100644
> --- a/drivers/net/wimax/i2400m/Kconfig
> +++ b/drivers/net/wimax/i2400m/Kconfig
> @@ -25,6 +25,7 @@ config WIMAX_I2400M_SDIO
> tristate "Intel Wireless WiMAX Connection 2400 over SDIO"
> depends on WIMAX && MMC
> select WIMAX_I2400M
> + select IWMC3200TOP
> help
> Select if you have a device based on the Intel WiMAX
> Connection 2400 over SDIO.
this is not true actually. Since the WiMAX hardware in my laptop doesn't
require the top driver.
Regards
Marcel
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: fanotify as syscalls
From: Davide Libenzi @ 2009-09-22 23:43 UTC (permalink / raw)
To: Jamie Lokier
Cc: Eric Paris, Andreas Gruenbacher, Linus Torvalds, Evgeniy Polyakov,
David Miller, Linux Kernel Mailing List, linux-fsdevel, netdev,
viro, alan, hch
In-Reply-To: <20090922162707.GA11608@shareable.org>
On Tue, 22 Sep 2009, Jamie Lokier wrote:
> Eric Paris wrote:
> > That's not the fatal flaw. The fatal flaw is that I am not going to
> > write 90% of a rootkit and make it easy to use.
>
> I hate to point out the obvious, but fanotify's ability to intercept
> every file access and rewrite the file before the access proceeds is
> also 90% of a rootkit...
Obvious, but worth noticing.
Indeed, the syscall table has been RO to make it harder for RKs to
exploit it, not to make it impossible. RO syscall table makes perfect
sense.
But, once you are root, with very few lines of code, you can find,
prot-fix the page, and patch the table.
> But fortunately both fanotify and syscall rewriting require root in
> the first place. ^^^^^^^^^
Again, maybe I wasn't clear about how this would work, but the syscall
table would continue to remain RO ;)
And as I said before, if we want to bring the cost of the interception for
non-users pretty close to zero (a few NOPs to run onto), we could even
adopt an alternative-like patching triggered by a kernel boot option.
- Davide
^ permalink raw reply
* [PATCH 3/3] i2400m-sdio: select IWMC3200TOP in Kconfig
From: Tomas Winkler @ 2009-09-22 23:38 UTC (permalink / raw)
To: davem, linville, netdev, linux-wireless, linux-mmc
Cc: yi.zhu, inaky.perez-gonzalez, cindy.h.kao, guy.cohen,
ron.rindjunsky, Tomas Winkler
In-Reply-To: <1253662724-16497-3-git-send-email-tomas.winkler@intel.com>
i2400m-sdio requires iwmc3200top for its operation
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
---
drivers/net/wimax/i2400m/Kconfig | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/drivers/net/wimax/i2400m/Kconfig b/drivers/net/wimax/i2400m/Kconfig
index d623b3d..7368ad5 100644
--- a/drivers/net/wimax/i2400m/Kconfig
+++ b/drivers/net/wimax/i2400m/Kconfig
@@ -25,6 +25,7 @@ config WIMAX_I2400M_SDIO
tristate "Intel Wireless WiMAX Connection 2400 over SDIO"
depends on WIMAX && MMC
select WIMAX_I2400M
+ select IWMC3200TOP
help
Select if you have a device based on the Intel WiMAX
Connection 2400 over SDIO.
--
1.6.0.6
---------------------------------------------------------------------
Intel Israel (74) Limited
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply related
* [PATCH 2/3] iwmc3200wifi: select IWMC3200TOP in Kconfig
From: Tomas Winkler @ 2009-09-22 23:38 UTC (permalink / raw)
To: davem-fT/PcQaiUtIeIZ0/mPfg9Q, linville-2XuSBdqkA4R54TAoqtyWWQ,
netdev-u79uwXL29TY76Z2rM5mHXA,
linux-wireless-u79uwXL29TY76Z2rM5mHXA,
linux-mmc-u79uwXL29TY76Z2rM5mHXA
Cc: yi.zhu-ral2JQCrhuEAvxtiuMwx3w,
inaky.perez-gonzalez-ral2JQCrhuEAvxtiuMwx3w,
cindy.h.kao-ral2JQCrhuEAvxtiuMwx3w,
guy.cohen-ral2JQCrhuEAvxtiuMwx3w,
ron.rindjunsky-ral2JQCrhuEAvxtiuMwx3w, Tomas Winkler
In-Reply-To: <1253662724-16497-2-git-send-email-tomas.winkler-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
iwmc3200wifi requires iwmc3200top for its operation
Signed-off-by: Tomas Winkler <tomas.winkler-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
---
drivers/net/wireless/iwmc3200wifi/Kconfig | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/drivers/net/wireless/iwmc3200wifi/Kconfig b/drivers/net/wireless/iwmc3200wifi/Kconfig
index c62da43..69faaf1 100644
--- a/drivers/net/wireless/iwmc3200wifi/Kconfig
+++ b/drivers/net/wireless/iwmc3200wifi/Kconfig
@@ -3,6 +3,7 @@ config IWM
depends on MMC && WLAN_80211 && EXPERIMENTAL
depends on CFG80211
select FW_LOADER
+ select IWMC3200TOP
help
The Intel Wireless Multicomm 3200 hardware is a combo
card with GPS, Bluetooth, WiMax and 802.11 radios. It
--
1.6.0.6
---------------------------------------------------------------------
Intel Israel (74) Limited
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 1/3] iwmc3200top: Add Intel Wireless MultiCom 3200 top driver.
From: Tomas Winkler @ 2009-09-22 23:38 UTC (permalink / raw)
To: davem, linville, netdev, linux-wireless, linux-mmc
Cc: yi.zhu, inaky.perez-gonzalez, cindy.h.kao, guy.cohen,
ron.rindjunsky, Tomas Winkler
In-Reply-To: <1253662724-16497-1-git-send-email-tomas.winkler@intel.com>
This patch adds Intel Wireless MultiCom 3200 top driver.
IWMC3200 is 4Wireless Com CHIP (GPS/BT/WiFi/WiMAX).
Top driver is responsible for device initialization and firmware download.
Firmware handled by top is responsible for top itself and
as well as bluetooth and GPS coms. (Wifi and WiMax provide their own
firmware)
In addition top driver is used to retrieve firmware logs
and supports other debugging features
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
---
drivers/misc/Kconfig | 1 +
drivers/misc/Makefile | 1 +
drivers/misc/iwmc3200top/Kconfig | 20 +
drivers/misc/iwmc3200top/Makefile | 29 ++
drivers/misc/iwmc3200top/debugfs.c | 145 +++++++
drivers/misc/iwmc3200top/debugfs.h | 61 +++
drivers/misc/iwmc3200top/fw-download.c | 359 ++++++++++++++++
drivers/misc/iwmc3200top/fw-msg.h | 113 +++++
drivers/misc/iwmc3200top/iwmc3200top.h | 202 +++++++++
drivers/misc/iwmc3200top/log.c | 339 +++++++++++++++
drivers/misc/iwmc3200top/log.h | 158 +++++++
drivers/misc/iwmc3200top/main.c | 702 ++++++++++++++++++++++++++++++++
12 files changed, 2130 insertions(+), 0 deletions(-)
create mode 100644 drivers/misc/iwmc3200top/Kconfig
create mode 100644 drivers/misc/iwmc3200top/Makefile
create mode 100644 drivers/misc/iwmc3200top/debugfs.c
create mode 100644 drivers/misc/iwmc3200top/debugfs.h
create mode 100644 drivers/misc/iwmc3200top/fw-download.c
create mode 100644 drivers/misc/iwmc3200top/fw-msg.h
create mode 100644 drivers/misc/iwmc3200top/iwmc3200top.h
create mode 100644 drivers/misc/iwmc3200top/log.c
create mode 100644 drivers/misc/iwmc3200top/log.h
create mode 100644 drivers/misc/iwmc3200top/main.c
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 68ab39d..6f85b43 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -236,5 +236,6 @@ config ISL29003
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
+source "drivers/misc/iwmc3200top/Kconfig"
endif # MISC_DEVICES
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 36f733c..97f5303 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -20,5 +20,6 @@ obj-$(CONFIG_SGI_GRU) += sgi-gru/
obj-$(CONFIG_HP_ILO) += hpilo.o
obj-$(CONFIG_ISL29003) += isl29003.o
obj-$(CONFIG_C2PORT) += c2port/
+obj-$(CONFIG_IWMC3200TOP) += iwmc3200top/
obj-y += eeprom/
obj-y += cb710/
diff --git a/drivers/misc/iwmc3200top/Kconfig b/drivers/misc/iwmc3200top/Kconfig
new file mode 100644
index 0000000..9e4b88f
--- /dev/null
+++ b/drivers/misc/iwmc3200top/Kconfig
@@ -0,0 +1,20 @@
+config IWMC3200TOP
+ tristate "Intel Wireless MultiCom Top Driver"
+ depends on MMC && EXPERIMENTAL
+ select FW_LOADER
+ ---help---
+ Intel Wireless MultiCom 3200 Top driver is responsible for
+ for firmware load and enabled coms enumeration
+
+config IWMC3200TOP_DEBUG
+ bool "Enable full debug output of iwmc3200top Driver"
+ depends on IWMC3200TOP
+ ---help---
+ Enable full debug output of iwmc3200top Driver
+
+config IWMC3200TOP_DEBUGFS
+ bool "Enable Debugfs debugging interface for iwmc3200top"
+ depends on IWMC3200TOP
+ ---help---
+ Enable creation of debugfs files for iwmc3200top
+
diff --git a/drivers/misc/iwmc3200top/Makefile b/drivers/misc/iwmc3200top/Makefile
new file mode 100644
index 0000000..fbf53fb
--- /dev/null
+++ b/drivers/misc/iwmc3200top/Makefile
@@ -0,0 +1,29 @@
+# iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
+# drivers/misc/iwmc3200top/Makefile
+#
+# Copyright (C) 2009 Intel Corporation. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version
+# 2 as published by the Free Software Foundation.
+#
+# 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.
+#
+#
+# Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
+# -
+#
+#
+
+obj-$(CONFIG_IWMC3200TOP) += iwmc3200top.o
+iwmc3200top-objs := main.o fw-download.o
+iwmc3200top-$(CONFIG_IWMC3200TOP_DEBUG) += log.o
+iwmc3200top-$(CONFIG_IWMC3200TOP_DEBUGFS) += debugfs.o
diff --git a/drivers/misc/iwmc3200top/debugfs.c b/drivers/misc/iwmc3200top/debugfs.c
new file mode 100644
index 0000000..a531f6c
--- /dev/null
+++ b/drivers/misc/iwmc3200top/debugfs.c
@@ -0,0 +1,145 @@
+/*
+ * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
+ * drivers/misc/iwmc3200top/debufs.c
+ *
+ * Copyright (C) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ *
+ * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
+ * -
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio.h>
+
+#include "iwmc3200top.h"
+#include "log.h"
+#include "fw-msg.h"
+#include "debugfs.h"
+
+/* Constants definition */
+#define HEXADECIMAL_RADIX 16
+
+/* Functions definition */
+
+
+/* Debugfs macros */
+#define DEBUGFS_ADD_DIR(name, parent) do { \
+ dbgfs->dir_##name = debugfs_create_dir(#name, parent); \
+ if (!(dbgfs->dir_##name)) \
+ goto err; \
+} while (0)
+
+#define DEBUGFS_ADD_FILE(name, parent) do { \
+ dbgfs->dbgfs_##parent##_files.file_##name = \
+ debugfs_create_file(#name, 0644, dbgfs->dir_##parent, priv, \
+ &iwmct_dbgfs_##name##_ops); \
+ if (!(dbgfs->dbgfs_##parent##_files.file_##name)) \
+ goto err; \
+} while (0)
+
+#define DEBUGFS_REMOVE(name) do { \
+ debugfs_remove(name); \
+ name = NULL; \
+} while (0)
+
+#define DEBUGFS_READ_FUNC(name) \
+ssize_t iwmct_dbgfs_##name##_read(struct file *file, \
+ char __user *user_buf, \
+ size_t count, loff_t *ppos);
+
+#define DEBUGFS_WRITE_FUNC(name) \
+ssize_t iwmct_dbgfs_##name##_write(struct file *file, \
+ const char __user *user_buf, \
+ size_t count, loff_t *ppos);
+
+#define DEBUGFS_READ_FILE_OPS(name) \
+ DEBUGFS_READ_FUNC(name) \
+ static const struct file_operations iwmct_dbgfs_##name##_ops = { \
+ .read = iwmct_dbgfs_##name##_read, \
+ .open = iwmct_dbgfs_open_file_generic, \
+ };
+
+#define DEBUGFS_WRITE_FILE_OPS(name) \
+ DEBUGFS_WRITE_FUNC(name) \
+ static const struct file_operations iwmct_dbgfs_##name##_ops = { \
+ .write = iwmct_dbgfs_##name##_write, \
+ .open = iwmct_dbgfs_open_file_generic, \
+ };
+
+#define DEBUGFS_READ_WRITE_FILE_OPS(name) \
+ DEBUGFS_READ_FUNC(name) \
+ DEBUGFS_WRITE_FUNC(name) \
+ static const struct file_operations iwmct_dbgfs_##name##_ops = {\
+ .write = iwmct_dbgfs_##name##_write, \
+ .read = iwmct_dbgfs_##name##_read, \
+ .open = iwmct_dbgfs_open_file_generic, \
+ };
+
+
+/* Debugfs file ops definitions */
+
+/* Debugfs registration functions */
+
+/*
+ * Create the debugfs files and directories
+ *
+ */
+void iwmct_dbgfs_register(struct iwmct_priv *priv, const char *name)
+{
+ struct iwmct_debugfs *dbgfs;
+
+ dbgfs = kzalloc(sizeof(struct iwmct_debugfs), GFP_KERNEL);
+ if (!dbgfs) {
+ LOG_ERROR(priv, DEBUGFS, "failed to allocate %zd bytes\n",
+ sizeof(struct iwmct_debugfs));
+ goto err;
+ }
+
+ priv->dbgfs = dbgfs;
+ dbgfs->name = name;
+ dbgfs->dir_drv = debugfs_create_dir(name, NULL);
+ if (!dbgfs->dir_drv) {
+ LOG_ERROR(priv, DEBUGFS, "failed to create debugfs dir\n");
+ goto err;
+ }
+
+
+err:
+ return;
+}
+
+/**
+ * Remove the debugfs files and directories
+ *
+ */
+void iwmct_dbgfs_unregister(struct iwmct_debugfs *dbgfs)
+{
+ if (!dbgfs)
+ return;
+
+
+ DEBUGFS_REMOVE(dbgfs->dir_drv);
+ kfree(dbgfs);
+
+ dbgfs = NULL;
+}
+
diff --git a/drivers/misc/iwmc3200top/debugfs.h b/drivers/misc/iwmc3200top/debugfs.h
new file mode 100644
index 0000000..24b1369
--- /dev/null
+++ b/drivers/misc/iwmc3200top/debugfs.h
@@ -0,0 +1,61 @@
+/*
+ * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
+ * drivers/misc/iwmc3200top/debufs.h
+ *
+ * Copyright (C) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ *
+ * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
+ * -
+ *
+ */
+
+#ifndef __DEBUGFS_H__
+#define __DEBUGFS_H__
+
+
+#ifdef CONFIG_IWMC3200TOP_DEBUGFS
+
+#include <linux/debugfs.h>
+
+struct iwmct_debugfs {
+ const char *name;
+ struct dentry *dir_drv;
+ struct dir_drv_files {
+ } dbgfs_drv_files;
+};
+
+void iwmct_dbgfs_register(struct iwmct_priv *priv, const char *name);
+void iwmct_dbgfs_unregister(struct iwmct_debugfs *dbgfs);
+
+#else /* CONFIG_IWMC3200TOP_DEBUGFS */
+
+/* struct iwmct_debugfs is empty if CONFIG_IWMC3200TOP_DEBUGFS is not defined */
+struct iwmct_debugfs {
+};
+
+static inline void
+iwmct_dbgfs_register(struct iwmct_priv *priv, const char *name)
+{}
+
+static inline void iwmct_dbgfs_unregister(struct iwmct_debugfs *dbgfs)
+{}
+
+#endif /* CONFIG_IWMC3200TOP_DEBUGFS */
+
+#endif /* __DEBUGFS_H__ */
+
diff --git a/drivers/misc/iwmc3200top/fw-download.c b/drivers/misc/iwmc3200top/fw-download.c
new file mode 100644
index 0000000..33cb693
--- /dev/null
+++ b/drivers/misc/iwmc3200top/fw-download.c
@@ -0,0 +1,359 @@
+/*
+ * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
+ * drivers/misc/iwmc3200top/fw-download.c
+ *
+ * Copyright (C) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ *
+ * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
+ * -
+ *
+ */
+
+#include <linux/firmware.h>
+#include <linux/mmc/sdio_func.h>
+#include <asm/unaligned.h>
+
+#include "iwmc3200top.h"
+#include "log.h"
+#include "fw-msg.h"
+
+#define CHECKSUM_BYTES_NUM sizeof(u32)
+
+/**
+ init parser struct with file
+ */
+static int iwmct_fw_parser_init(struct iwmct_priv *priv, const u8 *file,
+ size_t file_size, size_t block_size)
+{
+ struct iwmct_parser *parser = &priv->parser;
+ struct iwmct_fw_hdr *fw_hdr = &parser->versions;
+
+ LOG_INFOEX(priv, INIT, "-->\n");
+
+ LOG_INFO(priv, FW_DOWNLOAD, "file_size=%zd\n", file_size);
+
+ parser->file = file;
+ parser->file_size = file_size;
+ parser->cur_pos = 0;
+ parser->buf = NULL;
+
+ parser->buf = kzalloc(block_size, GFP_KERNEL);
+ if (!parser->buf) {
+ LOG_ERROR(priv, FW_DOWNLOAD, "kzalloc error\n");
+ return -ENOMEM;
+ }
+ parser->buf_size = block_size;
+
+ /* extract fw versions */
+ memcpy(fw_hdr, parser->file, sizeof(struct iwmct_fw_hdr));
+ LOG_INFO(priv, FW_DOWNLOAD, "fw versions are:\n"
+ "top %u.%u.%u gps %u.%u.%u bt %u.%u.%u tic %s\n",
+ fw_hdr->top_major, fw_hdr->top_minor, fw_hdr->top_revision,
+ fw_hdr->gps_major, fw_hdr->gps_minor, fw_hdr->gps_revision,
+ fw_hdr->bt_major, fw_hdr->bt_minor, fw_hdr->bt_revision,
+ fw_hdr->tic_name);
+
+ parser->cur_pos += sizeof(struct iwmct_fw_hdr);
+
+ LOG_INFOEX(priv, INIT, "<--\n");
+ return 0;
+}
+
+static bool iwmct_checksum(struct iwmct_priv *priv)
+{
+ struct iwmct_parser *parser = &priv->parser;
+ __le32 *file = (__le32 *)parser->file;
+ int i, pad, steps;
+ u32 accum = 0;
+ u32 checksum;
+ u32 mask = 0xffffffff;
+
+ pad = (parser->file_size - CHECKSUM_BYTES_NUM) % 4;
+ steps = (parser->file_size - CHECKSUM_BYTES_NUM) / 4;
+
+ LOG_INFO(priv, FW_DOWNLOAD, "pad=%d steps=%d\n", pad, steps);
+
+ for (i = 0; i < steps; i++)
+ accum += le32_to_cpu(file[i]);
+
+ if (pad) {
+ mask <<= 8 * (4 - pad);
+ accum += le32_to_cpu(file[steps]) & mask;
+ }
+
+ checksum = get_unaligned_le32((__le32 *)(parser->file +
+ parser->file_size - CHECKSUM_BYTES_NUM));
+
+ LOG_INFO(priv, FW_DOWNLOAD,
+ "compare checksum accum=0x%x to checksum=0x%x\n",
+ accum, checksum);
+
+ return checksum == accum;
+}
+
+static int iwmct_parse_next_section(struct iwmct_priv *priv, const u8 **p_sec,
+ size_t *sec_size, __le32 *sec_addr)
+{
+ struct iwmct_parser *parser = &priv->parser;
+ struct iwmct_dbg *dbg = &priv->dbg;
+ struct iwmct_fw_sec_hdr *sec_hdr;
+
+ LOG_INFOEX(priv, INIT, "-->\n");
+
+ while (parser->cur_pos + sizeof(struct iwmct_fw_sec_hdr)
+ <= parser->file_size) {
+
+ sec_hdr = (struct iwmct_fw_sec_hdr *)
+ (parser->file + parser->cur_pos);
+ parser->cur_pos += sizeof(struct iwmct_fw_sec_hdr);
+
+ LOG_INFO(priv, FW_DOWNLOAD,
+ "sec hdr: type=%s addr=0x%x size=%d\n",
+ sec_hdr->type, sec_hdr->target_addr,
+ sec_hdr->data_size);
+
+ if (strcmp(sec_hdr->type, "ENT") == 0)
+ parser->entry_point = le32_to_cpu(sec_hdr->target_addr);
+ else if (strcmp(sec_hdr->type, "LBL") == 0)
+ strcpy(dbg->label_fw, parser->file + parser->cur_pos);
+ else if (((strcmp(sec_hdr->type, "TOP") == 0) &&
+ (priv->barker & BARKER_DNLOAD_TOP_MSK)) ||
+ ((strcmp(sec_hdr->type, "GPS") == 0) &&
+ (priv->barker & BARKER_DNLOAD_GPS_MSK)) ||
+ ((strcmp(sec_hdr->type, "BTH") == 0) &&
+ (priv->barker & BARKER_DNLOAD_BT_MSK))) {
+ *sec_addr = sec_hdr->target_addr;
+ *sec_size = le32_to_cpu(sec_hdr->data_size);
+ *p_sec = parser->file + parser->cur_pos;
+ parser->cur_pos += le32_to_cpu(sec_hdr->data_size);
+ return 1;
+ } else if (strcmp(sec_hdr->type, "LOG") != 0)
+ LOG_WARNING(priv, FW_DOWNLOAD,
+ "skipping section type %s\n",
+ sec_hdr->type);
+
+ parser->cur_pos += le32_to_cpu(sec_hdr->data_size);
+ LOG_INFO(priv, FW_DOWNLOAD,
+ "finished with section cur_pos=%zd\n", parser->cur_pos);
+ }
+
+ LOG_INFOEX(priv, INIT, "<--\n");
+ return 0;
+}
+
+static int iwmct_download_section(struct iwmct_priv *priv, const u8 *p_sec,
+ size_t sec_size, __le32 addr)
+{
+ struct iwmct_parser *parser = &priv->parser;
+ struct iwmct_fw_load_hdr *hdr = (struct iwmct_fw_load_hdr *)parser->buf;
+ const u8 *cur_block = p_sec;
+ size_t sent = 0;
+ int cnt = 0;
+ int ret = 0;
+ u32 cmd = 0;
+
+ LOG_INFOEX(priv, INIT, "-->\n");
+ LOG_INFO(priv, FW_DOWNLOAD, "Download address 0x%x size 0x%zx\n",
+ addr, sec_size);
+
+ while (sent < sec_size) {
+ int i;
+ u32 chksm = 0;
+ u32 reset = atomic_read(&priv->reset);
+ /* actual FW data */
+ u32 data_size = min(parser->buf_size - sizeof(*hdr),
+ sec_size - sent);
+ /* Pad to block size */
+ u32 trans_size = (data_size + sizeof(*hdr) +
+ IWMC_SDIO_BLK_SIZE - 1) &
+ ~(IWMC_SDIO_BLK_SIZE - 1);
+ ++cnt;
+
+ /* in case of reset, interrupt FW DOWNLAOD */
+ if (reset) {
+ LOG_INFO(priv, FW_DOWNLOAD,
+ "Reset detected. Abort FW download!!!");
+ ret = -ECANCELED;
+ goto exit;
+ }
+
+ memset(parser->buf, 0, parser->buf_size);
+ cmd |= IWMC_OPCODE_WRITE << CMD_HDR_OPCODE_POS;
+ cmd |= IWMC_CMD_SIGNATURE << CMD_HDR_SIGNATURE_POS;
+ cmd |= (priv->dbg.direct ? 1 : 0) << CMD_HDR_DIRECT_ACCESS_POS;
+ cmd |= (priv->dbg.checksum ? 1 : 0) << CMD_HDR_USE_CHECKSUM_POS;
+ hdr->data_size = cpu_to_le32(data_size);
+ hdr->target_addr = addr;
+
+ /* checksum is allowed for sizes divisible by 4 */
+ if (data_size & 0x3)
+ cmd &= ~CMD_HDR_USE_CHECKSUM_MSK;
+
+ memcpy(hdr->data, cur_block, data_size);
+
+
+ if (cmd & CMD_HDR_USE_CHECKSUM_MSK) {
+
+ chksm = data_size + le32_to_cpu(addr) + cmd;
+ for (i = 0; i < data_size >> 2; i++)
+ chksm += ((u32 *)cur_block)[i];
+
+ hdr->block_chksm = cpu_to_le32(chksm);
+ LOG_INFO(priv, FW_DOWNLOAD, "Checksum = 0x%X\n",
+ hdr->block_chksm);
+ }
+
+ LOG_INFO(priv, FW_DOWNLOAD, "trans#%d, len=%d, sent=%zd, "
+ "sec_size=%zd, startAddress 0x%X\n",
+ cnt, trans_size, sent, sec_size, addr);
+
+ if (priv->dbg.dump)
+ LOG_HEXDUMP(FW_DOWNLOAD, parser->buf, trans_size);
+
+
+ hdr->cmd = cpu_to_le32(cmd);
+ /* send it down */
+ /* TODO: add more proper sending and error checking */
+ ret = iwmct_tx(priv, 0, parser->buf, trans_size);
+ if (ret != 0) {
+ LOG_INFO(priv, FW_DOWNLOAD,
+ "iwmct_tx returned %d\n", ret);
+ goto exit;
+ }
+
+ addr = cpu_to_le32(le32_to_cpu(addr) + data_size);
+ sent += data_size;
+ cur_block = p_sec + sent;
+
+ if (priv->dbg.blocks && (cnt + 1) >= priv->dbg.blocks) {
+ LOG_INFO(priv, FW_DOWNLOAD,
+ "Block number limit is reached [%d]\n",
+ priv->dbg.blocks);
+ break;
+ }
+ }
+
+ if (sent < sec_size)
+ ret = -EINVAL;
+exit:
+ LOG_INFOEX(priv, INIT, "<--\n");
+ return ret;
+}
+
+static int iwmct_kick_fw(struct iwmct_priv *priv, bool jump)
+{
+ struct iwmct_parser *parser = &priv->parser;
+ struct iwmct_fw_load_hdr *hdr = (struct iwmct_fw_load_hdr *)parser->buf;
+ int ret;
+ u32 cmd;
+
+ LOG_INFOEX(priv, INIT, "-->\n");
+
+ memset(parser->buf, 0, parser->buf_size);
+ cmd = IWMC_CMD_SIGNATURE << CMD_HDR_SIGNATURE_POS;
+ if (jump) {
+ cmd |= IWMC_OPCODE_JUMP << CMD_HDR_OPCODE_POS;
+ hdr->target_addr = cpu_to_le32(parser->entry_point);
+ LOG_INFO(priv, FW_DOWNLOAD, "jump address 0x%x\n",
+ parser->entry_point);
+ } else {
+ cmd |= IWMC_OPCODE_LAST_COMMAND << CMD_HDR_OPCODE_POS;
+ LOG_INFO(priv, FW_DOWNLOAD, "last command\n");
+ }
+
+ hdr->cmd = cpu_to_le32(cmd);
+
+ LOG_HEXDUMP(FW_DOWNLOAD, parser->buf, sizeof(*hdr));
+ /* send it down */
+ /* TODO: add more proper sending and error checking */
+ ret = iwmct_tx(priv, 0, parser->buf, IWMC_SDIO_BLK_SIZE);
+ if (ret)
+ LOG_INFO(priv, FW_DOWNLOAD, "iwmct_tx returned %d", ret);
+
+ LOG_INFOEX(priv, INIT, "<--\n");
+ return 0;
+}
+
+int iwmct_fw_load(struct iwmct_priv *priv)
+{
+ const struct firmware *raw = NULL;
+ __le32 addr;
+ size_t len;
+ const u8 *pdata;
+ const u8 *name = "iwmc3200top.1.fw";
+ int ret = 0;
+
+ /* clear parser struct */
+ memset(&priv->parser, 0, sizeof(struct iwmct_parser));
+ if (!name) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ /* get the firmware */
+ ret = request_firmware(&raw, name, &priv->func->dev);
+ if (ret < 0) {
+ LOG_ERROR(priv, FW_DOWNLOAD, "%s request_firmware failed %d\n",
+ name, ret);
+ goto exit;
+ }
+
+ if (raw->size < sizeof(struct iwmct_fw_sec_hdr)) {
+ LOG_ERROR(priv, FW_DOWNLOAD, "%s smaller then (%zd) (%zd)\n",
+ name, sizeof(struct iwmct_fw_sec_hdr), raw->size);
+ goto exit;
+ }
+
+ LOG_INFO(priv, FW_DOWNLOAD, "Read firmware '%s'\n", name);
+
+ ret = iwmct_fw_parser_init(priv, raw->data, raw->size, priv->trans_len);
+ if (ret < 0) {
+ LOG_ERROR(priv, FW_DOWNLOAD,
+ "iwmct_parser_init failed: Reason %d\n", ret);
+ goto exit;
+ }
+
+ /* checksum */
+ if (!iwmct_checksum(priv)) {
+ LOG_ERROR(priv, FW_DOWNLOAD, "checksum error\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ /* download firmware to device */
+ while (iwmct_parse_next_section(priv, &pdata, &len, &addr)) {
+ if (iwmct_download_section(priv, pdata, len, addr)) {
+ LOG_ERROR(priv, FW_DOWNLOAD,
+ "%s download section failed\n", name);
+ ret = -EIO;
+ goto exit;
+ }
+ }
+
+ iwmct_kick_fw(priv, !!(priv->barker & BARKER_DNLOAD_JUMP_MSK));
+
+exit:
+ kfree(priv->parser.buf);
+
+ if (raw)
+ release_firmware(raw);
+
+ raw = NULL;
+
+ return ret;
+}
diff --git a/drivers/misc/iwmc3200top/fw-msg.h b/drivers/misc/iwmc3200top/fw-msg.h
new file mode 100644
index 0000000..9e26b75
--- /dev/null
+++ b/drivers/misc/iwmc3200top/fw-msg.h
@@ -0,0 +1,113 @@
+/*
+ * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
+ * drivers/misc/iwmc3200top/fw-msg.h
+ *
+ * Copyright (C) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ *
+ * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
+ * -
+ *
+ */
+
+#ifndef __FWMSG_H__
+#define __FWMSG_H__
+
+#define COMM_TYPE_D2H 0xFF
+#define COMM_TYPE_H2D 0xEE
+
+#define COMM_CATEGORY_OPERATIONAL 0x00
+#define COMM_CATEGORY_DEBUG 0x01
+#define COMM_CATEGORY_TESTABILITY 0x02
+#define COMM_CATEGORY_DIAGNOSTICS 0x03
+
+#define OP_DBG_ZSTR_MSG cpu_to_le16(0x1A)
+
+#define FW_LOG_SRC_MAX 32
+#define FW_LOG_SRC_ALL 255
+
+#define FW_STRING_TABLE_ADDR cpu_to_le32(0x0C000000)
+
+#define CMD_DBG_LOG_LEVEL cpu_to_le16(0x0001)
+#define CMD_TST_DEV_RESET cpu_to_le16(0x0060)
+#define CMD_TST_FUNC_RESET cpu_to_le16(0x0062)
+#define CMD_TST_IFACE_RESET cpu_to_le16(0x0064)
+#define CMD_TST_CPU_UTILIZATION cpu_to_le16(0x0065)
+#define CMD_TST_TOP_DEEP_SLEEP cpu_to_le16(0x0080)
+#define CMD_TST_WAKEUP cpu_to_le16(0x0081)
+#define CMD_TST_FUNC_WAKEUP cpu_to_le16(0x0082)
+#define CMD_TST_FUNC_DEEP_SLEEP_REQUEST cpu_to_le16(0x0083)
+#define CMD_TST_GET_MEM_DUMP cpu_to_le16(0x0096)
+
+#define OP_OPR_ALIVE cpu_to_le16(0x0010)
+#define OP_OPR_CMD_ACK cpu_to_le16(0x001F)
+#define OP_OPR_CMD_NACK cpu_to_le16(0x0020)
+#define OP_TST_MEM_DUMP cpu_to_le16(0x0043)
+
+#define CMD_FLAG_PADDING_256 0x80
+
+#define FW_HCMD_BLOCK_SIZE 256
+
+struct msg_hdr {
+ u8 type;
+ u8 category;
+ __le16 opcode;
+ u8 seqnum;
+ u8 flags;
+ __le16 length;
+} __attribute__((__packed__));
+
+struct log_hdr {
+ __le32 timestamp;
+ u8 severity;
+ u8 logsource;
+ __le16 reserved;
+} __attribute__((__packed__));
+
+struct mdump_hdr {
+ u8 dmpid;
+ u8 frag;
+ __le16 size;
+ __le32 addr;
+} __attribute__((__packed__));
+
+struct top_msg {
+ struct msg_hdr hdr;
+ union {
+ /* D2H messages */
+ struct {
+ struct log_hdr log_hdr;
+ u8 data[1];
+ } __attribute__((__packed__)) log;
+
+ struct {
+ struct log_hdr log_hdr;
+ struct mdump_hdr md_hdr;
+ u8 data[1];
+ } __attribute__((__packed__)) mdump;
+
+ /* H2D messages */
+ struct {
+ u8 logsource;
+ u8 sevmask;
+ } __attribute__((__packed__)) logdefs[FW_LOG_SRC_MAX];
+ struct mdump_hdr mdump_req;
+ } u;
+} __attribute__((__packed__));
+
+
+#endif /* __FWMSG_H__ */
diff --git a/drivers/misc/iwmc3200top/iwmc3200top.h b/drivers/misc/iwmc3200top/iwmc3200top.h
new file mode 100644
index 0000000..59e4b7a
--- /dev/null
+++ b/drivers/misc/iwmc3200top/iwmc3200top.h
@@ -0,0 +1,202 @@
+/*
+ * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
+ * drivers/misc/iwmc3200top/iwmc3200top.h
+ *
+ * Copyright (C) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ *
+ * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
+ * -
+ *
+ */
+
+#ifndef __IWMC3200TOP_H__
+#define __IWMC3200TOP_H__
+
+#define DRV_NAME "iwmc3200top"
+
+#define IWMC_SDIO_BLK_SIZE 256
+#define IWMC_DEFAULT_TR_BLK 64
+#define IWMC_SDIO_DATA_ADDR 0x0
+#define IWMC_SDIO_INTR_ENABLE_ADDR 0x14
+#define IWMC_SDIO_INTR_STATUS_ADDR 0x13
+#define IWMC_SDIO_INTR_CLEAR_ADDR 0x13
+#define IWMC_SDIO_INTR_GET_SIZE_ADDR 0x2C
+
+#define COMM_HUB_HEADER_LENGTH 16
+#define LOGGER_HEADER_LENGTH 10
+
+
+#define BARKER_DNLOAD_BT_POS 0
+#define BARKER_DNLOAD_BT_MSK BIT(BARKER_DNLOAD_BT_POS)
+#define BARKER_DNLOAD_GPS_POS 1
+#define BARKER_DNLOAD_GPS_MSK BIT(BARKER_DNLOAD_GPS_POS)
+#define BARKER_DNLOAD_TOP_POS 2
+#define BARKER_DNLOAD_TOP_MSK BIT(BARKER_DNLOAD_TOP_POS)
+#define BARKER_DNLOAD_RESERVED1_POS 3
+#define BARKER_DNLOAD_RESERVED1_MSK BIT(BARKER_DNLOAD_RESERVED1_POS)
+#define BARKER_DNLOAD_JUMP_POS 4
+#define BARKER_DNLOAD_JUMP_MSK BIT(BARKER_DNLOAD_JUMP_POS)
+#define BARKER_DNLOAD_SYNC_POS 5
+#define BARKER_DNLOAD_SYNC_MSK BIT(BARKER_DNLOAD_SYNC_POS)
+#define BARKER_DNLOAD_RESERVED2_POS 6
+#define BARKER_DNLOAD_RESERVED2_MSK (0x3 << BARKER_DNLOAD_RESERVED2_POS)
+#define BARKER_DNLOAD_BARKER_POS 8
+#define BARKER_DNLOAD_BARKER_MSK (0xffffff << BARKER_DNLOAD_BARKER_POS)
+
+#define IWMC_BARKER_REBOOT (0xdeadbe << BARKER_DNLOAD_BARKER_POS)
+/* whole field barker */
+#define IWMC_BARKER_ACK 0xfeedbabe
+
+#define IWMC_CMD_SIGNATURE 0xcbbc
+
+#define CMD_HDR_OPCODE_POS 0
+#define CMD_HDR_OPCODE_MSK_MSK (0xf << CMD_HDR_OPCODE_MSK_POS)
+#define CMD_HDR_RESPONSE_CODE_POS 4
+#define CMD_HDR_RESPONSE_CODE_MSK (0xf << CMD_HDR_RESPONSE_CODE_POS)
+#define CMD_HDR_USE_CHECKSUM_POS 8
+#define CMD_HDR_USE_CHECKSUM_MSK BIT(CMD_HDR_USE_CHECKSUM_POS)
+#define CMD_HDR_RESPONSE_REQUIRED_POS 9
+#define CMD_HDR_RESPONSE_REQUIRED_MSK BIT(CMD_HDR_RESPONSE_REQUIRED_POS)
+#define CMD_HDR_DIRECT_ACCESS_POS 10
+#define CMD_HDR_DIRECT_ACCESS_MSK BIT(CMD_HDR_DIRECT_ACCESS_POS)
+#define CMD_HDR_RESERVED_POS 11
+#define CMD_HDR_RESERVED_MSK BIT(0x1f << CMD_HDR_RESERVED_POS)
+#define CMD_HDR_SIGNATURE_POS 16
+#define CMD_HDR_SIGNATURE_MSK BIT(0xffff << CMD_HDR_SIGNATURE_POS)
+
+enum {
+ IWMC_OPCODE_PING = 0,
+ IWMC_OPCODE_READ = 1,
+ IWMC_OPCODE_WRITE = 2,
+ IWMC_OPCODE_JUMP = 3,
+ IWMC_OPCODE_REBOOT = 4,
+ IWMC_OPCODE_PERSISTENT_WRITE = 5,
+ IWMC_OPCODE_PERSISTENT_READ = 6,
+ IWMC_OPCODE_READ_MODIFY_WRITE = 7,
+ IWMC_OPCODE_LAST_COMMAND = 15
+};
+
+struct iwmct_fw_load_hdr {
+ __le32 cmd;
+ __le32 target_addr;
+ __le32 data_size;
+ __le32 block_chksm;
+ u8 data[0];
+};
+
+/**
+ * struct iwmct_fw_hdr
+ * holds all sw components versions
+ */
+struct iwmct_fw_hdr {
+ u8 top_major;
+ u8 top_minor;
+ u8 top_revision;
+ u8 gps_major;
+ u8 gps_minor;
+ u8 gps_revision;
+ u8 bt_major;
+ u8 bt_minor;
+ u8 bt_revision;
+ u8 tic_name[31];
+};
+
+/**
+ * struct iwmct_fw_sec_hdr
+ * @type: function type
+ * @data_size: section's data size
+ * @target_addr: download address
+ */
+struct iwmct_fw_sec_hdr {
+ u8 type[4];
+ __le32 data_size;
+ __le32 target_addr;
+};
+
+/**
+ * struct iwmct_parser
+ * @file: fw image
+ * @file_size: fw size
+ * @cur_pos: position in file
+ * @buf: temp buf for download
+ * @buf_size: size of buf
+ * @entry_point: address to jump in fw kick-off
+ */
+struct iwmct_parser {
+ const u8 *file;
+ size_t file_size;
+ size_t cur_pos;
+ u8 *buf;
+ size_t buf_size;
+ u32 entry_point;
+ struct iwmct_fw_hdr versions;
+};
+
+
+struct iwmct_work_struct {
+ struct list_head list;
+ ssize_t iosize;
+};
+
+struct iwmct_dbg {
+ int blocks;
+ bool dump;
+ bool jump;
+ bool direct;
+ bool checksum;
+ bool fw_download;
+ int block_size;
+ int download_trans_blks;
+
+ char label_fw[256];
+};
+
+struct iwmct_priv {
+ struct sdio_func *func;
+ struct iwmct_debugfs *dbgfs;
+ struct iwmct_parser parser;
+ atomic_t reset;
+ atomic_t dev_sync;
+ u32 trans_len;
+ u32 barker;
+ struct iwmct_dbg dbg;
+
+ /* drivers work queue */
+ struct workqueue_struct *wq;
+ struct workqueue_struct *bus_rescan_wq;
+ struct work_struct bus_rescan_worker;
+ struct work_struct isr_worker;
+
+ /* drivers wait queue */
+ wait_queue_head_t wait_q;
+
+ /* rx request list */
+ struct list_head read_req_list;
+};
+
+extern int iwmct_tx(struct iwmct_priv *priv, unsigned int addr,
+ void *src, int count);
+
+extern int iwmct_fw_load(struct iwmct_priv *priv);
+
+extern void iwmct_dbg_init_params(struct iwmct_priv *drv);
+extern void iwmct_dbg_init_drv_attrs(struct device_driver *drv);
+extern void iwmct_dbg_remove_drv_attrs(struct device_driver *drv);
+extern int iwmct_send_hcmd(struct iwmct_priv *priv, u8 *cmd, u16 len);
+
+#endif /* __IWMC3200TOP_H__ */
diff --git a/drivers/misc/iwmc3200top/log.c b/drivers/misc/iwmc3200top/log.c
new file mode 100644
index 0000000..96b5e4a
--- /dev/null
+++ b/drivers/misc/iwmc3200top/log.c
@@ -0,0 +1,339 @@
+/*
+ * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
+ * drivers/misc/iwmc3200top/log.c
+ *
+ * Copyright (C) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ *
+ * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
+ * -
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/ctype.h>
+#include "fw-msg.h"
+#include "iwmc3200top.h"
+#include "log.h"
+
+/* Maximal hexadecimal string size of the FW memdump message */
+#define LOG_MSG_SIZE_MAX 12400
+
+/* iwmct_logdefs is a global used by log macros */
+u8 iwmct_logdefs[LOG_SRC_MAX];
+static u8 iwmct_fw_logdefs[FW_LOG_SRC_MAX];
+
+
+static int _log_set_log_filter(u8 *logdefs, int size, u8 src, u8 logmask)
+{
+ int i;
+
+ if (src < size)
+ logdefs[src] = logmask;
+ else if (src == LOG_SRC_ALL)
+ for (i = 0; i < size; i++)
+ logdefs[i] = logmask;
+ else
+ return -1;
+
+ return 0;
+}
+
+
+int iwmct_log_set_filter(u8 src, u8 logmask)
+{
+ return _log_set_log_filter(iwmct_logdefs, LOG_SRC_MAX, src, logmask);
+}
+
+
+int iwmct_log_set_fw_filter(u8 src, u8 logmask)
+{
+ return _log_set_log_filter(iwmct_fw_logdefs,
+ FW_LOG_SRC_MAX, src, logmask);
+}
+
+
+static int log_msg_format_hex(char *str, int slen, u8 *ibuf,
+ int ilen, char *pref)
+{
+ int pos = 0;
+ int i;
+ int len;
+
+ for (pos = 0, i = 0; pos < slen - 2 && pref[i] != '\0'; i++, pos++)
+ str[pos] = pref[i];
+
+ for (i = 0; pos < slen - 2 && i < ilen; pos += len, i++)
+ len = snprintf(&str[pos], slen - pos - 1, " %2.2X", ibuf[i]);
+
+ if (i < ilen)
+ return -1;
+
+ return 0;
+}
+
+/* NOTE: This function is not thread safe.
+ Currently it's called only from sdio rx worker - no race there
+*/
+void iwmct_log_top_message(struct iwmct_priv *priv, u8 *buf, int len)
+{
+ struct top_msg *msg;
+ static char logbuf[LOG_MSG_SIZE_MAX];
+
+ msg = (struct top_msg *)buf;
+
+ if (len < sizeof(msg->hdr) + sizeof(msg->u.log.log_hdr)) {
+ LOG_ERROR(priv, FW_MSG, "Log message from TOP "
+ "is too short %d (expected %zd)\n",
+ len, sizeof(msg->hdr) + sizeof(msg->u.log.log_hdr));
+ return;
+ }
+
+ if (!(iwmct_fw_logdefs[msg->u.log.log_hdr.logsource] &
+ BIT(msg->u.log.log_hdr.severity)) ||
+ !(iwmct_logdefs[LOG_SRC_FW_MSG] & BIT(msg->u.log.log_hdr.severity)))
+ return;
+
+ switch (msg->hdr.category) {
+ case COMM_CATEGORY_TESTABILITY:
+ if (!(iwmct_logdefs[LOG_SRC_TST] &
+ BIT(msg->u.log.log_hdr.severity)))
+ return;
+ if (log_msg_format_hex(logbuf, LOG_MSG_SIZE_MAX, buf,
+ le16_to_cpu(msg->hdr.length) +
+ sizeof(msg->hdr), "<TST>"))
+ LOG_WARNING(priv, TST,
+ "TOP TST message is too long, truncating...");
+ LOG_WARNING(priv, TST, "%s\n", logbuf);
+ break;
+ case COMM_CATEGORY_DEBUG:
+ if (msg->hdr.opcode == OP_DBG_ZSTR_MSG)
+ LOG_INFO(priv, FW_MSG, "%s %s", "<DBG>",
+ ((u8 *)msg) + sizeof(msg->hdr)
+ + sizeof(msg->u.log.log_hdr));
+ else {
+ if (log_msg_format_hex(logbuf, LOG_MSG_SIZE_MAX, buf,
+ le16_to_cpu(msg->hdr.length)
+ + sizeof(msg->hdr),
+ "<DBG>"))
+ LOG_WARNING(priv, FW_MSG,
+ "TOP DBG message is too long,"
+ "truncating...");
+ LOG_WARNING(priv, FW_MSG, "%s\n", logbuf);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static int _log_get_filter_str(u8 *logdefs, int logdefsz, char *buf, int size)
+{
+ int i, pos, len;
+ for (i = 0, pos = 0; (pos < size-1) && (i < logdefsz); i++) {
+ len = snprintf(&buf[pos], size - pos - 1, "0x%02X%02X,",
+ i, logdefs[i]);
+ pos += len;
+ }
+ buf[pos-1] = '\n';
+ buf[pos] = '\0';
+
+ if (i < logdefsz)
+ return -1;
+ return 0;
+}
+
+int log_get_filter_str(char *buf, int size)
+{
+ return _log_get_filter_str(iwmct_logdefs, LOG_SRC_MAX, buf, size);
+}
+
+int log_get_fw_filter_str(char *buf, int size)
+{
+ return _log_get_filter_str(iwmct_fw_logdefs, FW_LOG_SRC_MAX, buf, size);
+}
+
+#define HEXADECIMAL_RADIX 16
+#define LOG_SRC_FORMAT 7 /* log level is in format of "0xXXXX," */
+
+ssize_t show_iwmct_log_level(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct iwmct_priv *priv = d->driver_data;
+ char *str_buf;
+ int buf_size;
+ ssize_t ret = -EAGAIN;
+
+ buf_size = (LOG_SRC_FORMAT * LOG_SRC_MAX) + 1;
+ str_buf = kzalloc(buf_size, GFP_KERNEL);
+ if (!str_buf) {
+ LOG_ERROR(priv, DEBUGFS,
+ "failed to allocate %d bytes\n", buf_size);
+ goto exit;
+ }
+
+ if (log_get_filter_str(str_buf, buf_size) < 0)
+ goto exit;
+
+ ret = sprintf(buf, "%s", str_buf);
+
+exit:
+ kfree(str_buf);
+ return ret;
+}
+
+ssize_t store_iwmct_log_level(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct iwmct_priv *priv = d->driver_data;
+ char *token, *str_buf = NULL;
+ long val;
+ u8 src, mask;
+
+ if (!count)
+ goto err;
+
+ str_buf = kzalloc(count, GFP_KERNEL);
+ if (!str_buf) {
+ LOG_ERROR(priv, DEBUGFS,
+ "failed to allocate %zd bytes\n", count);
+ goto err;
+ }
+
+ memcpy(str_buf, buf, count);
+
+ while ((token = strsep(&str_buf, ",")) != NULL) {
+ while (isspace(*token))
+ ++token;
+ if (strict_strtol(token, HEXADECIMAL_RADIX, &val)) {
+ LOG_ERROR(priv, DEBUGFS,
+ "failed to convert string to long %s\n",
+ token);
+ goto err;
+ }
+
+ mask = val & 0xFF;
+ src = (val & 0XFF00) >> 8;
+ iwmct_log_set_filter(src, mask);
+ }
+
+ kfree(str_buf);
+ return count;
+
+err:
+ kfree(str_buf);
+ return -EAGAIN;
+}
+
+ssize_t show_iwmct_log_level_fw(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct iwmct_priv *priv = d->driver_data;
+ char *str_buf;
+ int buf_size;
+ ssize_t ret = -EAGAIN;
+
+ buf_size = (LOG_SRC_FORMAT * FW_LOG_SRC_MAX) + 2;
+
+ str_buf = kzalloc(buf_size, GFP_KERNEL);
+ if (!str_buf) {
+ LOG_ERROR(priv, DEBUGFS,
+ "failed to allocate %d bytes\n", buf_size);
+ goto exit;
+ }
+
+ if (log_get_fw_filter_str(str_buf, buf_size) < 0)
+ goto exit;
+
+ ret = sprintf(buf, "%s", str_buf);
+
+exit:
+ kfree(str_buf);
+ return ret;
+}
+
+ssize_t store_iwmct_log_level_fw(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct iwmct_priv *priv = d->driver_data;
+ char *token, *str_buf = NULL;
+ long val;
+ u8 src, mask;
+ struct top_msg cmd;
+ u16 cmdlen = 0;
+ int i, rc;
+
+ if (!count)
+ goto err;
+
+ str_buf = kzalloc(count, GFP_KERNEL);
+ if (!str_buf) {
+ LOG_ERROR(priv, DEBUGFS,
+ "failed to allocate %zd bytes\n", count);
+ goto err;
+ }
+
+ memcpy(str_buf, buf, count);
+
+ cmd.hdr.type = COMM_TYPE_H2D;
+ cmd.hdr.category = COMM_CATEGORY_DEBUG;
+ cmd.hdr.opcode = CMD_DBG_LOG_LEVEL;
+
+ for (i = 0; ((token = strsep(&str_buf, ",")) != NULL) &&
+ (i < FW_LOG_SRC_MAX); i++) {
+
+ while (isspace(*token))
+ ++token;
+
+ if (strict_strtol(token, HEXADECIMAL_RADIX, &val)) {
+ LOG_ERROR(priv, DEBUGFS,
+ "failed to convert string to long %s\n",
+ token);
+ goto err;
+ }
+
+ mask = val & 0xFF; /* LSB */
+ src = (val & 0XFF00) >> 8; /* 2nd least significant byte. */
+ iwmct_log_set_fw_filter(src, mask);
+
+ cmd.u.logdefs[i].logsource = src;
+ cmd.u.logdefs[i].sevmask = mask;
+ }
+
+ cmd.hdr.length = cpu_to_le16(i * sizeof(cmd.u.logdefs[0]));
+ cmdlen = (i * sizeof(cmd.u.logdefs[0]) + sizeof(cmd.hdr));
+
+ rc = iwmct_send_hcmd(priv, (u8 *) &cmd, cmdlen);
+ if (rc) {
+ LOG_ERROR(priv, DEBUGFS,
+ "Failed to send %d bytes of fwcmd, rc=%d\n",
+ cmdlen, rc);
+ goto err;
+ } else
+ LOG_INFO(priv, DEBUGFS, "fwcmd sent (%d bytes)\n", cmdlen);
+
+ kfree(str_buf);
+ return count;
+
+err:
+ kfree(str_buf);
+ return -EAGAIN;
+}
+
diff --git a/drivers/misc/iwmc3200top/log.h b/drivers/misc/iwmc3200top/log.h
new file mode 100644
index 0000000..aba8121
--- /dev/null
+++ b/drivers/misc/iwmc3200top/log.h
@@ -0,0 +1,158 @@
+/*
+ * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
+ * drivers/misc/iwmc3200top/log.h
+ *
+ * Copyright (C) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ *
+ * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
+ * -
+ *
+ */
+
+#ifndef __LOG_H__
+#define __LOG_H__
+
+
+/* log severity:
+ * The log levels here match FW log levels
+ * so values need to stay as is */
+#define LOG_SEV_CRITICAL 0
+#define LOG_SEV_ERROR 1
+#define LOG_SEV_WARNING 2
+#define LOG_SEV_INFO 3
+#define LOG_SEV_INFOEX 4
+
+#define LOG_SEV_FILTER_ALL \
+ (BIT(LOG_SEV_CRITICAL) | \
+ BIT(LOG_SEV_ERROR) | \
+ BIT(LOG_SEV_WARNING) | \
+ BIT(LOG_SEV_INFO) | \
+ BIT(LOG_SEV_INFOEX))
+
+/* log source */
+#define LOG_SRC_INIT 0
+#define LOG_SRC_DEBUGFS 1
+#define LOG_SRC_FW_DOWNLOAD 2
+#define LOG_SRC_FW_MSG 3
+#define LOG_SRC_TST 4
+#define LOG_SRC_IRQ 5
+
+#define LOG_SRC_MAX 6
+#define LOG_SRC_ALL 0xFF
+
+/**
+ * Default intitialization runtime log level
+ */
+#ifndef LOG_SEV_FILTER_RUNTIME
+#define LOG_SEV_FILTER_RUNTIME \
+ (BIT(LOG_SEV_CRITICAL) | \
+ BIT(LOG_SEV_ERROR) | \
+ BIT(LOG_SEV_WARNING))
+#endif
+
+#ifndef FW_LOG_SEV_FILTER_RUNTIME
+#define FW_LOG_SEV_FILTER_RUNTIME LOG_SEV_FILTER_ALL
+#endif
+
+#ifdef CONFIG_IWMC3200TOP_DEBUG
+/**
+ * Log macros
+ */
+
+#define priv2dev(priv) (&(priv->func)->dev)
+
+#define LOG_CRITICAL(priv, src, fmt, args...) \
+do { \
+ if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_CRITICAL)) \
+ dev_crit(priv2dev(priv), "%s %d: " fmt, \
+ __func__, __LINE__, ##args); \
+} while (0)
+
+#define LOG_ERROR(priv, src, fmt, args...) \
+do { \
+ if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_ERROR)) \
+ dev_err(priv2dev(priv), "%s %d: " fmt, \
+ __func__, __LINE__, ##args); \
+} while (0)
+
+#define LOG_WARNING(priv, src, fmt, args...) \
+do { \
+ if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_WARNING)) \
+ dev_warn(priv2dev(priv), "%s %d: " fmt, \
+ __func__, __LINE__, ##args); \
+} while (0)
+
+#define LOG_INFO(priv, src, fmt, args...) \
+do { \
+ if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_INFO)) \
+ dev_info(priv2dev(priv), "%s %d: " fmt, \
+ __func__, __LINE__, ##args); \
+} while (0)
+
+#define LOG_INFOEX(priv, src, fmt, args...) \
+do { \
+ if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_INFOEX)) \
+ dev_dbg(priv2dev(priv), "%s %d: " fmt, \
+ __func__, __LINE__, ##args); \
+} while (0)
+
+#define LOG_HEXDUMP(src, ptr, len) \
+do { \
+ if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_INFOEX)) \
+ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_NONE, \
+ 16, 1, ptr, len, false); \
+} while (0)
+
+void iwmct_log_top_message(struct iwmct_priv *priv, u8 *buf, int len);
+
+extern u8 iwmct_logdefs[];
+
+int iwmct_log_set_filter(u8 src, u8 logmask);
+int iwmct_log_set_fw_filter(u8 src, u8 logmask);
+
+ssize_t show_iwmct_log_level(struct device *d,
+ struct device_attribute *attr, char *buf);
+ssize_t store_iwmct_log_level(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+ssize_t show_iwmct_log_level_fw(struct device *d,
+ struct device_attribute *attr, char *buf);
+ssize_t store_iwmct_log_level_fw(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+#else
+
+#define LOG_CRITICAL(priv, src, fmt, args...)
+#define LOG_ERROR(priv, src, fmt, args...)
+#define LOG_WARNING(priv, src, fmt, args...)
+#define LOG_INFO(priv, src, fmt, args...)
+#define LOG_INFOEX(priv, src, fmt, args...)
+#define LOG_HEXDUMP(src, ptr, len)
+
+static inline void iwmct_log_top_message(struct iwmct_priv *priv,
+ u8 *buf, int len) {}
+static inline int iwmct_log_set_filter(u8 src, u8 logmask) { return 0; }
+static inline int iwmct_log_set_fw_filter(u8 src, u8 logmask) { return 0; }
+
+#endif /* CONFIG_IWMC3200TOP_DEBUG */
+
+int log_get_filter_str(char *buf, int size);
+int log_get_fw_filter_str(char *buf, int size);
+
+#endif /* __LOG_H__ */
diff --git a/drivers/misc/iwmc3200top/main.c b/drivers/misc/iwmc3200top/main.c
new file mode 100644
index 0000000..09f8d06
--- /dev/null
+++ b/drivers/misc/iwmc3200top/main.c
@@ -0,0 +1,702 @@
+/*
+ * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
+ * drivers/misc/iwmc3200top/main.c
+ *
+ * Copyright (C) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ *
+ * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
+ * -
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio.h>
+
+#include "iwmc3200top.h"
+#include "log.h"
+#include "fw-msg.h"
+#include "debugfs.h"
+
+
+#define DRIVER_DESCRIPTION "Intel(R) IWMC 3200 Top Driver"
+#define DRIVER_COPYRIGHT "Copyright (c) 2008 Intel Corporation."
+
+#define IWMCT_VERSION "0.1.62"
+
+#ifdef REPOSITORY_LABEL
+#define RL REPOSITORY_LABEL
+#else
+#define RL ""
+#endif
+
+#ifdef CONFIG_IWMC3200TOP_DEBUG
+#define VD "-d"
+#else
+#define VD
+#endif
+
+#define DRIVER_VERSION IWMCT_VERSION "-" __stringify(RL) VD
+
+MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR(DRIVER_COPYRIGHT);
+
+
+#ifndef SDIO_DEVICE_ID_INTEL_IWMC3200TOP
+#define SDIO_DEVICE_ID_INTEL_IWMC3200TOP 0x1404
+#endif
+
+/*
+ * This workers main task is to wait for OP_OPR_ALIVE
+ * from TOP FW until ALIVE_MSG_TIMOUT timeout is elapsed.
+ * When OP_OPR_ALIVE received it will issue
+ * a call to "bus_rescan_devices".
+ */
+static void iwmct_rescan_worker(struct work_struct *ws)
+{
+ struct iwmct_priv *priv;
+ int ret;
+
+ priv = container_of(ws, struct iwmct_priv, bus_rescan_worker);
+
+ LOG_INFO(priv, FW_MSG, "Calling bus_rescan\n");
+
+ ret = bus_rescan_devices(priv->func->dev.bus);
+ if (ret < 0)
+ LOG_INFO(priv, FW_DOWNLOAD, "bus_rescan_devices FAILED!!!\n");
+}
+
+static void op_top_message(struct iwmct_priv *priv, struct top_msg *msg)
+{
+ switch (msg->hdr.opcode) {
+ case OP_OPR_ALIVE:
+ LOG_INFO(priv, FW_MSG, "Got ALIVE from device, wake rescan\n");
+ queue_work(priv->bus_rescan_wq, &priv->bus_rescan_worker);
+ break;
+ default:
+ LOG_INFO(priv, FW_MSG, "Received msg opcode 0x%X\n",
+ msg->hdr.opcode);
+ break;
+ }
+}
+
+
+static void handle_top_message(struct iwmct_priv *priv, u8 *buf, int len)
+{
+ struct top_msg *msg;
+
+ msg = (struct top_msg *)buf;
+
+ if (msg->hdr.type != COMM_TYPE_D2H) {
+ LOG_ERROR(priv, FW_MSG,
+ "Message from TOP with invalid message type 0x%X\n",
+ msg->hdr.type);
+ return;
+ }
+
+ if (len < sizeof(msg->hdr)) {
+ LOG_ERROR(priv, FW_MSG,
+ "Message from TOP is too short for message header "
+ "received %d bytes, expected at least %zd bytes\n",
+ len, sizeof(msg->hdr));
+ return;
+ }
+
+ if (len < le16_to_cpu(msg->hdr.length) + sizeof(msg->hdr)) {
+ LOG_ERROR(priv, FW_MSG,
+ "Message length (%d bytes) is shorter than "
+ "in header (%d bytes)\n",
+ len, le16_to_cpu(msg->hdr.length));
+ return;
+ }
+
+ switch (msg->hdr.category) {
+ case COMM_CATEGORY_OPERATIONAL:
+ op_top_message(priv, (struct top_msg *)buf);
+ break;
+
+ case COMM_CATEGORY_DEBUG:
+ case COMM_CATEGORY_TESTABILITY:
+ case COMM_CATEGORY_DIAGNOSTICS:
+ iwmct_log_top_message(priv, buf, len);
+ break;
+
+ default:
+ LOG_ERROR(priv, FW_MSG,
+ "Message from TOP with unknown category 0x%X\n",
+ msg->hdr.category);
+ break;
+ }
+}
+
+int iwmct_send_hcmd(struct iwmct_priv *priv, u8 *cmd, u16 len)
+{
+ int ret;
+ u8 *buf;
+
+ LOG_INFOEX(priv, FW_MSG, "Sending hcmd:\n");
+
+ /* add padding to 256 for IWMC */
+ ((struct top_msg *)cmd)->hdr.flags |= CMD_FLAG_PADDING_256;
+
+ LOG_HEXDUMP(FW_MSG, cmd, len);
+
+ if (len > FW_HCMD_BLOCK_SIZE) {
+ LOG_ERROR(priv, FW_MSG, "size %d exceeded hcmd max size %d\n",
+ len, FW_HCMD_BLOCK_SIZE);
+ return -1;
+ }
+
+ buf = kzalloc(FW_HCMD_BLOCK_SIZE, GFP_KERNEL);
+ if (!buf) {
+ LOG_ERROR(priv, FW_MSG, "kzalloc error, buf size %d\n",
+ FW_HCMD_BLOCK_SIZE);
+ return -1;
+ }
+
+ memcpy(buf, cmd, len);
+
+ sdio_claim_host(priv->func);
+ ret = sdio_memcpy_toio(priv->func, IWMC_SDIO_DATA_ADDR, buf,
+ FW_HCMD_BLOCK_SIZE);
+ sdio_release_host(priv->func);
+
+ kfree(buf);
+ return ret;
+}
+
+int iwmct_tx(struct iwmct_priv *priv, unsigned int addr,
+ void *src, int count)
+{
+ int ret;
+
+ sdio_claim_host(priv->func);
+ ret = sdio_memcpy_toio(priv->func, addr, src, count);
+ sdio_release_host(priv->func);
+
+ return ret;
+}
+
+static void iwmct_irq_read_worker(struct work_struct *ws)
+{
+ struct iwmct_priv *priv;
+ struct iwmct_work_struct *read_req;
+ __le32 *buf = NULL;
+ int ret, val, i;
+ int iosize;
+ u32 barker;
+
+ priv = container_of(ws, struct iwmct_priv, isr_worker);
+
+ LOG_INFO(priv, IRQ, "enter iwmct_irq_read_worker %p\n", ws);
+
+ /* --------------------- Handshake with device -------------------- */
+ sdio_claim_host(priv->func);
+
+ /* all list manipulations have to be protected by
+ * sdio_claim_host/sdio_release_host */
+ if (list_empty(&priv->read_req_list)) {
+ LOG_ERROR(priv, IRQ, "read_req_list empty in read worker\n");
+ goto exit_release;
+ }
+
+ read_req = list_entry(priv->read_req_list.next,
+ struct iwmct_work_struct, list);
+
+ list_del(&read_req->list);
+ iosize = read_req->iosize;
+ kfree(read_req);
+
+ buf = kzalloc(iosize, GFP_KERNEL);
+ if (!buf) {
+ LOG_ERROR(priv, IRQ, "kzalloc error, buf size %d\n", iosize);
+ goto exit_release;
+ }
+
+ LOG_INFO(priv, IRQ, "iosize=%d, buf=%p, func=%d\n",
+ iosize, buf, priv->func->num);
+
+ /* read from device */
+ ret = sdio_memcpy_fromio(priv->func, buf, IWMC_SDIO_DATA_ADDR, iosize);
+ if (ret) {
+ LOG_ERROR(priv, IRQ, "error %d reading buffer\n", ret);
+ goto exit_release;
+ }
+
+ LOG_HEXDUMP(IRQ, (u8 *)buf, iosize);
+
+ barker = le32_to_cpu(buf[0]);
+ if (barker != IWMC_BARKER_ACK &&
+ (barker & BARKER_DNLOAD_BARKER_MSK) != IWMC_BARKER_REBOOT) {
+ /* Handle Top Commhub message.
+ * Current handling - treat all message as
+ * logger messages and print the context as a string. */
+ sdio_release_host(priv->func);
+ handle_top_message(priv, (u8 *)buf, iosize);
+ goto exit;
+ }
+
+ /* verify basic barker sanity */
+ for (i = 1; i < 4; i++)
+ if (buf[i] != buf[0]) {
+ LOG_ERROR(priv, IRQ, "Corrupted barker,"
+ "buf[%d]=%x buf[0]=%x\n",
+ i, buf[i], buf[0]);
+ goto exit_release;
+ }
+
+ val = atomic_read(&priv->dev_sync);
+
+ switch (val) {
+ case 0:
+ /* we expect to recieve REBOOT BARKER */
+ if ((barker & BARKER_DNLOAD_BARKER_MSK) != IWMC_BARKER_REBOOT) {
+ LOG_ERROR(priv, IRQ, "Expecting for reboot barker %x,"
+ "but got %x\n",
+ IWMC_BARKER_REBOOT,
+ (barker & BARKER_DNLOAD_BARKER_MSK));
+ goto exit_release;
+ }
+
+ LOG_INFO(priv, IRQ, "Recieved reboot barker: %x\n", barker);
+ priv->barker = barker;
+
+ if (barker & BARKER_DNLOAD_SYNC_MSK) {
+ /* Send the same barker back */
+ ret = sdio_memcpy_toio(priv->func, IWMC_SDIO_DATA_ADDR,
+ buf, iosize);
+ if (ret) {
+ LOG_ERROR(priv, IRQ,
+ "error %d echoing barker\n", ret);
+ goto exit_release;
+ }
+ LOG_INFO(priv, IRQ, "Echoing barker to device\n");
+ atomic_inc(&priv->dev_sync);
+ goto exit_release;
+ }
+ break;
+
+ case 1:
+ /* we expect to recieve ACK BARKER */
+ if (barker != IWMC_BARKER_ACK) {
+ LOG_INFO(priv, IRQ, "Expecting for ACK barker[%x],"
+ "got this instead %x\n",
+ IWMC_BARKER_ACK, barker);
+ goto exit_release;
+ }
+ kfree(buf);
+ atomic_set(&priv->dev_sync, 0);
+ atomic_set(&priv->reset, 0);
+ break;
+
+ default:
+ LOG_INFO(priv, IRQ, "Wrong dev_sync %d\n", val);
+ break;
+ }
+
+ sdio_release_host(priv->func);
+
+
+ LOG_INFO(priv, IRQ, "barker download request 0x%x is:\n", priv->barker);
+ LOG_INFO(priv, IRQ, "******* Top FW %s requested ********\n",
+ (priv->barker & BARKER_DNLOAD_TOP_MSK) ? "was" : "not");
+ LOG_INFO(priv, IRQ, "******* GPS FW %s requested ********\n",
+ (priv->barker & BARKER_DNLOAD_GPS_MSK) ? "was" : "not");
+ LOG_INFO(priv, IRQ, "******* BT FW %s requested ********\n",
+ (priv->barker & BARKER_DNLOAD_BT_MSK) ? "was" : "not");
+
+ if (priv->dbg.fw_download)
+ iwmct_fw_load(priv);
+ else
+ LOG_ERROR(priv, IRQ, "FW download not allowed\n");
+
+ return;
+
+exit_release:
+ sdio_release_host(priv->func);
+exit:
+ kfree(buf);
+ LOG_INFO(priv, IRQ, "exit iwmct_irq_read_worker\n");
+}
+
+static void iwmct_irq(struct sdio_func *func)
+{
+ struct iwmct_priv *priv;
+ int val, ret;
+ int iosize;
+ int addr = IWMC_SDIO_INTR_GET_SIZE_ADDR;
+ struct iwmct_work_struct *read_req;
+
+ priv = sdio_get_drvdata(func);
+
+ LOG_INFO(priv, IRQ, "enter iwmct_irq\n");
+
+ /* read the function's status register */
+ val = sdio_readb(func, IWMC_SDIO_INTR_STATUS_ADDR, &ret);
+
+ LOG_INFO(priv, IRQ, "iir value = %d, ret=%d\n", val, ret);
+
+ if (!val) {
+ LOG_ERROR(priv, IRQ, "iir = 0, exiting ISR\n");
+ goto exit_clear_intr;
+ }
+
+
+ /*
+ * read 2 bytes of the transaction size
+ * IMPORTANT: sdio transaction size has to be read before clearing
+ * sdio interrupt!!!
+ */
+ val = sdio_readb(priv->func, addr++, &ret);
+ iosize = val;
+ val = sdio_readb(priv->func, addr++, &ret);
+ iosize += val << 8;
+
+ LOG_INFO(priv, IRQ, "READ size %d\n", iosize);
+
+ if (iosize == 0) {
+ LOG_ERROR(priv, IRQ, "READ size %d, exiting ISR\n", iosize);
+ goto exit_clear_intr;
+ }
+
+ /* allocate a work structure to pass iosize to the worker */
+ read_req = kzalloc(sizeof(struct iwmct_work_struct), GFP_KERNEL);
+ if (!read_req) {
+ LOG_ERROR(priv, IRQ, "failed to allocate read_req, exit ISR\n");
+ goto exit_clear_intr;
+ }
+
+ INIT_LIST_HEAD(&read_req->list);
+ read_req->iosize = iosize;
+
+ list_add_tail(&priv->read_req_list, &read_req->list);
+
+ /* clear the function's interrupt request bit (write 1 to clear) */
+ sdio_writeb(func, 1, IWMC_SDIO_INTR_CLEAR_ADDR, &ret);
+
+ queue_work(priv->wq, &priv->isr_worker);
+
+ LOG_INFO(priv, IRQ, "exit iwmct_irq\n");
+
+ return;
+
+exit_clear_intr:
+ /* clear the function's interrupt request bit (write 1 to clear) */
+ sdio_writeb(func, 1, IWMC_SDIO_INTR_CLEAR_ADDR, &ret);
+}
+
+
+static int blocks;
+module_param(blocks, int, 0604);
+MODULE_PARM_DESC(blocks, "max_blocks_to_send");
+
+static int dump;
+module_param(dump, bool, 0604);
+MODULE_PARM_DESC(dump, "dump_hex_content");
+
+static int jump = 1;
+module_param(jump, bool, 0604);
+
+static int direct = 1;
+module_param(direct, bool, 0604);
+
+static int checksum = 1;
+module_param(checksum, bool, 0604);
+
+static int fw_download = 1;
+module_param(fw_download, bool, 0604);
+
+static int block_size = IWMC_SDIO_BLK_SIZE;
+module_param(block_size, int, 0404);
+
+static int download_trans_blks = IWMC_DEFAULT_TR_BLK;
+module_param(download_trans_blks, int, 0604);
+
+static int rubbish_barker;
+module_param(rubbish_barker, bool, 0604);
+
+#ifdef CONFIG_IWMC3200TOP_DEBUG
+static int log_level[LOG_SRC_MAX];
+static unsigned int log_level_argc;
+module_param_array(log_level, int, &log_level_argc, 0604);
+MODULE_PARM_DESC(log_level, "log_level");
+
+static int log_level_fw[FW_LOG_SRC_MAX];
+static unsigned int log_level_fw_argc;
+module_param_array(log_level_fw, int, &log_level_fw_argc, 0604);
+MODULE_PARM_DESC(log_level_fw, "log_level_fw");
+#endif
+
+void iwmct_dbg_init_params(struct iwmct_priv *priv)
+{
+#ifdef CONFIG_IWMC3200TOP_DEBUG
+ int i;
+
+ for (i = 0; i < log_level_argc; i++) {
+ dev_notice(&priv->func->dev, "log_level[%d]=0x%X\n",
+ i, log_level[i]);
+ iwmct_log_set_filter((log_level[i] >> 8) & 0xFF,
+ log_level[i] & 0xFF);
+ }
+ for (i = 0; i < log_level_fw_argc; i++) {
+ dev_notice(&priv->func->dev, "log_level_fw[%d]=0x%X\n",
+ i, log_level_fw[i]);
+ iwmct_log_set_fw_filter((log_level_fw[i] >> 8) & 0xFF,
+ log_level_fw[i] & 0xFF);
+ }
+#endif
+
+ priv->dbg.blocks = blocks;
+ LOG_INFO(priv, INIT, "blocks=%d\n", blocks);
+ priv->dbg.dump = (bool)dump;
+ LOG_INFO(priv, INIT, "dump=%d\n", dump);
+ priv->dbg.jump = (bool)jump;
+ LOG_INFO(priv, INIT, "jump=%d\n", jump);
+ priv->dbg.direct = (bool)direct;
+ LOG_INFO(priv, INIT, "direct=%d\n", direct);
+ priv->dbg.checksum = (bool)checksum;
+ LOG_INFO(priv, INIT, "checksum=%d\n", checksum);
+ priv->dbg.fw_download = (bool)fw_download;
+ LOG_INFO(priv, INIT, "fw_download=%d\n", fw_download);
+ priv->dbg.block_size = block_size;
+ LOG_INFO(priv, INIT, "block_size=%d\n", block_size);
+ priv->dbg.download_trans_blks = download_trans_blks;
+ LOG_INFO(priv, INIT, "download_trans_blks=%d\n", download_trans_blks);
+}
+
+/*****************************************************************************
+ *
+ * sysfs attributes
+ *
+ *****************************************************************************/
+static ssize_t show_iwmct_fw_version(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n",
+ ((struct iwmct_priv *)d->driver_data)->dbg.label_fw);
+}
+static DEVICE_ATTR(cc_label_fw, S_IRUGO, show_iwmct_fw_version, NULL);
+
+#ifdef CONFIG_IWMC3200TOP_DEBUG
+static DEVICE_ATTR(log_level, S_IWUSR | S_IRUGO,
+ show_iwmct_log_level, store_iwmct_log_level);
+static DEVICE_ATTR(log_level_fw, S_IWUSR | S_IRUGO,
+ show_iwmct_log_level_fw, store_iwmct_log_level_fw);
+#endif
+
+static struct attribute *iwmct_sysfs_entries[] = {
+ &dev_attr_cc_label_fw.attr,
+#ifdef CONFIG_IWMC3200TOP_DEBUG
+ &dev_attr_log_level.attr,
+ &dev_attr_log_level_fw.attr,
+#endif
+ NULL
+};
+
+static struct attribute_group iwmct_attribute_group = {
+ .name = NULL, /* put in device directory */
+ .attrs = iwmct_sysfs_entries,
+};
+
+
+static int iwmct_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ struct iwmct_priv *priv;
+ int ret;
+ int val = 1;
+ int addr = IWMC_SDIO_INTR_ENABLE_ADDR;
+
+ dev_info(&func->dev, "enter iwmct_probe\n");
+
+ dev_info(&func->dev, "IRQ polling period id %u msecs, HZ is %d\n",
+ jiffies_to_msecs(2147483647), HZ);
+
+ priv = kzalloc(sizeof(struct iwmct_priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&func->dev, "kzalloc error\n");
+ return -ENOMEM;
+ }
+ priv->func = func;
+ sdio_set_drvdata(func, priv);
+
+ LOG_INFO(priv, INIT, "HIDR supported\n");
+
+ /* create drivers work queue */
+ priv->wq = create_workqueue(DRV_NAME "_wq");
+ priv->bus_rescan_wq = create_workqueue(DRV_NAME "_rescan_wq");
+ INIT_WORK(&priv->bus_rescan_worker, iwmct_rescan_worker);
+ INIT_WORK(&priv->isr_worker, iwmct_irq_read_worker);
+
+ init_waitqueue_head(&priv->wait_q);
+
+ sdio_claim_host(func);
+ /* FIXME: Remove after it is fixed in the Boot ROM upgrade */
+ func->enable_timeout = 10;
+
+ /* In our HW, setting the block size also wakes up the boot rom. */
+ ret = sdio_set_block_size(func, priv->dbg.block_size);
+ if (ret) {
+ LOG_ERROR(priv, INIT,
+ "sdio_set_block_size() failure: %d\n", ret);
+ goto error_sdio_enable;
+ }
+
+ ret = sdio_enable_func(func);
+ if (ret) {
+ LOG_ERROR(priv, INIT, "sdio_enable_func() failure: %d\n", ret);
+ goto error_sdio_enable;
+ }
+
+ /* init reset and dev_sync states */
+ atomic_set(&priv->reset, 0);
+ atomic_set(&priv->dev_sync, 0);
+
+ /* init read req queue */
+ INIT_LIST_HEAD(&priv->read_req_list);
+
+ /* process configurable parameters */
+ iwmct_dbg_init_params(priv);
+ ret = sysfs_create_group(&func->dev.kobj, &iwmct_attribute_group);
+ if (ret) {
+ LOG_ERROR(priv, INIT, "Failed to register attributes and "
+ "initialize module_params\n");
+ goto error_dev_attrs;
+ }
+
+ iwmct_dbgfs_register(priv, DRV_NAME);
+
+ if (!priv->dbg.direct && priv->dbg.download_trans_blks > 8) {
+ LOG_INFO(priv, INIT,
+ "Reducing transaction to 8 blocks = 2K (from %d)\n",
+ priv->dbg.download_trans_blks);
+ priv->dbg.download_trans_blks = 8;
+ }
+ priv->trans_len = priv->dbg.download_trans_blks * priv->dbg.block_size;
+ LOG_INFO(priv, INIT, "Transaction length = %d\n", priv->trans_len);
+
+ ret = sdio_claim_irq(func, iwmct_irq);
+ if (ret) {
+ LOG_ERROR(priv, INIT, "sdio_claim_irq() failure: %d\n", ret);
+ goto error_claim_irq;
+ }
+
+
+ /* Enable function's interrupt */
+ sdio_writeb(priv->func, val, addr, &ret);
+ if (ret) {
+ LOG_ERROR(priv, INIT, "Failure writing to "
+ "Interrupt Enable Register (%d): %d\n", addr, ret);
+ goto error_enable_int;
+ }
+
+ sdio_release_host(func);
+
+ LOG_INFO(priv, INIT, "exit iwmct_probe\n");
+
+ return ret;
+
+error_enable_int:
+ sdio_release_irq(func);
+error_claim_irq:
+ sdio_disable_func(func);
+error_dev_attrs:
+ iwmct_dbgfs_unregister(priv->dbgfs);
+ sysfs_remove_group(&func->dev.kobj, &iwmct_attribute_group);
+error_sdio_enable:
+ sdio_release_host(func);
+ return ret;
+}
+
+static void iwmct_remove(struct sdio_func *func)
+{
+ struct iwmct_work_struct *read_req;
+ struct iwmct_priv *priv = sdio_get_drvdata(func);
+
+ priv = sdio_get_drvdata(func);
+
+ LOG_INFO(priv, INIT, "enter\n");
+
+ sdio_claim_host(func);
+ sdio_release_irq(func);
+ sdio_release_host(func);
+
+ /* Safely destroy osc workqueue */
+ destroy_workqueue(priv->bus_rescan_wq);
+ destroy_workqueue(priv->wq);
+
+ sdio_claim_host(func);
+ sdio_disable_func(func);
+ sysfs_remove_group(&func->dev.kobj, &iwmct_attribute_group);
+ iwmct_dbgfs_unregister(priv->dbgfs);
+ sdio_release_host(func);
+
+ /* free read requests */
+ while (!list_empty(&priv->read_req_list)) {
+ read_req = list_entry(priv->read_req_list.next,
+ struct iwmct_work_struct, list);
+
+ list_del(&read_req->list);
+ kfree(read_req);
+ }
+
+ kfree(priv);
+}
+
+
+static const struct sdio_device_id iwmct_ids[] = {
+ { SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, SDIO_DEVICE_ID_INTEL_IWMC3200TOP)},
+ { /* end: all zeroes */ },
+};
+
+MODULE_DEVICE_TABLE(sdio, iwmct_ids);
+
+static struct sdio_driver iwmct_driver = {
+ .probe = iwmct_probe,
+ .remove = iwmct_remove,
+ .name = DRV_NAME,
+ .id_table = iwmct_ids,
+};
+
+static int __init iwmct_init(void)
+{
+ int rc;
+
+ /* Default log filter settings */
+ iwmct_log_set_filter(LOG_SRC_ALL, LOG_SEV_FILTER_RUNTIME);
+ iwmct_log_set_filter(LOG_SRC_FW_MSG, LOG_SEV_FILTER_ALL);
+ iwmct_log_set_fw_filter(LOG_SRC_ALL, FW_LOG_SEV_FILTER_RUNTIME);
+
+ rc = sdio_register_driver(&iwmct_driver);
+
+ return rc;
+}
+
+static void __exit iwmct_exit(void)
+{
+ sdio_unregister_driver(&iwmct_driver);
+}
+
+module_init(iwmct_init);
+module_exit(iwmct_exit);
+
--
1.6.0.6
---------------------------------------------------------------------
Intel Israel (74) Limited
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply related
* [PATCH 0/3 net] Add iwmc3200top driver
From: Tomas Winkler @ 2009-09-22 23:38 UTC (permalink / raw)
To: davem, linville, netdev, linux-wireless, linux-mmc
Cc: yi.zhu, inaky.perez-gonzalez, cindy.h.kao, guy.cohen,
ron.rindjunsky
iwmc3200top: Add Intel Wireless MultiCom 3200 top driver.
iwmc3200wifi: select IWMC3200TOP in Kconfig
i2400m-sdio: select IWMC3200TOP in Kconfig
Add iwmc3200top driver. Imwc3200 is under drivers/misc driver but IMWC network
drivers depends on it therefore submitted through netdev
---------------------------------------------------------------------
Intel Israel (74) Limited
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply
* Re: [PATCH][RESEND 3] IPv6: 6rd tunnel mode
From: Joe Perches @ 2009-09-22 23:06 UTC (permalink / raw)
To: Alexandre Cassen; +Cc: netdev
In-Reply-To: <20090922220251.GA22874@lnxos.staff.proxad.net>
On Wed, 2009-09-23 at 00:02 +0200, Alexandre Cassen wrote:
> diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
> index 0ae4f64..3587149 100644
> --- a/net/ipv6/sit.c
> +++ b/net/ipv6/sit.c
[]
> +static inline __be32 try_6rd(struct in6_addr *addr, u8 prefix_len, struct in6_addr *v6dst)
> +{
> + __be32 dst = 0;
> +
> + /* isolate addr according to mask */
> + if (ipv6_prefix_equal(v6dst, addr, prefix_len)) {
> + unsigned int d32_off, bits;
> +
> + d32_off = prefix_len >> 5;
> + bits = (prefix_len & 0x1f);
> +
> + dst = (ntohl(v6dst->s6_addr32[d32_off]) << bits);
unnecessary parens and a sparse warning?
Perhaps use a temporary u32 and an htonl or cpu_to_be32 at the end?
> + if (bits)
> + dst |= ntohl(v6dst->s6_addr32[d32_off + 1]) >> (32 - bits);
> + dst = htonl(dst);
> + }
> + return dst;
> +}
> +#endif
^ permalink raw reply
* Re: [GIT *] Solos PCI ADSL driver update
From: David Miller @ 2009-09-22 22:19 UTC (permalink / raw)
To: dwmw2; +Cc: netdev
In-Reply-To: <1253481424.6317.203.camel@macbook.infradead.org>
From: David Woodhouse <dwmw2@infradead.org>
Date: Sun, 20 Sep 2009 14:17:04 -0700
> I'm sorry, I suck. I forgot I had anything outstanding to be pushed
> upstream, and should have done this a while ago.
>
> In later versions of the card's FPGA code, the RAM layout for
> card<->host communication has changed -- and the new FPGA code is in the
> devices which are being shipped now, so it would be really useful if we
> could sneak this into 2.6.32. Sorry it's so late.
>
> Please pull from git://git.infradead.org/users/dwmw2/solos-2.6.git
Pulled, thanks.
^ permalink raw reply
* Re: [PATCH][RESEND 3] IPv6: 6rd tunnel mode
From: Eric Dumazet @ 2009-09-22 22:18 UTC (permalink / raw)
To: Alexandre Cassen; +Cc: netdev
In-Reply-To: <20090922220251.GA22874@lnxos.staff.proxad.net>
Alexandre Cassen a écrit :
> This patch add support to 6rd tunnel mode as described into
> draft-despres-6rd-03.
>
> Patch history :
> * http://patchwork.ozlabs.org/patch/26870/
> * http://patchwork.ozlabs.org/patch/34026/
> * http://patchwork.ozlabs.org/patch/34045/
>
> IPv6 rapid deployment (draft-despres-6rd-03) builds upon mechanisms
> of 6to4 (RFC3056) to enable a service provider to rapidly deploy IPv6
> unicast service to IPv4 sites to which it provides customer premise
> equipment. Like 6to4, it utilizes stateless IPv6 in IPv4 encapsulation
> in order to transit IPv4-only network infrastructure. Unlike 6to4, a
> 6rd service provider uses an IPv6 prefix of its own in place of the
> fixed 6to4 prefix.
>
> Signed-off-by: Alexandre Cassen <acassen@freebox.fr>
> ---
> include/linux/if_tunnel.h | 10 +++++
> include/net/ipip.h | 2 +
> net/ipv6/Kconfig | 13 +++++++
> net/ipv6/sit.c | 84 +++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 109 insertions(+), 0 deletions(-)
>
> diff --git a/include/linux/if_tunnel.h b/include/linux/if_tunnel.h
> index 5eb9b0f..0d44376 100644
> --- a/include/linux/if_tunnel.h
> +++ b/include/linux/if_tunnel.h
> @@ -15,6 +15,10 @@
> #define SIOCADDPRL (SIOCDEVPRIVATE + 5)
> #define SIOCDELPRL (SIOCDEVPRIVATE + 6)
> #define SIOCCHGPRL (SIOCDEVPRIVATE + 7)
> +#define SIOCGET6RD (SIOCDEVPRIVATE + 8)
> +#define SIOCADD6RD (SIOCDEVPRIVATE + 9)
> +#define SIOCDEL6RD (SIOCDEVPRIVATE + 10)
> +#define SIOCCHG6RD (SIOCDEVPRIVATE + 11)
>
> #define GRE_CSUM __cpu_to_be16(0x8000)
> #define GRE_ROUTING __cpu_to_be16(0x4000)
> @@ -51,6 +55,12 @@ struct ip_tunnel_prl {
> /* PRL flags */
> #define PRL_DEFAULT 0x0001
>
> +/* 6RD parms */
> +struct ip_tunnel_6rd {
> + struct in6_addr addr;
> + __u8 prefixlen;
> +};
> +
> enum
> {
> IFLA_GRE_UNSPEC,
> diff --git a/include/net/ipip.h b/include/net/ipip.h
> index 5d3036f..fa92c41 100644
> --- a/include/net/ipip.h
> +++ b/include/net/ipip.h
> @@ -26,6 +26,8 @@ struct ip_tunnel
>
> struct ip_tunnel_prl_entry *prl; /* potential router list */
> unsigned int prl_count; /* # of entries in PRL */
> +
> + struct ip_tunnel_6rd ip6rd_prefix; /* 6RD SP prefix */
> };
>
> /* ISATAP: default interval between RS in secondy */
> diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
> index ead6c7a..8b779f7 100644
> --- a/net/ipv6/Kconfig
> +++ b/net/ipv6/Kconfig
> @@ -170,6 +170,19 @@ config IPV6_SIT
>
> Saying M here will produce a module called sit. If unsure, say Y.
>
> +config IPV6_SIT_6RD
> + bool "IPv6: 6rd tunnel mode (EXPERIMENTAL)"
> + depends on IPV6_SIT && EXPERIMENTAL
> + default n
> + ---help---
> + IPv6 rapid deployment (draft-despres-6rd-03) builds upon mechanisms
> + of 6to4 (RFC3056) to enable a service provider to rapidly deploy IPv6
> + unicast service to IPv4 sites to which it provides customer premise
> + equipment. Like 6to4, it utilizes stateless IPv6 in IPv4 encapsulation
> + in order to transit IPv4-only network infrastructure. Unlike 6to4, a
> + 6rd service provider uses an IPv6 prefix of its own in place of the
> + fixed 6to4 prefix.
> +
> config IPV6_NDISC_NODETYPE
> bool
>
> diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
> index 0ae4f64..3587149 100644
> --- a/net/ipv6/sit.c
> +++ b/net/ipv6/sit.c
> @@ -604,6 +604,30 @@ static inline __be32 try_6to4(struct in6_addr *v6dst)
> return dst;
> }
>
> +#ifdef CONFIG_IPV6_SIT_6RD
> +/* Returns the embedded IPv4 address if the IPv6 address comes from
> + 6rd rule */
> +
> +static inline __be32 try_6rd(struct in6_addr *addr, u8 prefix_len, struct in6_addr *v6dst)
> +{
> + __be32 dst = 0;
> +
> + /* isolate addr according to mask */
> + if (ipv6_prefix_equal(v6dst, addr, prefix_len)) {
> + unsigned int d32_off, bits;
> +
> + d32_off = prefix_len >> 5;
> + bits = (prefix_len & 0x1f);
> +
> + dst = (ntohl(v6dst->s6_addr32[d32_off]) << bits);
> + if (bits)
> + dst |= ntohl(v6dst->s6_addr32[d32_off + 1]) >> (32 - bits);
> + dst = htonl(dst);
> + }
> + return dst;
> +}
> +#endif
> +
> /*
> * This function assumes it is being called from dev_queue_xmit()
> * and that skb is filled properly by that function.
> @@ -657,6 +681,13 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
> goto tx_error;
> }
>
> +#ifdef CONFIG_IPV6_SIT_6RD
> + if (!dst && tunnel->ip6rd_prefix.prefixlen)
> + dst = try_6rd(&tunnel->ip6rd_prefix.addr,
> + tunnel->ip6rd_prefix.prefixlen,
> + &iph6->daddr);
> + else
> +#endif
> if (!dst)
> dst = try_6to4(&iph6->daddr);
>
> @@ -848,6 +879,9 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
> int err = 0;
> struct ip_tunnel_parm p;
> struct ip_tunnel_prl prl;
> +#ifdef CONFIG_IPV6_SIT_6RD
> + struct ip_tunnel_6rd ip6rd;
> +#endif
> struct ip_tunnel *t;
> struct net *net = dev_net(dev);
> struct sit_net *sitn = net_generic(net, sit_net_id);
> @@ -987,6 +1021,56 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
> netdev_state_change(dev);
> break;
>
> +#ifdef CONFIG_IPV6_SIT_6RD
> + case SIOCGET6RD:
> + err = -EINVAL;
> + if (dev == sitn->fb_tunnel_dev)
> + goto done;
> + err = -ENOENT;
> + if (!(t = netdev_priv(dev)))
> + goto done;
> + memcpy(&ip6rd, &t->ip6rd_prefix, sizeof(ip6rd));
Just wondering why you need a temporary ip6rd here,
why dont you copy_to_user(ifr->ifr_ifru.ifru_data, &t->ip6rd_prefix, sizeof(ip6rd)); ?
> + if (copy_to_user(ifr->ifr_ifru.ifru_data, &ip6rd, sizeof(ip6rd)))
> + err = -EFAULT;
> + else
> + err = 0;
> + break;
> +
> + case SIOCADD6RD:
> + case SIOCDEL6RD:
> + case SIOCCHG6RD:
> + err = -EPERM;
> + if (!capable(CAP_NET_ADMIN))
> + goto done;
> + err = -EINVAL;
> + if (dev == sitn->fb_tunnel_dev)
> + goto done;
> + err = -EFAULT;
> + if (copy_from_user(&ip6rd, ifr->ifr_ifru.ifru_data, sizeof(ip6rd)))
> + goto done;
> + err = -ENOENT;
> + if (!(t = netdev_priv(dev)))
> + goto done;
> +
> + err = 0;
> + switch (cmd) {
> + case SIOCDEL6RD:
> + memset(&t->ip6rd_prefix, 0, sizeof(ip6rd));
> + break;
> + case SIOCADD6RD:
> + case SIOCCHG6RD:
> + if (ip6rd.prefixlen >= 95) {
> + err = -EINVAL;
> + goto done;
> + }
> + ipv6_addr_copy(&t->ip6rd_prefix.addr, &ip6rd.addr);
> + t->ip6rd_prefix.prefixlen = ip6rd.prefixlen;
> + break;
> + }
> + netdev_state_change(dev);
> + break;
> +#endif
> +
> default:
> err = -EINVAL;
> }
^ permalink raw reply
* Re: [PATCH] fec: Add FEC support for MX25 processor
From: David Miller @ 2009-09-22 22:09 UTC (permalink / raw)
To: fabioestevam; +Cc: netdev, s.hauer
In-Reply-To: <123584.91508.qm@web51002.mail.re2.yahoo.com>
From: Fabio Estevam <fabioestevam@yahoo.com>
Date: Mon, 21 Sep 2009 17:41:21 -0700 (PDT)
> Add FEC support for MX25 processor.
>
> Signed-off-by: Fabio Estevam <fabio.estevam@freescale.com>
Applied.
^ permalink raw reply
* Re: [PATCH] cnic: Shutdown iSCSI ring during uio_close.
From: David Miller @ 2009-09-22 22:09 UTC (permalink / raw)
To: mchan; +Cc: netdev, michaelc, benli
In-Reply-To: <1253583577-8027-1-git-send-email-mchan@broadcom.com>
From: "Michael Chan" <mchan@broadcom.com>
Date: Mon, 21 Sep 2009 18:39:37 -0700
> The iSCSI ring should be shutdown during uio_close instead of uio_open
> for proper operations. This fixes the problem of the ring getting
> stuck intermittently.
>
> Signed-off-by: Michael Chan <mchan@broadcom.com>
> Signed-off-by: Benjamin Li <benli@broadcom.com>
Applied.
^ permalink raw reply
* Re: [PATCH 3/3] at91_can: add driver for Atmel's CAN controller on AT91SAM9263
From: David Miller @ 2009-09-22 22:09 UTC (permalink / raw)
To: wg; +Cc: mkl, netdev, Socketcan-core, linux, linux-arm-kernel
In-Reply-To: <4AB205E9.3090902@grandegger.com>
From: Wolfgang Grandegger <wg@grandegger.com>
Date: Thu, 17 Sep 2009 11:48:25 +0200
> Marc Kleine-Budde wrote:
>> This patch add the driver for the SoC CAN controller in Atmel's AT91SAM9263.
>>
>> Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
>
> Signed-off-by: Wolfgang Grandegger <wg@grandegger.com>
>
> Thanks for your contribution.
Applied.
^ permalink raw reply
* Re: [PATCH 2/3] at91sam9263ek: activate at91 CAN controller
From: David Miller @ 2009-09-22 22:09 UTC (permalink / raw)
To: wg; +Cc: mkl, netdev, Socketcan-core, linux, linux-arm-kernel
In-Reply-To: <4AB2364F.60700@grandegger.com>
From: Wolfgang Grandegger <wg@grandegger.com>
Date: Thu, 17 Sep 2009 15:14:55 +0200
> Marc Kleine-Budde wrote:
>> This patch activates the at91 CAN controller for the at91sam9263ek
>> development board.
>>
>> Signed-off-by: Hans J. Koch <hjk@linutronix.de>
>> Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
...
> Acked-by: Wolfgang Grandegger <wg@grandegger.com>
Applied.
^ permalink raw reply
* Re: [PATCH 1/3] at91sam9263: add at91_can device to generic device definition
From: David Miller @ 2009-09-22 22:08 UTC (permalink / raw)
To: wg; +Cc: mkl, netdev, Socketcan-core, linux, linux-arm-kernel
In-Reply-To: <4AB2362E.3070509@grandegger.com>
From: Wolfgang Grandegger <wg@grandegger.com>
Date: Thu, 17 Sep 2009 15:14:22 +0200
> Marc Kleine-Budde wrote:
>> This patch adds the device definition for the at91_can device to
>> the generic device definiton file for the at91sam9263.
>>
>> Signed-off-by: Hans J. Koch <hjk@linutronix.de>
>> Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
...
> Acked-by: Wolfgang Grandegger <wg@grandegger.com>
Applied.
^ permalink raw reply
* Re: [PATCH V2 0/3] at91_can: add support for Atmel's CAN controller on AT91SAM9263
From: Marc Kleine-Budde @ 2009-09-22 22:07 UTC (permalink / raw)
To: Andrew Victor
Cc: Socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA, Andrew Victor, Wolfgang Grandegger,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <cd73a99e0909211431k37429a70o4efaac7104f7d526-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Hey Andrew,
Andrew Victor wrote:
>>>> Marc Kleine-Budde (3):
>>>> at91sam9263: add at91_can device to generic device definition
>>>> at91sam9263ek: activate at91 CAN controller
>>>> at91_can: add driver for Atmel's CAN controller on AT91SAM9263
>>> I have just added my "signed-off-by" for the Socket-CAN patch #3. Don't
>>> known who will take care of the other patches.
>> Thanks Wolfgang.
>>
>> Andrew, are the first two going throught the arm tree?
>
> That would be best.
> Please submit them to Russell King's patch system.
done as:
5722 at91sam9263: add at91_can device to generic device definition
5723 at91sam9263ek: activate at91 CAN controller
Cheers, Marc
- --
Pengutronix e.K. | Marc Kleine-Budde |
Linux Solutions for Science and Industry | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
iEYEARECAAYFAkq5Sr4ACgkQjTAFq1RaXHNrLgCfeVy7xmi1UzdxGeWK3z4XRqMC
rjYAn2EK0LKhOe/S890W2bq6ElzPDNQL
=phat
-----END PGP SIGNATURE-----
^ permalink raw reply
* [PATCH][RESEND 3] IPv6: 6rd tunnel mode
From: Alexandre Cassen @ 2009-09-22 22:02 UTC (permalink / raw)
To: netdev
This patch add support to 6rd tunnel mode as described into
draft-despres-6rd-03.
Patch history :
* http://patchwork.ozlabs.org/patch/26870/
* http://patchwork.ozlabs.org/patch/34026/
* http://patchwork.ozlabs.org/patch/34045/
IPv6 rapid deployment (draft-despres-6rd-03) builds upon mechanisms
of 6to4 (RFC3056) to enable a service provider to rapidly deploy IPv6
unicast service to IPv4 sites to which it provides customer premise
equipment. Like 6to4, it utilizes stateless IPv6 in IPv4 encapsulation
in order to transit IPv4-only network infrastructure. Unlike 6to4, a
6rd service provider uses an IPv6 prefix of its own in place of the
fixed 6to4 prefix.
Signed-off-by: Alexandre Cassen <acassen@freebox.fr>
---
include/linux/if_tunnel.h | 10 +++++
include/net/ipip.h | 2 +
net/ipv6/Kconfig | 13 +++++++
net/ipv6/sit.c | 84 +++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 109 insertions(+), 0 deletions(-)
diff --git a/include/linux/if_tunnel.h b/include/linux/if_tunnel.h
index 5eb9b0f..0d44376 100644
--- a/include/linux/if_tunnel.h
+++ b/include/linux/if_tunnel.h
@@ -15,6 +15,10 @@
#define SIOCADDPRL (SIOCDEVPRIVATE + 5)
#define SIOCDELPRL (SIOCDEVPRIVATE + 6)
#define SIOCCHGPRL (SIOCDEVPRIVATE + 7)
+#define SIOCGET6RD (SIOCDEVPRIVATE + 8)
+#define SIOCADD6RD (SIOCDEVPRIVATE + 9)
+#define SIOCDEL6RD (SIOCDEVPRIVATE + 10)
+#define SIOCCHG6RD (SIOCDEVPRIVATE + 11)
#define GRE_CSUM __cpu_to_be16(0x8000)
#define GRE_ROUTING __cpu_to_be16(0x4000)
@@ -51,6 +55,12 @@ struct ip_tunnel_prl {
/* PRL flags */
#define PRL_DEFAULT 0x0001
+/* 6RD parms */
+struct ip_tunnel_6rd {
+ struct in6_addr addr;
+ __u8 prefixlen;
+};
+
enum
{
IFLA_GRE_UNSPEC,
diff --git a/include/net/ipip.h b/include/net/ipip.h
index 5d3036f..fa92c41 100644
--- a/include/net/ipip.h
+++ b/include/net/ipip.h
@@ -26,6 +26,8 @@ struct ip_tunnel
struct ip_tunnel_prl_entry *prl; /* potential router list */
unsigned int prl_count; /* # of entries in PRL */
+
+ struct ip_tunnel_6rd ip6rd_prefix; /* 6RD SP prefix */
};
/* ISATAP: default interval between RS in secondy */
diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
index ead6c7a..8b779f7 100644
--- a/net/ipv6/Kconfig
+++ b/net/ipv6/Kconfig
@@ -170,6 +170,19 @@ config IPV6_SIT
Saying M here will produce a module called sit. If unsure, say Y.
+config IPV6_SIT_6RD
+ bool "IPv6: 6rd tunnel mode (EXPERIMENTAL)"
+ depends on IPV6_SIT && EXPERIMENTAL
+ default n
+ ---help---
+ IPv6 rapid deployment (draft-despres-6rd-03) builds upon mechanisms
+ of 6to4 (RFC3056) to enable a service provider to rapidly deploy IPv6
+ unicast service to IPv4 sites to which it provides customer premise
+ equipment. Like 6to4, it utilizes stateless IPv6 in IPv4 encapsulation
+ in order to transit IPv4-only network infrastructure. Unlike 6to4, a
+ 6rd service provider uses an IPv6 prefix of its own in place of the
+ fixed 6to4 prefix.
+
config IPV6_NDISC_NODETYPE
bool
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index 0ae4f64..3587149 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -604,6 +604,30 @@ static inline __be32 try_6to4(struct in6_addr *v6dst)
return dst;
}
+#ifdef CONFIG_IPV6_SIT_6RD
+/* Returns the embedded IPv4 address if the IPv6 address comes from
+ 6rd rule */
+
+static inline __be32 try_6rd(struct in6_addr *addr, u8 prefix_len, struct in6_addr *v6dst)
+{
+ __be32 dst = 0;
+
+ /* isolate addr according to mask */
+ if (ipv6_prefix_equal(v6dst, addr, prefix_len)) {
+ unsigned int d32_off, bits;
+
+ d32_off = prefix_len >> 5;
+ bits = (prefix_len & 0x1f);
+
+ dst = (ntohl(v6dst->s6_addr32[d32_off]) << bits);
+ if (bits)
+ dst |= ntohl(v6dst->s6_addr32[d32_off + 1]) >> (32 - bits);
+ dst = htonl(dst);
+ }
+ return dst;
+}
+#endif
+
/*
* This function assumes it is being called from dev_queue_xmit()
* and that skb is filled properly by that function.
@@ -657,6 +681,13 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
goto tx_error;
}
+#ifdef CONFIG_IPV6_SIT_6RD
+ if (!dst && tunnel->ip6rd_prefix.prefixlen)
+ dst = try_6rd(&tunnel->ip6rd_prefix.addr,
+ tunnel->ip6rd_prefix.prefixlen,
+ &iph6->daddr);
+ else
+#endif
if (!dst)
dst = try_6to4(&iph6->daddr);
@@ -848,6 +879,9 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
int err = 0;
struct ip_tunnel_parm p;
struct ip_tunnel_prl prl;
+#ifdef CONFIG_IPV6_SIT_6RD
+ struct ip_tunnel_6rd ip6rd;
+#endif
struct ip_tunnel *t;
struct net *net = dev_net(dev);
struct sit_net *sitn = net_generic(net, sit_net_id);
@@ -987,6 +1021,56 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
netdev_state_change(dev);
break;
+#ifdef CONFIG_IPV6_SIT_6RD
+ case SIOCGET6RD:
+ err = -EINVAL;
+ if (dev == sitn->fb_tunnel_dev)
+ goto done;
+ err = -ENOENT;
+ if (!(t = netdev_priv(dev)))
+ goto done;
+ memcpy(&ip6rd, &t->ip6rd_prefix, sizeof(ip6rd));
+ if (copy_to_user(ifr->ifr_ifru.ifru_data, &ip6rd, sizeof(ip6rd)))
+ err = -EFAULT;
+ else
+ err = 0;
+ break;
+
+ case SIOCADD6RD:
+ case SIOCDEL6RD:
+ case SIOCCHG6RD:
+ err = -EPERM;
+ if (!capable(CAP_NET_ADMIN))
+ goto done;
+ err = -EINVAL;
+ if (dev == sitn->fb_tunnel_dev)
+ goto done;
+ err = -EFAULT;
+ if (copy_from_user(&ip6rd, ifr->ifr_ifru.ifru_data, sizeof(ip6rd)))
+ goto done;
+ err = -ENOENT;
+ if (!(t = netdev_priv(dev)))
+ goto done;
+
+ err = 0;
+ switch (cmd) {
+ case SIOCDEL6RD:
+ memset(&t->ip6rd_prefix, 0, sizeof(ip6rd));
+ break;
+ case SIOCADD6RD:
+ case SIOCCHG6RD:
+ if (ip6rd.prefixlen >= 95) {
+ err = -EINVAL;
+ goto done;
+ }
+ ipv6_addr_copy(&t->ip6rd_prefix.addr, &ip6rd.addr);
+ t->ip6rd_prefix.prefixlen = ip6rd.prefixlen;
+ break;
+ }
+ netdev_state_change(dev);
+ break;
+#endif
+
default:
err = -EINVAL;
}
--
1.6.0.4
^ permalink raw reply related
* Re: fanotify as syscalls
From: Andreas Gruenbacher @ 2009-09-22 21:38 UTC (permalink / raw)
To: Eric Paris
Cc: Jamie Lokier, Linus Torvalds, Evgeniy Polyakov, David Miller,
linux-kernel, linux-fsdevel, netdev, viro, alan, hch
In-Reply-To: <1253653576.2747.27.camel@dhcp231-106.rdu.redhat.com>
On Tuesday, 22 September 2009 23:06:16 Eric Paris wrote:
> this is true, if there is only one namespace subtree notification works
> the same as global notification.
>
> [...]
>
> I don't see how subtree notification can possibly solve the global
> notification problem.
I'm thinking of is something like this: A listener registers interest in "/",
recursively. The kernel sets a FSNOTIFY_WATCH_RECURSIVE flag on "/" and each
mount point below. Afterwards when something is mounted anywhere, same
namespace or not, the kernel sets the new mount's FSNOTIFY_WATCH_RECURSIVE
flag if the parent mount has this flag set.
(Of course we need per fsnotify_group flags and not global ones, but this
doesn't change the principle.)
Andreas
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox