* Re: Getting physical packet counts with LRO enabled with ixgbe?
From: Ben Greear @ 2009-09-23 21:37 UTC (permalink / raw)
To: Peter P Waskiewicz Jr; +Cc: NetDev
In-Reply-To: <1253730766.2538.28.camel@localhost.localdomain>
I noticed that 'ethtool -d' has some counters that may be right,
including bytes.
(I'm looking at the 82599 chipset currently)
0x04074: gprc (Good Packets Received Count) 0xF611E2C2
0x04078: bprc (Broadcast Packets Rx Count) 0x00000000
0x0407C: mprc (Multicast Packets Rx Count) 0x0000000C
0x04080: gptc (Good Packets Transmitted Count) 0xC9962C10
0x04088: gorcl (Good Octets Rx Count Low) 0x9A23F0B0
0x0408C: gorch (Good Octets Rx Count High) 0x00000000
0x04090: gotcl (Good Octets Tx Count Low) 0x5DFAACD4
0x04094: gotch (Good Octets Tx Count High) 0x00000000
Any suggestions on a more efficient way to get these than dumping 'ethtool -d' and
searching all that data? Maybe put it in 'ethtool -S' as well ?
Thanks,
Ben
--
Ben Greear <greearb@candelatech.com>
Candela Technologies Inc http://www.candelatech.com
^ permalink raw reply
* Re: [AX25] kernel panic
From: Bernard Pidoux F6BVP @ 2009-09-23 21:17 UTC (permalink / raw)
To: Jarek Poplawski
Cc: Bernard Pidoux, Ralf Baechle DL5RB, Linux Netdev List, linux-hams
In-Reply-To: <20090921201157.GA5460@del.dom.local>
Hi Jarek,
After applying your second patch I had to wait until today before I catched
these kernel messages.
The last three ones where single lines.
There was no kernel panic.
Sep 23 10:13:53 f6bvp-11 klogd: ------------[ cut here ]------------
Sep 23 10:13:53 f6bvp-11 klogd: WARNING: at net/ax25/af_ax25.c:241 ax25_find_cb+0x170/0x1a0 [ax25]()
Sep 23 10:13:53 f6bvp-11 klogd: Hardware name: MS-7258
Sep 23 10:13:53 f6bvp-11 klogd: Modules linked in: netconsole netrom mkiss rose ax25 nfsd exportfs nfs l
ockd nfs_acl auth_rpcgss sunrpc af_packet ipv6 snd_via82xx snd_ac97_codec ac97_bus snd_mpu401_uart snd_r
awmidi snd_seq_dummy snd_seq_oss snd_seq_midi_event snd_seq snd_seq_device snd_pcm_oss snd_pcm snd_timer
snd_page_alloc 8139cp snd_mixer_oss 8139too snd i2c_viapro i2c_core soundcore shpchp sr_mod mii pci_hot
plug binfmt_misc ext3 jbd cpufreq_ondemand cpufreq_conservative cpufreq_powersave acpi_cpufreq freq_tabl
e processor floppy sg rtc_cmos evdev button thermal via_agp pata_via ata_generic ide_pci_generic pata_ac
pi sata_via libata sd_mod scsi_mod crc_t10dif
Sep 23 10:13:53 f6bvp-11 klogd: Pid: 5, comm: events/0 Not tainted 2.6.31-nosmp #3
Sep 23 10:13:53 f6bvp-11 klogd: Call Trace:
Sep 23 10:13:53 f6bvp-11 klogd: <IRQ> [<ffffffffa03b40d0>] ? ax25_find_cb+0x170/0x1a0 [ax25]
Sep 23 10:13:53 f6bvp-11 klogd: [<ffffffff81053b90>] warn_slowpath_common+0x80/0xe0
Sep 23 10:13:53 f6bvp-11 klogd: [<ffffffff81053c12>] warn_slowpath_null+0x22/0x40
Sep 23 10:13:53 f6bvp-11 klogd: [<ffffffffa03b40d0>] ax25_find_cb+0x170/0x1a0 [ax25]
Sep 23 10:13:53 f6bvp-11 klogd: [<ffffffffa03aced5>] ? ax25_listen_mine+0x95/0xb0 [ax25]
Sep 23 10:13:53 f6bvp-11 klogd: [<ffffffffa03ad299>] ax25_kiss_rcv+0x199/0xac0 [ax25]
Sep 23 10:13:53 f6bvp-11 klogd: [<ffffffff8132b4bd>] ? sock_def_readable+0x3d/0x80
Sep 23 10:13:53 f6bvp-11 klogd: [<ffffffff8132c96d>] ? sock_queue_rcv_skb+0x12d/0x160
Sep 23 10:13:53 f6bvp-11 klogd: [<ffffffff8133a831>] netif_receive_skb+0x351/0x5f0
Sep 23 10:13:53 f6bvp-11 klogd: [<ffffffff81061699>] ? run_timer_softirq+0x179/0x250
Sep 23 10:13:53 f6bvp-11 klogd: [<ffffffff8133ab50>] process_backlog+0x80/0xe0
Sep 23 10:13:53 f6bvp-11 klogd: [<ffffffff8133b4a4>] net_rx_action+0xf4/0x220
Sep 23 10:13:53 f6bvp-11 klogd: [<ffffffff8105b242>] __do_softirq+0xe2/0x1d0
Sep 23 10:13:53 f6bvp-11 klogd: [<ffffffff8101414a>] call_softirq+0x1a/0x30
Sep 23 10:13:53 f6bvp-11 klogd: <EOI> [<ffffffff810162c5>] do_softirq+0x75/0xc0
Sep 23 10:13:53 f6bvp-11 klogd: [<ffffffff8105b094>] local_bh_enable+0xc4/0xd0
Sep 23 10:13:53 f6bvp-11 klogd: [<ffffffffa03cf798>] mkiss_receive_buf+0x3a8/0x460 [mkiss]
Sep 23 10:13:53 f6bvp-11 klogd: [<ffffffff8104b864>] ? finish_task_switch+0x44/0xe0
Sep 23 10:13:53 f6bvp-11 klogd: [<ffffffff812af800>] flush_to_ldisc+0x110/0x1f0
Sep 23 10:13:53 f6bvp-11 klogd: [<ffffffff8106c71c>] ? schedule_delayed_work+0x2c/0x50
Sep 23 10:13:53 f6bvp-11 klogd: [<ffffffff812af6f0>] ? flush_to_ldisc+0x0/0x1f0
Sep 23 10:13:53 f6bvp-11 klogd: [<ffffffff8106c263>] worker_thread+0x173/0x260
Sep 23 10:13:53 f6bvp-11 klogd: [<ffffffff81071be0>] ? autoremove_wake_function+0x0/0x60
Sep 23 10:13:53 f6bvp-11 klogd: [<ffffffff8106c0f0>] ? worker_thread+0x0/0x260
Sep 23 10:13:53 f6bvp-11 klogd: [<ffffffff8107149e>] kthread+0xae/0xc0
Sep 23 10:13:53 f6bvp-11 klogd: [<ffffffff8101404a>] child_rip+0xa/0x20
Sep 23 10:13:53 f6bvp-11 klogd: [<ffffffff810713f0>] ? kthread+0x0/0xc0
Sep 23 10:13:53 f6bvp-11 klogd: [<ffffffff81014040>] ? child_rip+0x0/0x20
Sep 23 10:13:53 f6bvp-11 klogd: ---[ end trace 422da9fe354a7ce3 ]---
Sep 23 10:13:53 f6bvp-11 klogd: AX25_DBG: 1 ffff880003c80000 0 ax25_find_cb
Sep 23 10:13:53 f6bvp-11 last message repeated 2 times
-----
Sep 23 14:26:56 f6bvp-11 klogd: AX25_DBG: 1 ffff88005247b000 0 ax25_find_cb
Sep 23 14:26:56 f6bvp-11 klogd: AX25_DBG: 1 ffff88005247b000 0 ax25_find_cb
Sep 23 14:26:56 f6bvp-11 klogd: AX25_DBG: 1 ffff88005247b000 0 ax25_find_cb
-----
Regards,
Bernard
Jarek Poplawski a e'crit :
> <20090910142436.GB10547@linux-mips.org> <4AA9288B.2070205@upmc.fr>
> <20090911120557.GA12175@linux-mips.org> <4AB5EAE5.6070605@upmc.fr>
> <20090920210242.GA9804@del.dom.local> <4AB73CDE.4030709@upmc.fr>
> In-Reply-To: <4AB73CDE.4030709@upmc.fr>
>
> Bernard Pidoux wrote, On 09/21/2009 10:44 AM:
>
>> Hi Jarek,
>>
>> Good fishing !
>>
>> During the night I catched the following two identical AX25_DBG
>> messages with netconsole
>> sending already reported message: kernel BUG at kernel/timer.c:913!
>> and followed by kernel
>> panics and the machine rebooting.
>>
>>
>> Sep 21 03:24:06 f6bvp-11 klogd: ------------[ cut here ]------------
>> Sep 21 03:24:06 f6bvp-11 klogd: WARNING: at include/net/ax25.h:260
>> ax25_kiss_rcv+0x650/0xab0 [ax25]()
>
> Thanks for testing. Alas I don't get how it's possible at this place
> (unless I miss the place), especially with a nosmp kernel. So here is
> take 2 (to apply after reverting the previous one).
>
> Regards,
> Jarek P.
> --- (debugging patch, take 2)
>
> include/net/ax25.h | 36 ++++++++++++++++++++++++++++++++++++
> net/ax25/af_ax25.c | 12 ++++++++++++
> 2 files changed, 48 insertions(+), 0 deletions(-)
>
> diff --git a/include/net/ax25.h b/include/net/ax25.h
> index 717e219..7fefbb0 100644
> --- a/include/net/ax25.h
> +++ b/include/net/ax25.h
> @@ -252,9 +252,45 @@ typedef struct ax25_cb {
> #define ax25_cb_hold(__ax25) \
> atomic_inc(&((__ax25)->refcount))
>
> +static __inline__ int ax25_timers_warn(ax25_cb *ax25)
> +{
> + int err = 0;
> +
> + if (del_timer(&ax25->timer)) {
> + WARN_ON_ONCE(1);
> + err = 1;
> + }
> + if (del_timer(&ax25->t1timer)) {
> + WARN_ON_ONCE(1);
> + err += 2;
> + }
> + if (del_timer(&ax25->t2timer)) {
> + WARN_ON_ONCE(1);
> + err += 4;
> + }
> + if (del_timer(&ax25->t3timer)) {
> + WARN_ON_ONCE(1);
> + err += 8;
> + }
> + if (del_timer(&ax25->idletimer)) {
> + WARN_ON_ONCE(1);
> + err += 16;
> + }
> + if (del_timer(&ax25->dtimer)) {
> + WARN_ON_ONCE(1);
> + err += 32;
> + }
> + if (err)
> + printk(KERN_WARNING "AX25_DBG: %d %p %u %s %d\n", err, ax25,
> + ax25->state, __func__, __LINE__);
> +
> + return err;
> +}
> +
> static __inline__ void ax25_cb_put(ax25_cb *ax25)
> {
> if (atomic_dec_and_test(&ax25->refcount)) {
> + ax25_timers_warn(ax25);
> kfree(ax25->digipeat);
> kfree(ax25);
> }
> diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c
> index da0f64f..f1f515c 100644
> --- a/net/ax25/af_ax25.c
> +++ b/net/ax25/af_ax25.c
> @@ -58,6 +58,9 @@ static const struct proto_ops ax25_proto_ops;
>
> static void ax25_free_sock(struct sock *sk)
> {
> + if (ax25_timers_warn(ax25_sk(sk)))
> + printk(KERN_WARNING "AX25_DBG: %p %u %u %u\n", sk,
> + sk->sk_family, sk->sk_type, sk->sk_protocol);
> ax25_cb_put(ax25_sk(sk));
> }
>
> @@ -222,6 +225,8 @@ ax25_cb *ax25_find_cb(ax25_address *src_addr, ax25_address *dest_addr,
> if (s->ax25_dev == NULL)
> continue;
> if (ax25cmp(&s->source_addr, src_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->ax25_dev->dev == dev) {
> + int ref;
> +
> if (digi != NULL && digi->ndigi != 0) {
> if (s->digipeat == NULL)
> continue;
> @@ -231,6 +236,13 @@ ax25_cb *ax25_find_cb(ax25_address *src_addr, ax25_address *dest_addr,
> if (s->digipeat != NULL && s->digipeat->ndigi != 0)
> continue;
> }
> + ref = atomic_read(&s->refcount);
> + if (ref < 2) {
> + WARN_ON_ONCE(1);
> + printk(KERN_WARNING "AX25_DBG: %d %p %d %s\n",
> + ref, s, s->state, __func__);
> + }
> +
> ax25_cb_hold(s);
> spin_unlock_bh(&ax25_list_lock);
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-hams" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
>
^ permalink raw reply
* Re: [PATCHv5 3/3] vhost_net: a kernel-level virtio server
From: Gregory Haskins @ 2009-09-23 21:15 UTC (permalink / raw)
To: Avi Kivity
Cc: Ira W. Snyder, Michael S. Tsirkin, netdev, virtualization, kvm,
linux-kernel, mingo, linux-mm, akpm, hpa, Rusty Russell, s.hetze,
alacrityvm-devel
In-Reply-To: <4ABA78DC.7070604@redhat.com>
[-- Attachment #1: Type: text/plain, Size: 13736 bytes --]
Avi Kivity wrote:
> On 09/23/2009 08:58 PM, Gregory Haskins wrote:
>>>
>>>> It also pulls parts of the device model into the host kernel.
>>>>
>>> That is the point. Most of it needs to be there for performance.
>>>
>> To clarify this point:
>>
>> There are various aspects about designing high-performance virtual
>> devices such as providing the shortest paths possible between the
>> physical resources and the consumers. Conversely, we also need to
>> ensure that we meet proper isolation/protection guarantees at the same
>> time. What this means is there are various aspects to any
>> high-performance PV design that require to be placed in-kernel to
>> maximize the performance yet properly isolate the guest.
>>
>> For instance, you are required to have your signal-path (interrupts and
>> hypercalls), your memory-path (gpa translation), and
>> addressing/isolation model in-kernel to maximize performance.
>>
>
> Exactly. That's what vhost puts into the kernel and nothing more.
Actually, no. Generally, _KVM_ puts those things into the kernel, and
vhost consumes them. Without KVM (or something equivalent), vhost is
incomplete. One of my goals with vbus is to generalize the "something
equivalent" part here.
I know you may not care about non-kvm use cases, and thats fine. No one
says you have to. However, note that some of use do care about these
non-kvm cases, and thus its a distinction I am making here as a benefit
of the vbus framework.
>
>> Vbus accomplishes its in-kernel isolation model by providing a
>> "container" concept, where objects are placed into this container by
>> userspace. The host kernel enforces isolation/protection by using a
>> namespace to identify objects that is only relevant within a specific
>> container's context (namely, a "u32 dev-id"). The guest addresses the
>> objects by its dev-id, and the kernel ensures that the guest can't
>> access objects outside of its dev-id namespace.
>>
>
> vhost manages to accomplish this without any kernel support.
No, vhost manages to accomplish this because of KVMs kernel support
(ioeventfd, etc). Without a KVM-like in-kernel support, vhost is a
merely a kind of "tuntap"-like clone signalled by eventfds.
vbus on the other hand, generalizes one more piece of the puzzle
(namely, the function of pio+ioeventfd and userspace's programming of
it) by presenting the devid namespace and container concept.
This goes directly to my rebuttal of your claim that vbus places too
much in the kernel. I state that, one way or the other, address decode
and isolation _must_ be in the kernel for performance. Vbus does this
with a devid/container scheme. vhost+virtio-pci+kvm does it with
pci+pio+ioeventfd.
> The guest
> simply has not access to any vhost resources other than the guest->host
> doorbell, which is handed to the guest outside vhost (so it's somebody
> else's problem, in userspace).
You mean _controlled_ by userspace, right? Obviously, the other side of
the kernel still needs to be programmed (ioeventfd, etc). Otherwise,
vhost would be pointless: e.g. just use vanilla tuntap if you don't need
fast in-kernel decoding.
>
>> All that is required is a way to transport a message with a "devid"
>> attribute as an address (such as DEVCALL(devid)) and the framework
>> provides the rest of the decode+execute function.
>>
>
> vhost avoids that.
No, it doesn't avoid it. It just doesn't specify how its done, and
relies on something else to do it on its behalf.
Conversely, vbus specifies how its done, but not how to transport the
verb "across the wire". That is the role of the vbus-connector abstraction.
>
>> Contrast this to vhost+virtio-pci (called simply "vhost" from here).
>>
>
> It's the wrong name. vhost implements only the data path.
Understood, but vhost+virtio-pci is what I am contrasting, and I use
"vhost" for short from that point on because I am too lazy to type the
whole name over and over ;)
>
>> It is not immune to requiring in-kernel addressing support either, but
>> rather it just does it differently (and its not as you might expect via
>> qemu).
>>
>> Vhost relies on QEMU to render PCI objects to the guest, which the guest
>> assigns resources (such as BARs, interrupts, etc).
>
> vhost does not rely on qemu. It relies on its user to handle
> configuration. In one important case it's qemu+pci. It could just as
> well be the lguest launcher.
I meant vhost=vhost+virtio-pci here. Sorry for the confusion.
The point I am making specifically is that vhost in general relies on
other in-kernel components to function. I.e. It cannot function without
having something like the PCI model to build an IO namespace. That
namespace (in this case, pio addresses+data tuples) are used for the
in-kernel addressing function under KVM + virtio-pci.
The case of the lguest launcher is a good one to highlight. Yes, you
can presumably also use lguest with vhost, if the requisite facilities
are exposed to lguest-bus, and some eventfd based thing like ioeventfd
is written for the host (if it doesnt exist already).
And when the next virt design "foo" comes out, it can make a "foo-bus"
model, and implement foo-eventfd on the backend, etc, etc.
Ira can make ira-bus, and ira-eventfd, etc, etc.
Each iteration will invariably introduce duplicated parts of the stack.
Vbus tries to generalize some of those pieces so we can reuse them.
I chose the very non-specific name "virtual-bus" for the design
intentionally to decouple it from any one particular "hypervisor" (e.g.
xenbus, lguest-bus, etc) and promote it as a general purpose bus for
hopefully any hypervisor (or physical systems too, e.g. Iras). I assume
"virtio" was chosen to reflect a similar positioning at the device-model
layer.
Had vbus come out before lguest, I would have proposed that lguest
should use it natively instead of creating lguest-bus. While its
probably too late in that specific case, perhaps going forward this is
the direction we can take, just like perhaps virtio is the device model
direction we can take.
Likewise, the backend is generalized so one model can be written that
works in all environments that support vbus. The connector takes care
of the "wire" details, and the other stuff functions to serve the bus
portion of the stack (signal-routing, memory-routing,
isolation/addressing, etc).
>
>> A PCI-BAR in this
>> example may represent a PIO address for triggering some operation in the
>> device-model's fast-path. For it to have meaning in the fast-path, KVM
>> has to have in-kernel knowledge of what a PIO-exit is, and what to do
>> with it (this is where pio-bus and ioeventfd come in). The programming
>> of the PIO-exit and the ioeventfd are likewise controlled by some
>> userspace management entity (i.e. qemu). The PIO address and value
>> tuple form the address, and the ioeventfd framework within KVM provide
>> the decode+execute function.
>>
>
> Right.
>
>> This idea seemingly works fine, mind you, but it rides on top of a *lot*
>> of stuff including but not limited to: the guests pci stack, the qemu
>> pci emulation, kvm pio support, and ioeventfd. When you get into
>> situations where you don't have PCI or even KVM underneath you (e.g. a
>> userspace container, Ira's rig, etc) trying to recreate all of that PCI
>> infrastructure for the sake of using PCI is, IMO, a lot of overhead for
>> little gain.
>>
>
> For the N+1th time, no. vhost is perfectly usable without pci. Can we
> stop raising and debunking this point?
Again, I understand vhost is decoupled from PCI, and I don't mean to
imply anything different. I use PCI as an example here because a) its
the only working example of vhost today (to my knowledge), and b) you
have stated in the past that PCI is the only "right" way here, to
paraphrase. Perhaps you no longer feel that way, so I apologize if you
feel you already recanted your position on PCI and I missed it.
I digress. My point here isn't PCI. The point here is the missing
component for when PCI is not present. The component that is partially
satisfied by vbus's devid addressing scheme. If you are going to use
vhost, and you don't have PCI, you've gotta build something to replace it.
>
>> All you really need is a simple decode+execute mechanism, and a way to
>> program it from userspace control. vbus tries to do just that:
>> commoditize it so all you need is the transport of the control messages
>> (like DEVCALL()), but the decode+execute itself is reuseable, even
>> across various environments (like KVM or Iras rig).
>>
>
> If you think it should be "commodotized", write libvhostconfig.so.
I know you are probably being facetious here, but what do you propose
for the parts that must be in-kernel?
>
>> And your argument, I believe, is that vbus allows both to be implemented
>> in the kernel (though to reiterate, its optional) and is therefore a bad
>> design, so lets discuss that.
>>
>> I believe the assertion is that things like config-space are best left
>> to userspace, and we should only relegate fast-path duties to the
>> kernel. The problem is that, in my experience, a good deal of
>> config-space actually influences the fast-path and thus needs to
>> interact with the fast-path mechanism eventually anyway.
>> Whats left
>> over that doesn't fall into this category may cheaply ride on existing
>> plumbing, so its not like we created something new or unnatural just to
>> support this subclass of config-space.
>>
>
> Flexibility is reduced, because changing code in the kernel is more
> expensive than in userspace, and kernel/user interfaces aren't typically
> as wide as pure userspace interfaces. Security is reduced, since a bug
> in the kernel affects the host, while a bug in userspace affects just on
> guest.
For a mac-address attribute? Thats all we are really talking about
here. These points you raise, while true of any kernel code I suppose,
are a bit of a stretch in this context.
>
> Example: feature negotiation. If it happens in userspace, it's easy to
> limit what features we expose to the guest.
Its not any harder in the kernel. I do this today.
And when you are done negotiating said features, you will generally have
to turn around and program the feature into the backend anyway (e.g.
ioctl() to vhost module). Now you have to maintain some knowledge of
that particular feature and how to program it in two places.
Conversely, I am eliminating the (unnecessary) middleman by letting the
feature negotiating take place directly between the two entities that
will consume it.
> If it happens in the
> kernel, we need to add an interface to let the kernel know which
> features it should expose to the guest.
You need this already either way for both models anyway. As an added
bonus, vbus has generalized that interface using sysfs attributes, so
all models are handled in a similar and community accepted way.
> We also need to add an
> interface to let userspace know which features were negotiated, if we
> want to implement live migration. Something fairly trivial bloats rapidly.
Can you elaborate on the requirements for live-migration? Wouldnt an
opaque save/restore model work here? (e.g. why does userspace need to be
able to interpret the in-kernel state, just pass it along as a blob to
the new instance).
>
>> For example: take an attribute like the mac-address assigned to a NIC.
>> This clearly doesn't need to be in-kernel and could go either way (such
>> as a PCI config-space register).
>>
>> As another example: consider an option bit that enables a new feature
>> that affects the fast-path, like RXBUF merging. If we use the split
>> model where config space is handled by userspace and fast-path is
>> in-kernel, the userspace component is only going to act as a proxy.
>> I.e. it will pass the option down to the kernel eventually. Therefore,
>> there is little gain in trying to split this type of slow-path out to
>> userspace. In fact, its more work.
>>
>
> As you can see above, userspace needs to be involved in this, and the
> number of interfaces required is smaller if it's in userspace:
Actually, no. My experience has been the opposite. Anytime I sat down
and tried to satisfy your request to move things to the userspace,
things got ugly and duplicative really quick. I suspect part of the
reason you may think its easier because you already have part of
virtio-net in userspace and its surrounding support, but that is not the
case moving forward for new device types.
> you only
> need to know which features the kernel supports (they can be enabled
> unconditionally, just not exposed).
>
> Further, some devices are perfectly happy to be implemented in
> userspace, so we need userspace configuration support anyway. Why
> reimplement it in the kernel?
Thats fine. vbus is targetted for high-performance IO. So if you have
a robust userspace (like KVM+QEMU) and low-performance constraints (say,
for a console or something), put it in userspace and vbus is not
involved. I don't care.
However, if you are coming from somewhere else (like Ira's rig) where
you don't necessarily have a robust userspace module, vbus provides a
model that allows you to chose whether you want to do a vhost like
model, or a full resource container with the isolation guarantees, etc,
built in.
Kind Regards,
-Greg
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 267 bytes --]
^ permalink raw reply
* PATCH 1/1: rt2x00dev.c / rt2x00lib.h fixes build breakage
From: Ken Lewis @ 2009-09-23 20:59 UTC (permalink / raw)
To: LKML, linux-next, netdev
[-- Attachment #1: Type: text/plain, Size: 450 bytes --]
The headers in drivers/net/wireless/rt2x00/rt2x00lib.h don't match the
use of the function in rt2x00dev.c The build fails as a result.
This has been a problem in linux-next since early September. I've
e-mailed a patch to linux-next and to linux-net, but the 2.6.32 merge
window has brought the problem to the mainline and so I'm re-sending
my patch. I've opened a bug on bugzilla:
http://bugzilla.kernel.org/show_bug.cgi?id=14217
Take care.
Ken
[-- Attachment #2: diff-fix-for-rt2x00lib.h.patch --]
[-- Type: text/x-diff, Size: 544 bytes --]
diff --git a/drivers/net/wireless/rt2x00/rt2x00lib.h b/drivers/net/wireless/rt2x00/rt2x00lib.h
index 5462cb5..567f029 100644
--- a/drivers/net/wireless/rt2x00/rt2x00lib.h
+++ b/drivers/net/wireless/rt2x00/rt2x00lib.h
@@ -380,7 +380,7 @@ static inline void rt2x00crypto_tx_insert_iv(struct sk_buff *skb,
{
}
-static inline void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, bool l2pad,
+static inline void rt2x00crypto_rx_insert_iv(struct sk_buff *skb,
unsigned int header_length,
struct rxdone_entry_desc *rxdesc)
{
^ permalink raw reply related
* PATCH 0/1: rt2x00dev.c / rt2x00lib.h fixes build breakage
From: Ken Lewis @ 2009-09-23 20:58 UTC (permalink / raw)
To: linux-next, LKML, netdev
The headers in drivers/net/wireless/rt2x00/rt2x00lib.h don't match the
use of the function in rt2x00dev.c The build fails as a result.
This has been a problem in linux-next since early September. I've
e-mailed a patch to linux-next and to linux-net, but the 2.6.32 merge
window has brought the problem to the mainline and so I'm re-sending
my patch. I've opened a bug on bugzilla:
http://bugzilla.kernel.org/show_bug.cgi?id=14217
Take care.
Ken
^ permalink raw reply
* Re: [PATCH] af_packet: add interframe drop cmsg
From: Eric Dumazet @ 2009-09-23 20:55 UTC (permalink / raw)
To: Neil Horman; +Cc: netdev, davem
In-Reply-To: <20090923203202.GA13805@hmsreliant.think-freely.org>
Neil Horman a écrit :
> Add Ancilliary data to better represent loss information
>
> I've had a few requests recently to provide more detail regarding frame loss
> during an AF_PACKET packet capture session. Specifically the requestors want to
> see where in a packet sequence frames were lost, i.e. they want to see that 40
> frames were lost between frames 302 and 303 in a packet capture file. In order
> to do this we need:
>
> 1) The kernel to export this data to user space
> 2) The applications to make use of it
>
> This patch addresses item (1). It does this by doing the following:
>
> A) attaching ancilliary data to any skb enqueued to a socket recieve queue for
> which frames were lost between it and the previously enqueued frame. Note I use
> a ring buffer with a correlator value (the skb pointer) to do this. This was
> done because the skb->cb array is exhausted already for AF_PACKET
Hmm, how mmap() users can find this information ? I thought recent libpcap were
using mmap(), in order to reduce losses :)
>
> B) For any frame dequeued that has ancilliary data in the ring buffer (as
> determined by the correlator value), we add a cmsg structure to the msghdr that
> gets copied to user space, this cmsg structure is of cmsg_level AF_PACKET, and
> cmsg_type PACKET_GAPDATA. It contains a u32 value which counts the number of
> frames lost between the reception of the frame being currently recevied and the
> frame most recently preceding it. Note this creates a situation in which if we
> have packet loss followed immediately by a socket close operation we might miss
> some gap information. This situation is covered by the use of the
> PACKET_AUXINFO socket option, which provides total loss stats (from which the
> final gap can be computed).
>
> I've tested this patch myself, and it works well.
Okay :)
>
> Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
>
>
> include/linux/if_packet.h | 2 +
> net/packet/af_packet.c | 90 +++++++++++++++++++++++++++++++++++++++++++++-
> 2 files changed, 91 insertions(+), 1 deletion(-)
>
> diff --git a/include/linux/if_packet.h b/include/linux/if_packet.h
> index dea7d6b..e5d200f 100644
> --- a/include/linux/if_packet.h
> +++ b/include/linux/if_packet.h
> @@ -48,11 +48,13 @@ struct sockaddr_ll
> #define PACKET_RESERVE 12
> #define PACKET_TX_RING 13
> #define PACKET_LOSS 14
> +#define PACKET_GAPDATA 15
>
> struct tpacket_stats
> {
> unsigned int tp_packets;
> unsigned int tp_drops;
> + unsigned int tp_gap;
> };
>
> struct tpacket_auxdata
> diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
> index d3d52c6..b74a91c 100644
> --- a/net/packet/af_packet.c
> +++ b/net/packet/af_packet.c
> @@ -179,6 +179,11 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg);
>
> static void packet_flush_mclist(struct sock *sk);
>
> +struct packet_gap_record {
> + struct sk_buff *skb;
> + __u32 gap;
> +};
> +
> struct packet_sock {
> /* struct sock has to be the first member of packet_sock */
> struct sock sk;
> @@ -197,6 +202,11 @@ struct packet_sock {
> int ifindex; /* bound device */
> __be16 num;
> struct packet_mclist *mclist;
> + struct packet_gap_record *gaps;
> + unsigned int gap_head;
> + unsigned int gap_tail;
> + unsigned int gap_list_size;
> +
> #ifdef CONFIG_PACKET_MMAP
> atomic_t mapped;
> enum tpacket_versions tp_version;
> @@ -524,6 +534,55 @@ static inline unsigned int run_filter(struct sk_buff *skb, struct sock *sk,
> }
>
> /*
> + * If we've lost frames since the last time we queued one to the
> + * sk_receive_queue, we need to record it here.
> + * This must be called under the protection of the socket lock
> + * to prevent racing with other softirqs and user space
> + */
> +static void record_packet_gap(struct sk_buff *skb, struct packet_sock *po)
> +{
> + /*
> + * do nothing if there is no gap
> + */
> + if (!po->stats.tp_gap)
> + return;
> +
> + /*
> + * If there is, check the gap list tail to make sure we
> + * have an open entry
> + */
> + if (po->gaps[po->gap_tail].skb != NULL) {
> + if (net_ratelimit())
> + printk(KERN_WARNING "packet socket gap list is full!\n");
New code can use pr_warning() macro
> + return;
> + }
> +
> + /*
> + * We have a free entry, record it
> + */
> + po->gaps[po->gap_tail].skb = skb;
> + po->gaps[po->gap_tail].gap = po->stats.tp_gap;
> + po->gap_tail = (po->gap_tail+1) % po->gap_list_size;
you could avoid this divide
if (++po->gap_tail == po->gap_list_size)
po->gap_tail = 0;
> + po->stats.tp_gap = 0;
> + return;
> +
> +}
> +
> +static __u32 check_packet_gap(struct sk_buff *skb, struct packet_sock *po)
> +{
> + __u32 gap = 0;
> +
> + if (po->gaps[po->gap_head].skb != skb)
> + return 0;
> +
> + gap = po->gaps[po->gap_head].gap;
> + po->gaps[po->gap_head].skb = NULL;
> + po->gap_head = (po->gap_head + 1) % po->gap_list_size;
ditto
> + return gap;
> +}
> +
> +
> +/*
> This function makes lazy skb cloning in hope that most of packets
> are discarded by BPF.
>
> @@ -626,6 +685,7 @@ static int packet_rcv(struct sk_buff *skb, struct net_device *dev,
>
> spin_lock(&sk->sk_receive_queue.lock);
> po->stats.tp_packets++;
> + record_packet_gap(skb, po);
> __skb_queue_tail(&sk->sk_receive_queue, skb);
> spin_unlock(&sk->sk_receive_queue.lock);
> sk->sk_data_ready(sk, skb->len);
> @@ -634,6 +694,7 @@ static int packet_rcv(struct sk_buff *skb, struct net_device *dev,
> drop_n_acct:
> spin_lock(&sk->sk_receive_queue.lock);
> po->stats.tp_drops++;
> + po->stats.tp_gap++;
> spin_unlock(&sk->sk_receive_queue.lock);
>
> drop_n_restore:
> @@ -811,6 +872,7 @@ drop:
>
> ring_is_full:
> po->stats.tp_drops++;
> + po->stats.tp_gap++;
> spin_unlock(&sk->sk_receive_queue.lock);
>
> sk->sk_data_ready(sk, 0);
> @@ -1223,6 +1285,8 @@ static int packet_release(struct socket *sock)
> skb_queue_purge(&sk->sk_receive_queue);
> sk_refcnt_debug_release(sk);
>
> + kfree(po->gaps);
> +
> sock_put(sk);
> return 0;
> }
> @@ -1350,6 +1414,7 @@ static int packet_create(struct net *net, struct socket *sock, int protocol)
> struct packet_sock *po;
> __be16 proto = (__force __be16)protocol; /* weird, but documented */
> int err;
> + unsigned int num_records = PAGE_SIZE/sizeof(struct packet_gap_record);
>
> if (!capable(CAP_NET_RAW))
> return -EPERM;
> @@ -1360,6 +1425,7 @@ static int packet_create(struct net *net, struct socket *sock, int protocol)
> sock->state = SS_UNCONNECTED;
>
> err = -ENOBUFS;
> +
> sk = sk_alloc(net, PF_PACKET, GFP_KERNEL, &packet_proto);
> if (sk == NULL)
> goto out;
> @@ -1374,6 +1440,19 @@ static int packet_create(struct net *net, struct socket *sock, int protocol)
> sk->sk_family = PF_PACKET;
> po->num = proto;
>
> + err = -ENOMEM;
> + po->gaps = kmalloc(sizeof(struct packet_gap_record)*num_records,
> + GFP_KERNEL);
kzalloc(), and no need for some following lines
> + if (!po->gaps)
> + goto out_free;
> + po->gap_tail = po->gap_head = 0;
> + po->gap_list_size = num_records;
> +
> + for (num_records = 0; num_records < po->gap_list_size; num_records++) {
> + po->gaps[num_records].skb = NULL;
> + po->gaps[num_records].gap = 0;
> + }
> +
> sk->sk_destruct = packet_sock_destruct;
> sk_refcnt_debug_inc(sk);
>
> @@ -1402,6 +1481,9 @@ static int packet_create(struct net *net, struct socket *sock, int protocol)
> sock_prot_inuse_add(net, &packet_proto, 1);
> write_unlock_bh(&net->packet.sklist_lock);
> return 0;
> +
> +out_free:
> + sk_free(sk);
> out:
> return err;
> }
> @@ -1418,6 +1500,7 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,
> struct sk_buff *skb;
> int copied, err;
> struct sockaddr_ll *sll;
> + __u32 gap;
>
> err = -EINVAL;
> if (flags & ~(MSG_PEEK|MSG_DONTWAIT|MSG_TRUNC|MSG_CMSG_COMPAT))
> @@ -1492,10 +1575,15 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,
> aux.tp_mac = 0;
> aux.tp_net = skb_network_offset(skb);
> aux.tp_vlan_tci = skb->vlan_tci;
> -
Please dont mix cleanups
> put_cmsg(msg, SOL_PACKET, PACKET_AUXDATA, sizeof(aux), &aux);
> }
>
> + lock_sock(sk);
strange locking here. this doesnt match locking used at record time.
( spin_lock(&sk->sk_receive_queue.lock);)
> + gap = check_packet_gap(skb, pkt_sk(sk));
> + release_sock(sk);
> + if (gap)
> + put_cmsg(msg, SOL_PACKET, PACKET_GAPDATA, sizeof(u32), &gap);
> +
> /*
> * Free or return the buffer as appropriate. Again this
> * hides all the races and re-entrancy issues from us.
Thanks
^ permalink raw reply
* Re: r8169 chips on some Intel D945GSEJT boards fail to work after PXE boot
From: Francois Romieu @ 2009-09-23 20:57 UTC (permalink / raw)
To: Simon Farnsworth; +Cc: netdev
In-Reply-To: <4ABA535E.2010801@onelan.com>
Simon Farnsworth <simon.farnsworth@onelan.com> :
[...]
> Some boards are good, and just work, whether I boot via PXE or boot from
> the local disk; dmesg.working and lspci.working are from a good board.
>
> Some boards are bad; they work fine if I boot from local disk (including
> network), but the kernel cannot detect link, or send or receive data if
> I PXE boot. dmesg.broken and lspci.broken are from a bad board.
No cunning theroy in sight but does reducing the amount of memory on a
bad board from 1 Go to 512 Mo turn it into a good one ?
The failing board exhibits a correctable error status bit. Clearing it
is the least we can do.
diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c
index 50c6a3c..79bc4ab 100644
--- a/drivers/net/r8169.c
+++ b/drivers/net/r8169.c
@@ -2200,6 +2200,11 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
tp->pcie_cap = pci_find_capability(pdev, PCI_CAP_ID_EXP);
if (!tp->pcie_cap && netif_msg_probe(tp))
dev_info(&pdev->dev, "no PCI Express capability\n");
+ else {
+ pci_write_config_word(pdev, tp->pcie_cap + PCI_EXP_DEVSTA,
+ PCI_EXP_DEVSTA_CED | PCI_EXP_DEVSTA_NFED |
+ PCI_EXP_DEVSTA_FED | PCI_EXP_DEVSTA_URD);
+ }
RTL_W16(IntrMask, 0x0000);
--
Ueimor
^ permalink raw reply related
* [PATCH] af_packet: add interframe drop cmsg
From: Neil Horman @ 2009-09-23 20:32 UTC (permalink / raw)
To: netdev; +Cc: davem, nhorman
Add Ancilliary data to better represent loss information
I've had a few requests recently to provide more detail regarding frame loss
during an AF_PACKET packet capture session. Specifically the requestors want to
see where in a packet sequence frames were lost, i.e. they want to see that 40
frames were lost between frames 302 and 303 in a packet capture file. In order
to do this we need:
1) The kernel to export this data to user space
2) The applications to make use of it
This patch addresses item (1). It does this by doing the following:
A) attaching ancilliary data to any skb enqueued to a socket recieve queue for
which frames were lost between it and the previously enqueued frame. Note I use
a ring buffer with a correlator value (the skb pointer) to do this. This was
done because the skb->cb array is exhausted already for AF_PACKET
B) For any frame dequeued that has ancilliary data in the ring buffer (as
determined by the correlator value), we add a cmsg structure to the msghdr that
gets copied to user space, this cmsg structure is of cmsg_level AF_PACKET, and
cmsg_type PACKET_GAPDATA. It contains a u32 value which counts the number of
frames lost between the reception of the frame being currently recevied and the
frame most recently preceding it. Note this creates a situation in which if we
have packet loss followed immediately by a socket close operation we might miss
some gap information. This situation is covered by the use of the
PACKET_AUXINFO socket option, which provides total loss stats (from which the
final gap can be computed).
I've tested this patch myself, and it works well.
Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
include/linux/if_packet.h | 2 +
net/packet/af_packet.c | 90 +++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 91 insertions(+), 1 deletion(-)
diff --git a/include/linux/if_packet.h b/include/linux/if_packet.h
index dea7d6b..e5d200f 100644
--- a/include/linux/if_packet.h
+++ b/include/linux/if_packet.h
@@ -48,11 +48,13 @@ struct sockaddr_ll
#define PACKET_RESERVE 12
#define PACKET_TX_RING 13
#define PACKET_LOSS 14
+#define PACKET_GAPDATA 15
struct tpacket_stats
{
unsigned int tp_packets;
unsigned int tp_drops;
+ unsigned int tp_gap;
};
struct tpacket_auxdata
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index d3d52c6..b74a91c 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -179,6 +179,11 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg);
static void packet_flush_mclist(struct sock *sk);
+struct packet_gap_record {
+ struct sk_buff *skb;
+ __u32 gap;
+};
+
struct packet_sock {
/* struct sock has to be the first member of packet_sock */
struct sock sk;
@@ -197,6 +202,11 @@ struct packet_sock {
int ifindex; /* bound device */
__be16 num;
struct packet_mclist *mclist;
+ struct packet_gap_record *gaps;
+ unsigned int gap_head;
+ unsigned int gap_tail;
+ unsigned int gap_list_size;
+
#ifdef CONFIG_PACKET_MMAP
atomic_t mapped;
enum tpacket_versions tp_version;
@@ -524,6 +534,55 @@ static inline unsigned int run_filter(struct sk_buff *skb, struct sock *sk,
}
/*
+ * If we've lost frames since the last time we queued one to the
+ * sk_receive_queue, we need to record it here.
+ * This must be called under the protection of the socket lock
+ * to prevent racing with other softirqs and user space
+ */
+static void record_packet_gap(struct sk_buff *skb, struct packet_sock *po)
+{
+ /*
+ * do nothing if there is no gap
+ */
+ if (!po->stats.tp_gap)
+ return;
+
+ /*
+ * If there is, check the gap list tail to make sure we
+ * have an open entry
+ */
+ if (po->gaps[po->gap_tail].skb != NULL) {
+ if (net_ratelimit())
+ printk(KERN_WARNING "packet socket gap list is full!\n");
+ return;
+ }
+
+ /*
+ * We have a free entry, record it
+ */
+ po->gaps[po->gap_tail].skb = skb;
+ po->gaps[po->gap_tail].gap = po->stats.tp_gap;
+ po->gap_tail = (po->gap_tail+1) % po->gap_list_size;
+ po->stats.tp_gap = 0;
+ return;
+
+}
+
+static __u32 check_packet_gap(struct sk_buff *skb, struct packet_sock *po)
+{
+ __u32 gap = 0;
+
+ if (po->gaps[po->gap_head].skb != skb)
+ return 0;
+
+ gap = po->gaps[po->gap_head].gap;
+ po->gaps[po->gap_head].skb = NULL;
+ po->gap_head = (po->gap_head + 1) % po->gap_list_size;
+ return gap;
+}
+
+
+/*
This function makes lazy skb cloning in hope that most of packets
are discarded by BPF.
@@ -626,6 +685,7 @@ static int packet_rcv(struct sk_buff *skb, struct net_device *dev,
spin_lock(&sk->sk_receive_queue.lock);
po->stats.tp_packets++;
+ record_packet_gap(skb, po);
__skb_queue_tail(&sk->sk_receive_queue, skb);
spin_unlock(&sk->sk_receive_queue.lock);
sk->sk_data_ready(sk, skb->len);
@@ -634,6 +694,7 @@ static int packet_rcv(struct sk_buff *skb, struct net_device *dev,
drop_n_acct:
spin_lock(&sk->sk_receive_queue.lock);
po->stats.tp_drops++;
+ po->stats.tp_gap++;
spin_unlock(&sk->sk_receive_queue.lock);
drop_n_restore:
@@ -811,6 +872,7 @@ drop:
ring_is_full:
po->stats.tp_drops++;
+ po->stats.tp_gap++;
spin_unlock(&sk->sk_receive_queue.lock);
sk->sk_data_ready(sk, 0);
@@ -1223,6 +1285,8 @@ static int packet_release(struct socket *sock)
skb_queue_purge(&sk->sk_receive_queue);
sk_refcnt_debug_release(sk);
+ kfree(po->gaps);
+
sock_put(sk);
return 0;
}
@@ -1350,6 +1414,7 @@ static int packet_create(struct net *net, struct socket *sock, int protocol)
struct packet_sock *po;
__be16 proto = (__force __be16)protocol; /* weird, but documented */
int err;
+ unsigned int num_records = PAGE_SIZE/sizeof(struct packet_gap_record);
if (!capable(CAP_NET_RAW))
return -EPERM;
@@ -1360,6 +1425,7 @@ static int packet_create(struct net *net, struct socket *sock, int protocol)
sock->state = SS_UNCONNECTED;
err = -ENOBUFS;
+
sk = sk_alloc(net, PF_PACKET, GFP_KERNEL, &packet_proto);
if (sk == NULL)
goto out;
@@ -1374,6 +1440,19 @@ static int packet_create(struct net *net, struct socket *sock, int protocol)
sk->sk_family = PF_PACKET;
po->num = proto;
+ err = -ENOMEM;
+ po->gaps = kmalloc(sizeof(struct packet_gap_record)*num_records,
+ GFP_KERNEL);
+ if (!po->gaps)
+ goto out_free;
+ po->gap_tail = po->gap_head = 0;
+ po->gap_list_size = num_records;
+
+ for (num_records = 0; num_records < po->gap_list_size; num_records++) {
+ po->gaps[num_records].skb = NULL;
+ po->gaps[num_records].gap = 0;
+ }
+
sk->sk_destruct = packet_sock_destruct;
sk_refcnt_debug_inc(sk);
@@ -1402,6 +1481,9 @@ static int packet_create(struct net *net, struct socket *sock, int protocol)
sock_prot_inuse_add(net, &packet_proto, 1);
write_unlock_bh(&net->packet.sklist_lock);
return 0;
+
+out_free:
+ sk_free(sk);
out:
return err;
}
@@ -1418,6 +1500,7 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,
struct sk_buff *skb;
int copied, err;
struct sockaddr_ll *sll;
+ __u32 gap;
err = -EINVAL;
if (flags & ~(MSG_PEEK|MSG_DONTWAIT|MSG_TRUNC|MSG_CMSG_COMPAT))
@@ -1492,10 +1575,15 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,
aux.tp_mac = 0;
aux.tp_net = skb_network_offset(skb);
aux.tp_vlan_tci = skb->vlan_tci;
-
put_cmsg(msg, SOL_PACKET, PACKET_AUXDATA, sizeof(aux), &aux);
}
+ lock_sock(sk);
+ gap = check_packet_gap(skb, pkt_sk(sk));
+ release_sock(sk);
+ if (gap)
+ put_cmsg(msg, SOL_PACKET, PACKET_GAPDATA, sizeof(u32), &gap);
+
/*
* Free or return the buffer as appropriate. Again this
* hides all the races and re-entrancy issues from us.
^ permalink raw reply related
* Re: [PATCH] tunnel: eliminate recursion field
From: Eric Dumazet @ 2009-09-23 20:28 UTC (permalink / raw)
To: David S. Miller; +Cc: Linux Netdev List
In-Reply-To: <4ABA8254.9000701@gmail.com>
Eric Dumazet a écrit :
> It seems recursion field from "struct ip_tunnel" is not anymore needed.
> recursion prevention is done at the upper level (in dev_queue_xmit()),
> since we use HARD_TX_LOCK protection for tunnels.
>
> This avoids a cache line ping pong on "struct ip_tunnel" : This structure
> should be now mostly read on xmit and receive paths.
Oops I forgot ipv6 tunnels, silly me, here is an updated version.
Thanks
[PATCH] tunnel: eliminate recursion field
It seems recursion field from "struct ip_tunnel" is not anymore needed.
recursion prevention is done at the upper level (in dev_queue_xmit()),
since we use HARD_TX_LOCK protection for tunnels.
This avoids a cache line ping pong on "struct ip_tunnel" : This structure
should be now mostly read on xmit and receive paths.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
include/net/ipip.h | 1 -
net/ipv4/ip_gre.c | 13 +------------
net/ipv4/ipip.c | 8 --------
net/ipv6/ip6_tunnel.c | 7 -------
net/ipv6/sit.c | 8 --------
5 files changed, 1 insertion(+), 36 deletions(-)
diff --git a/include/net/ipip.h b/include/net/ipip.h
index 5d3036f..76e3ea6 100644
--- a/include/net/ipip.h
+++ b/include/net/ipip.h
@@ -12,7 +12,6 @@ struct ip_tunnel
struct ip_tunnel *next;
struct net_device *dev;
- int recursion; /* Depth of hard_start_xmit recursion */
int err_count; /* Number of arrived ICMP errors */
unsigned long err_time; /* Time when the last ICMP error arrived */
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index d9645c9..41ada99 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -66,10 +66,7 @@
solution, but it supposes maintaing new variable in ALL
skb, even if no tunneling is used.
- Current solution: t->recursion lock breaks dead loops. It looks
- like dev->tbusy flag, but I preferred new variable, because
- the semantics is different. One day, when hard_start_xmit
- will be multithreaded we will have to use skb->encapsulation.
+ Current solution: HARD_TX_LOCK lock breaks dead loops.
@@ -678,11 +675,6 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
__be32 dst;
int mtu;
- if (tunnel->recursion++) {
- stats->collisions++;
- goto tx_error;
- }
-
if (dev->type == ARPHRD_ETHER)
IPCB(skb)->flags = 0;
@@ -820,7 +812,6 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
ip_rt_put(rt);
stats->tx_dropped++;
dev_kfree_skb(skb);
- tunnel->recursion--;
return NETDEV_TX_OK;
}
if (skb->sk)
@@ -888,7 +879,6 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
nf_reset(skb);
IPTUNNEL_XMIT();
- tunnel->recursion--;
return NETDEV_TX_OK;
tx_error_icmp:
@@ -897,7 +887,6 @@ tx_error_icmp:
tx_error:
stats->tx_errors++;
dev_kfree_skb(skb);
- tunnel->recursion--;
return NETDEV_TX_OK;
}
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
index 62548cb..08ccd34 100644
--- a/net/ipv4/ipip.c
+++ b/net/ipv4/ipip.c
@@ -402,11 +402,6 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
__be32 dst = tiph->daddr;
int mtu;
- if (tunnel->recursion++) {
- stats->collisions++;
- goto tx_error;
- }
-
if (skb->protocol != htons(ETH_P_IP))
goto tx_error;
@@ -485,7 +480,6 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
ip_rt_put(rt);
stats->tx_dropped++;
dev_kfree_skb(skb);
- tunnel->recursion--;
return NETDEV_TX_OK;
}
if (skb->sk)
@@ -523,7 +517,6 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
nf_reset(skb);
IPTUNNEL_XMIT();
- tunnel->recursion--;
return NETDEV_TX_OK;
tx_error_icmp:
@@ -531,7 +524,6 @@ tx_error_icmp:
tx_error:
stats->tx_errors++;
dev_kfree_skb(skb);
- tunnel->recursion--;
return NETDEV_TX_OK;
}
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 7d25bbe..c595bbe 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -1043,11 +1043,6 @@ ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
struct net_device_stats *stats = &t->dev->stats;
int ret;
- if (t->recursion++) {
- stats->collisions++;
- goto tx_err;
- }
-
switch (skb->protocol) {
case htons(ETH_P_IP):
ret = ip4ip6_tnl_xmit(skb, dev);
@@ -1062,14 +1057,12 @@ ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
if (ret < 0)
goto tx_err;
- t->recursion--;
return NETDEV_TX_OK;
tx_err:
stats->tx_errors++;
stats->tx_dropped++;
kfree_skb(skb);
- t->recursion--;
return NETDEV_TX_OK;
}
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index 0ae4f64..fcb5396 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -626,11 +626,6 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
struct in6_addr *addr6;
int addr_type;
- if (tunnel->recursion++) {
- stats->collisions++;
- goto tx_error;
- }
-
if (skb->protocol != htons(ETH_P_IPV6))
goto tx_error;
@@ -753,7 +748,6 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
ip_rt_put(rt);
stats->tx_dropped++;
dev_kfree_skb(skb);
- tunnel->recursion--;
return NETDEV_TX_OK;
}
if (skb->sk)
@@ -794,7 +788,6 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
nf_reset(skb);
IPTUNNEL_XMIT();
- tunnel->recursion--;
return NETDEV_TX_OK;
tx_error_icmp:
@@ -802,7 +795,6 @@ tx_error_icmp:
tx_error:
stats->tx_errors++;
dev_kfree_skb(skb);
- tunnel->recursion--;
return NETDEV_TX_OK;
}
^ permalink raw reply related
* [PATCH] tunnel: eliminate recursion field
From: Eric Dumazet @ 2009-09-23 20:17 UTC (permalink / raw)
To: David S. Miller; +Cc: Linux Netdev List
It seems recursion field from "struct ip_tunnel" is not anymore needed.
recursion prevention is done at the upper level (in dev_queue_xmit()),
since we use HARD_TX_LOCK protection for tunnels.
This avoids a cache line ping pong on "struct ip_tunnel" : This structure
should be now mostly read on xmit and receive paths.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
include/net/ipip.h | 1 -
net/ipv4/ip_gre.c | 13 +------------
net/ipv4/ipip.c | 8 --------
3 files changed, 1 insertion(+), 21 deletions(-)
diff --git a/include/net/ipip.h b/include/net/ipip.h
index 5d3036f..76e3ea6 100644
--- a/include/net/ipip.h
+++ b/include/net/ipip.h
@@ -12,7 +12,6 @@ struct ip_tunnel
struct ip_tunnel *next;
struct net_device *dev;
- int recursion; /* Depth of hard_start_xmit recursion */
int err_count; /* Number of arrived ICMP errors */
unsigned long err_time; /* Time when the last ICMP error arrived */
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index d9645c9..41ada99 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -66,10 +66,7 @@
solution, but it supposes maintaing new variable in ALL
skb, even if no tunneling is used.
- Current solution: t->recursion lock breaks dead loops. It looks
- like dev->tbusy flag, but I preferred new variable, because
- the semantics is different. One day, when hard_start_xmit
- will be multithreaded we will have to use skb->encapsulation.
+ Current solution: HARD_TX_LOCK lock breaks dead loops.
@@ -678,11 +675,6 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
__be32 dst;
int mtu;
- if (tunnel->recursion++) {
- stats->collisions++;
- goto tx_error;
- }
-
if (dev->type == ARPHRD_ETHER)
IPCB(skb)->flags = 0;
@@ -820,7 +812,6 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
ip_rt_put(rt);
stats->tx_dropped++;
dev_kfree_skb(skb);
- tunnel->recursion--;
return NETDEV_TX_OK;
}
if (skb->sk)
@@ -888,7 +879,6 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
nf_reset(skb);
IPTUNNEL_XMIT();
- tunnel->recursion--;
return NETDEV_TX_OK;
tx_error_icmp:
@@ -897,7 +887,6 @@ tx_error_icmp:
tx_error:
stats->tx_errors++;
dev_kfree_skb(skb);
- tunnel->recursion--;
return NETDEV_TX_OK;
}
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
index 62548cb..08ccd34 100644
--- a/net/ipv4/ipip.c
+++ b/net/ipv4/ipip.c
@@ -402,11 +402,6 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
__be32 dst = tiph->daddr;
int mtu;
- if (tunnel->recursion++) {
- stats->collisions++;
- goto tx_error;
- }
-
if (skb->protocol != htons(ETH_P_IP))
goto tx_error;
@@ -485,7 +480,6 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
ip_rt_put(rt);
stats->tx_dropped++;
dev_kfree_skb(skb);
- tunnel->recursion--;
return NETDEV_TX_OK;
}
if (skb->sk)
@@ -523,7 +517,6 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
nf_reset(skb);
IPTUNNEL_XMIT();
- tunnel->recursion--;
return NETDEV_TX_OK;
tx_error_icmp:
@@ -531,7 +524,6 @@ tx_error_icmp:
tx_error:
stats->tx_errors++;
dev_kfree_skb(skb);
- tunnel->recursion--;
return NETDEV_TX_OK;
}
^ permalink raw reply related
* Re: [PATCHv5 3/3] vhost_net: a kernel-level virtio server
From: Avi Kivity @ 2009-09-23 19:37 UTC (permalink / raw)
To: Gregory Haskins
Cc: Ira W. Snyder, Michael S. Tsirkin, netdev, virtualization, kvm,
linux-kernel, mingo, linux-mm, akpm, hpa, Rusty Russell, s.hetze,
alacrityvm-devel
In-Reply-To: <4ABA61D1.80703@gmail.com>
On 09/23/2009 08:58 PM, Gregory Haskins wrote:
>>
>>> It also pulls parts of the device model into the host kernel.
>>>
>> That is the point. Most of it needs to be there for performance.
>>
> To clarify this point:
>
> There are various aspects about designing high-performance virtual
> devices such as providing the shortest paths possible between the
> physical resources and the consumers. Conversely, we also need to
> ensure that we meet proper isolation/protection guarantees at the same
> time. What this means is there are various aspects to any
> high-performance PV design that require to be placed in-kernel to
> maximize the performance yet properly isolate the guest.
>
> For instance, you are required to have your signal-path (interrupts and
> hypercalls), your memory-path (gpa translation), and
> addressing/isolation model in-kernel to maximize performance.
>
Exactly. That's what vhost puts into the kernel and nothing more.
> Vbus accomplishes its in-kernel isolation model by providing a
> "container" concept, where objects are placed into this container by
> userspace. The host kernel enforces isolation/protection by using a
> namespace to identify objects that is only relevant within a specific
> container's context (namely, a "u32 dev-id"). The guest addresses the
> objects by its dev-id, and the kernel ensures that the guest can't
> access objects outside of its dev-id namespace.
>
vhost manages to accomplish this without any kernel support. The guest
simply has not access to any vhost resources other than the guest->host
doorbell, which is handed to the guest outside vhost (so it's somebody
else's problem, in userspace).
> All that is required is a way to transport a message with a "devid"
> attribute as an address (such as DEVCALL(devid)) and the framework
> provides the rest of the decode+execute function.
>
vhost avoids that.
> Contrast this to vhost+virtio-pci (called simply "vhost" from here).
>
It's the wrong name. vhost implements only the data path.
> It is not immune to requiring in-kernel addressing support either, but
> rather it just does it differently (and its not as you might expect via
> qemu).
>
> Vhost relies on QEMU to render PCI objects to the guest, which the guest
> assigns resources (such as BARs, interrupts, etc).
vhost does not rely on qemu. It relies on its user to handle
configuration. In one important case it's qemu+pci. It could just as
well be the lguest launcher.
> A PCI-BAR in this
> example may represent a PIO address for triggering some operation in the
> device-model's fast-path. For it to have meaning in the fast-path, KVM
> has to have in-kernel knowledge of what a PIO-exit is, and what to do
> with it (this is where pio-bus and ioeventfd come in). The programming
> of the PIO-exit and the ioeventfd are likewise controlled by some
> userspace management entity (i.e. qemu). The PIO address and value
> tuple form the address, and the ioeventfd framework within KVM provide
> the decode+execute function.
>
Right.
> This idea seemingly works fine, mind you, but it rides on top of a *lot*
> of stuff including but not limited to: the guests pci stack, the qemu
> pci emulation, kvm pio support, and ioeventfd. When you get into
> situations where you don't have PCI or even KVM underneath you (e.g. a
> userspace container, Ira's rig, etc) trying to recreate all of that PCI
> infrastructure for the sake of using PCI is, IMO, a lot of overhead for
> little gain.
>
For the N+1th time, no. vhost is perfectly usable without pci. Can we
stop raising and debunking this point?
> All you really need is a simple decode+execute mechanism, and a way to
> program it from userspace control. vbus tries to do just that:
> commoditize it so all you need is the transport of the control messages
> (like DEVCALL()), but the decode+execute itself is reuseable, even
> across various environments (like KVM or Iras rig).
>
If you think it should be "commodotized", write libvhostconfig.so.
> And your argument, I believe, is that vbus allows both to be implemented
> in the kernel (though to reiterate, its optional) and is therefore a bad
> design, so lets discuss that.
>
> I believe the assertion is that things like config-space are best left
> to userspace, and we should only relegate fast-path duties to the
> kernel. The problem is that, in my experience, a good deal of
> config-space actually influences the fast-path and thus needs to
> interact with the fast-path mechanism eventually anyway.
> Whats left
> over that doesn't fall into this category may cheaply ride on existing
> plumbing, so its not like we created something new or unnatural just to
> support this subclass of config-space.
>
Flexibility is reduced, because changing code in the kernel is more
expensive than in userspace, and kernel/user interfaces aren't typically
as wide as pure userspace interfaces. Security is reduced, since a bug
in the kernel affects the host, while a bug in userspace affects just on
guest.
Example: feature negotiation. If it happens in userspace, it's easy to
limit what features we expose to the guest. If it happens in the
kernel, we need to add an interface to let the kernel know which
features it should expose to the guest. We also need to add an
interface to let userspace know which features were negotiated, if we
want to implement live migration. Something fairly trivial bloats rapidly.
> For example: take an attribute like the mac-address assigned to a NIC.
> This clearly doesn't need to be in-kernel and could go either way (such
> as a PCI config-space register).
>
> As another example: consider an option bit that enables a new feature
> that affects the fast-path, like RXBUF merging. If we use the split
> model where config space is handled by userspace and fast-path is
> in-kernel, the userspace component is only going to act as a proxy.
> I.e. it will pass the option down to the kernel eventually. Therefore,
> there is little gain in trying to split this type of slow-path out to
> userspace. In fact, its more work.
>
As you can see above, userspace needs to be involved in this, and the
number of interfaces required is smaller if it's in userspace: you only
need to know which features the kernel supports (they can be enabled
unconditionally, just not exposed).
Further, some devices are perfectly happy to be implemented in
userspace, so we need userspace configuration support anyway. Why
reimplement it in the kernel?
--
Do not meddle in the internals of kernels, for they are subtle and quick to panic.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Is perfectly
From: Paul Martin @ 2009-09-23 19:05 UTC (permalink / raw)
To: srucnoc, alan, support, contact, contact, cris_mihai, daniel,
hydrogin
http://becomtec.com/THBedc9IZ6.html
^ permalink raw reply
* Re: Getting physical packet counts with LRO enabled with ixgbe?
From: Rick Jones @ 2009-09-23 19:03 UTC (permalink / raw)
To: Peter P Waskiewicz Jr; +Cc: Ben Greear, NetDev
In-Reply-To: <1253731834.2538.32.camel@localhost.localdomain>
>>Next time you guys re-compile your hardware, please consider adding byte counters :)
>
>
> On 10G adapters, byte counters can skyrocket quickly, so we'd need to
> read them often to avoid them wrapping.
10G Ethernet is ~1.16 GB/s (GiB/s for purists I guess) for simplicity, call that
2GB/s or 2^31 bytes per second. If the counter is 64 bits, that would suggest
wrap in 2^64/2^31 or 2^33 seconds right? Or have I made some nasty math error?
I'm having quit a difficult time imagining that someone would have 32 bit
counters in a 10G NIC.
rick jones
^ permalink raw reply
* Re: Getting physical packet counts with LRO enabled with ixgbe?
From: Ben Greear @ 2009-09-23 18:56 UTC (permalink / raw)
To: Peter P Waskiewicz Jr; +Cc: NetDev
In-Reply-To: <1253731834.2538.32.camel@localhost.localdomain>
On 09/23/2009 11:50 AM, Peter P Waskiewicz Jr wrote:
>> That's a bummer. I'm guessing you might get close to right on average with some
>> trivial math, but if someone is sending you pkts with size of 1000 and
>> your MTU is 1500, would there be any way to tell that the pkts were originally
>> 1000 bytes instead of 1500?
>
> Good point.
>
>> Next time you guys re-compile your hardware, please consider adding byte counters :)
>
> On 10G adapters, byte counters can skyrocket quickly, so we'd need to
> read them often to avoid them wrapping. But I will forward your request
> to our HW design folks and see if they have other ideas to implement
> these counters and make them efficient.
It still takes a while to wrap 64-bit counters :)
But, you do have to read every 3 secs or so if you're using 32-bit counters.
This can be dealt with in user-space easily enough as long as polling
the NIC for counters is efficient.
Thanks,
Ben
--
Ben Greear <greearb@candelatech.com>
Candela Technologies Inc http://www.candelatech.com
^ permalink raw reply
* Re: Getting physical packet counts with LRO enabled with ixgbe?
From: Peter P Waskiewicz Jr @ 2009-09-23 18:50 UTC (permalink / raw)
To: Ben Greear; +Cc: NetDev
In-Reply-To: <4ABA6D0D.8010200@candelatech.com>
On Wed, 2009-09-23 at 11:46 -0700, Ben Greear wrote:
> On 09/23/2009 11:32 AM, Peter P Waskiewicz Jr wrote:
> > On Wed, 2009-09-23 at 09:40 -0700, Ben Greear wrote:
> >> I notice that with LRO enabled, the interface stats count the LRO'd pkts,
> >> not the physical ones on the wire.
> >>
> >> I also tried using ethtool -S, but it seems those counters are the same.
> >>
> >> Is there any way to get the actual rx/tx packet count on the wire?
> >>
> >
> > Depending on which device you're using ixgbe with, there are slightly
> > different registers to get what you want.
> >
> > The only suggestion I have for you though is to refer to the datasheets
> > for each device on our SourceForge site (e1000.sf.net). Some of the
> > relevant counters to look at are PRC64, PRC127, etc, and GPRC/GPTC. For
> > the per-queue stuff, you'll need to look at the TQSMR and RQSMR mapping
> > registers. Let me know if you need assistance in using these registers.
>
> Thanks, I'll look at the data-sheet.
>
> I don't care about per-queue stats at this time, just
> over-all NIC stats.
>
> >> Also, for the rx/tx bytes, I assume that isn't counting the protocol headers
> >> for the physical pkts that have been merged into a single LRO packet. Is
> >> there any way to get the wire stats for bytes as well?
> >
> > The counters for per-byte are purely software-based, so if the packet is
> > LRO'd, you can probably do some somewhat trivial math with the MTU to
> > find the actual wire stats. But we only compute what we pass to the
> > stack, so it'd be the LRO'd packet.
>
> That's a bummer. I'm guessing you might get close to right on average with some
> trivial math, but if someone is sending you pkts with size of 1000 and
> your MTU is 1500, would there be any way to tell that the pkts were originally
> 1000 bytes instead of 1500?
Good point.
> Next time you guys re-compile your hardware, please consider adding byte counters :)
On 10G adapters, byte counters can skyrocket quickly, so we'd need to
read them often to avoid them wrapping. But I will forward your request
to our HW design folks and see if they have other ideas to implement
these counters and make them efficient.
Cheers,
-PJ
^ permalink raw reply
* Re: Getting physical packet counts with LRO enabled with ixgbe?
From: Ben Greear @ 2009-09-23 18:46 UTC (permalink / raw)
To: Peter P Waskiewicz Jr; +Cc: NetDev
In-Reply-To: <1253730766.2538.28.camel@localhost.localdomain>
On 09/23/2009 11:32 AM, Peter P Waskiewicz Jr wrote:
> On Wed, 2009-09-23 at 09:40 -0700, Ben Greear wrote:
>> I notice that with LRO enabled, the interface stats count the LRO'd pkts,
>> not the physical ones on the wire.
>>
>> I also tried using ethtool -S, but it seems those counters are the same.
>>
>> Is there any way to get the actual rx/tx packet count on the wire?
>>
>
> Depending on which device you're using ixgbe with, there are slightly
> different registers to get what you want.
>
> The only suggestion I have for you though is to refer to the datasheets
> for each device on our SourceForge site (e1000.sf.net). Some of the
> relevant counters to look at are PRC64, PRC127, etc, and GPRC/GPTC. For
> the per-queue stuff, you'll need to look at the TQSMR and RQSMR mapping
> registers. Let me know if you need assistance in using these registers.
Thanks, I'll look at the data-sheet.
I don't care about per-queue stats at this time, just
over-all NIC stats.
>> Also, for the rx/tx bytes, I assume that isn't counting the protocol headers
>> for the physical pkts that have been merged into a single LRO packet. Is
>> there any way to get the wire stats for bytes as well?
>
> The counters for per-byte are purely software-based, so if the packet is
> LRO'd, you can probably do some somewhat trivial math with the MTU to
> find the actual wire stats. But we only compute what we pass to the
> stack, so it'd be the LRO'd packet.
That's a bummer. I'm guessing you might get close to right on average with some
trivial math, but if someone is sending you pkts with size of 1000 and
your MTU is 1500, would there be any way to tell that the pkts were originally
1000 bytes instead of 1500?
Next time you guys re-compile your hardware, please consider adding byte counters :)
Thanks,
Ben
--
Ben Greear <greearb@candelatech.com>
Candela Technologies Inc http://www.candelatech.com
^ permalink raw reply
* Re: Getting physical packet counts with LRO enabled with ixgbe?
From: Peter P Waskiewicz Jr @ 2009-09-23 18:32 UTC (permalink / raw)
To: Ben Greear; +Cc: NetDev
In-Reply-To: <4ABA4F8B.6040504@candelatech.com>
On Wed, 2009-09-23 at 09:40 -0700, Ben Greear wrote:
> I notice that with LRO enabled, the interface stats count the LRO'd pkts,
> not the physical ones on the wire.
>
> I also tried using ethtool -S, but it seems those counters are the same.
>
> Is there any way to get the actual rx/tx packet count on the wire?
>
Depending on which device you're using ixgbe with, there are slightly
different registers to get what you want.
The only suggestion I have for you though is to refer to the datasheets
for each device on our SourceForge site (e1000.sf.net). Some of the
relevant counters to look at are PRC64, PRC127, etc, and GPRC/GPTC. For
the per-queue stuff, you'll need to look at the TQSMR and RQSMR mapping
registers. Let me know if you need assistance in using these registers.
> Also, for the rx/tx bytes, I assume that isn't counting the protocol headers
> for the physical pkts that have been merged into a single LRO packet. Is
> there any way to get the wire stats for bytes as well?
The counters for per-byte are purely software-based, so if the packet is
LRO'd, you can probably do some somewhat trivial math with the MTU to
find the actual wire stats. But we only compute what we pass to the
stack, so it'd be the LRO'd packet.
Cheers,
-PJ Waskiewicz
^ permalink raw reply
* [PATCH 8/8] [RFC] CAIF Protocol Stack
From: sjur.brandeland @ 2009-09-23 17:31 UTC (permalink / raw)
To: netdev; +Cc: Kim.xx.Lilliestierna, sjur.brandeland
From: Kim Lilliestierna <Kim.xx.Lilliestierna@ericsson.com>
Signed-off-by: sjur.brandeland@stericsson.com
---
drivers/net/Makefile | 1 +
net/Kconfig | 1 +
net/Makefile | 1 +
3 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 7629c90..d8441a8 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -255,4 +255,5 @@ obj-$(CONFIG_NETXEN_NIC) += netxen/
obj-$(CONFIG_NIU) += niu.o
obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
obj-$(CONFIG_SFC) += sfc/
+obj-$(CONFIG_CAIF) += caif/
diff --git a/net/Kconfig b/net/Kconfig
index 0210002..51e3eaa 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -253,5 +253,6 @@ endmenu
source "net/rfkill/Kconfig"
source "net/9p/Kconfig"
+source "net/caif/Kconfig"
endif # if NET
diff --git a/net/Makefile b/net/Makefile
index ba324ae..449dd91 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -57,6 +57,7 @@ obj-$(CONFIG_NETLABEL) += netlabel/
obj-$(CONFIG_IUCV) += iucv/
obj-$(CONFIG_RFKILL) += rfkill/
obj-$(CONFIG_NET_9P) += 9p/
+obj-$(CONFIG_CAIF) += caif/
ifneq ($(CONFIG_DCB),)
obj-y += dcb/
endif
--
1.6.0.4
^ permalink raw reply related
* [PATCH 7/8] [RFC] CAIF Protocol Stack
From: sjur.brandeland @ 2009-09-23 17:31 UTC (permalink / raw)
To: netdev; +Cc: Kim.xx.Lilliestierna, sjur.brandeland
From: Kim Lilliestierna <Kim.xx.Lilliestierna@ericsson.com>
Signed-off-by: sjur.brandeland@stericsson.com
---
Documentation/CAIF/Linux-CAIF.txt | 319 +++++++++++++++++
Documentation/CAIF/README | 60 ++++
Documentation/CAIF/chardevconfig/Makefile | 11 +
Documentation/CAIF/chardevconfig/README | 39 ++
Documentation/CAIF/chardevconfig/caif_cmd_parse.c | 365 ++++++++++++++++++++
Documentation/CAIF/chardevconfig/chardevconfig.c | 111 ++++++
.../CAIF/chardevconfig/create_devices.config | 12 +
.../CAIF/chardevconfig/delete_devices.config | 12 +
Documentation/CAIF/ldiscd/ldiscd.c | 123 +++++++
9 files changed, 1052 insertions(+), 0 deletions(-)
create mode 100644 Documentation/CAIF/Linux-CAIF.txt
create mode 100644 Documentation/CAIF/README
create mode 100644 Documentation/CAIF/chardevconfig/Makefile
create mode 100644 Documentation/CAIF/chardevconfig/README
create mode 100644 Documentation/CAIF/chardevconfig/caif_cmd_parse.c
create mode 100644 Documentation/CAIF/chardevconfig/chardevconfig.c
create mode 100644 Documentation/CAIF/chardevconfig/create_devices.config
create mode 100644 Documentation/CAIF/chardevconfig/delete_devices.config
create mode 100644 Documentation/CAIF/ldiscd/ldiscd.c
diff --git a/Documentation/CAIF/Linux-CAIF.txt b/Documentation/CAIF/Linux-CAIF.txt
new file mode 100644
index 0000000..42fd66e
--- /dev/null
+++ b/Documentation/CAIF/Linux-CAIF.txt
@@ -0,0 +1,319 @@
+Linux CAIF
+===========
+
+Introduction
+------------
+CAIF is a MUX protocol used by ST-Ericsson cellular modems for
+communication
+between Modem and host. The host processes can open virtual AT
+channels, initiate GPRS Data connections, Video channels and
+Utility Channels.
+The Utility Channels are general purpose pipes between modem
+and host.
+
+ST-Ericsson modems support a number of transports between modem
+and host,
+currently Uart and Shared Memory are available for Linux.
+
+Architecture:
+------------
+The Implementation of CAIF is divided into:
+* CAIF Drivers: Character Device, Net Device and Kernel API.
+* CAIF Generic Protocol Implementation
+* CAIF Link Layer
+
+CAIF is using IOCTLs to manage the CAIF Drivers.
+
+
+ IOCTL
+ !
+ ! +------+ +------+ +------+
+ ! +------+! +------+! +------+!
+ ! ! Char !! !Kernel!! ! Net !!
+ ! ! Dev !+ ! API !+ ! Dev !+ <- CAIF Drivers
+ ! +------+ +------! +------+
+ ! ! ! !
+ ! +----------!----------+
+ ! +------+ <- CAIF Protocol Implementation
+ +-------> ! CAIF ! /dev/caifconfig
+ +------+
+ +--------!--------+
+ ! !
+ +------+ +-----+
+ !ShMem ! ! TTY ! <- Link Layer
+ +------+ +-----+
+
+
+
+Using CAIF Character Device
+-----------------------------
+CAIF character devices are configured by use of IOCTLs on the
+node "/dev/caifconfig". E.g. the following code will create an
+CAIF Character Device that will make an AT channel accessible:
+
+ struct caif_channel_create_action at = {
+ .name = {
+ .name = "cnhlatl",
+ .type = CAIF_DEV_CHR
+ },
+ .config = {
+ .channel = CAIF_CHTY_AT,
+ }};
+ fd = open("/dev/caifconfig",..);
+ ioctl(fd, CAIF_IOC_CONFIG_DEVICE,&at_config);
+
+A configuration tool chardevconfig exist in order to simplify
+creation of CAIF Channels (typically used from init scripts).
+E.g:
+
+ $chardevconfig /dev/caifconfig -
+ CREATE TYPE=AT NAME=chnlat1 DEVTYPE=CHAR ^D
+
+This will result in creation of the device node "/dev/chnlat1".
+"/dev/chnlat1" can be used to read and write AT commands and
+responses
+from the modem:
+
+ $cat /dev/chnlat1 &
+ $printf "AT\r" > /dev/chnlat1
+ OK
+
+
+
+Using CAIF Net Device
+----------------------
+CAIF Net device can be created similarly as the character
+device.
+E.g:
+
+ $chardevconfig /dev/caifconfig -
+ CREATE TYPE=DGM NAME=caif0 DEVTYPE=NET CONNID=1 ^D
+
+ $ifconfig caif0 <ip address> up
+
+
+Using the Kernel API
+----------------------
+The Kernel API is used for accessing CAIF channels from the
+kernel.
+The user of the API has to implement two callbacks for receive
+and control.
+The receive callback give a CAIF packet as a SKB. The control
+callback will
+notify about channel initialization complete, and flow-on/flow-
+off.
+
+
+ struct caif_device caif_dev = {
+ .caif_config = {
+ .name = "MYDEV"
+ .type = CAIF_CHTY_AT
+ }
+ .receive_cb = my_receive,
+ .control_cb = my_control,
+ };
+
+ caif_add_device(&caif_dev);
+
+ caif_transmit(&caif_dev, skb);
+
+
+See the caif_kernel.h for details about the CAIF kernel API.
+
+
+
+
+
+
+
+
+I M P L E M E N T A T I O N
+===========================
+===========================
+
+
+
+
+GenCAIF - The Generic CAIF Protocol Layer
+=========================================
+
+
+GenCaif is a generic CAIF protocol implementation. It implements the CAIF
+protocol as specified in "CAIF Protocol Specification" (155 19-CRH 109 913).
+GenCaif implements the CAIF protocol stack in a layered approach, where
+each layer described in the specification is implemented as a separate layer.
+The architecture is inspired by the design patterns "Protocol Layer" and
+"Protocol Packet".
+
+== CAIF structure ==
+
+The goal is to have caif as system independent as possible.
+All caif code can be found under GenCaif/src and GenCaif/inc.
+The actual linux module implementation is under src/kernel.
+There is also a user space program that is not up to date to run the stack in
+user space for testing.
+
+We have tested the kernel implementation on the emulator with a modem and we
+are able to enumerate and make a link setup.
+
+GenCAIF is:
+ - Simple implementation of CAIF.
+ - Layered architecture (ala Streams), each layer specified CAIF
+ specification is implemented in a separate c-file.
+ - Client of GenCaif must implement PHY layer to access physical HW
+ with receive and transmit functions.
+ - Client of GenCaif must call configuration function add PHY layer.
+ - Client of GenCaif must implement adaptation layer to consume/produce
+ CAIF payload with receive and transmit functions.
+ - Client of GenCaif must call configuration function add adaptation
+ layer.
+ - When receiving / transmitting CAIF Packets (cfpkt) ownership is passed
+ to the called function (except Framinglayer's receive function).
+
+
+
+Layered Architecture
+--------------------
+The CAIF protocol can be divided into two parts Support functions and Protocol
+Implementation. The support functions include:
+
+ - CFPKT CAIF Packet. Implementation of CAIF Protocol Packet. The
+ CAIF Packet has functions for creating,destroying, adding content, and
+ adding/extracting header and trailers to protocol packets.
+
+ - CFLST CAIF list implementation.
+
+ - CFGLUE CAIF Glue. Contains OS Specifics such as memory
+ allocation, endianness etc.
+
+
+The CAIF Protocol implementation contains:
+
+ - CFCNFG CAIF Configuration layer. Configures the CAIF Protocol
+ Stack, and has Client interface for adding Link-Layer and
+ Driver interfaces on top of the CAIF Stack.
+
+ - CFCTRL CAIF Control layer. Encodes and Decodes control messages
+ such as enumeration, and channel setup. And matches request and
+ response messages.
+
+ - CFSERVL General CAIF Service Layer functionality, handle flow
+ control and remote shutdown requests.
+
+ - CFVEI CAIF VEI layer. Handles CAIF VEI layer (AT-Channel),
+ code/encodes VEI frames.
+
+ - CFDGML CAIF Data-gram layer. Handles CAIF Data-gram layer(IP
+ traffic), code/encodes Datagram frames.
+
+ - CFMUX CAIF Mux layer. Handles multiplexing between multiple
+ physical bearers and multiple channels such as VEI, Data-gram etc
+ The MUX is keeping track of the existing CAIF Channels and
+ Physical Instances and selects the apropriate instance based
+ on Channel-Id and Physical-ID.
+
+ - CFFRML CAIF Framing layer. Handles Framing i.e. Frame length
+ and frame checksum.
+
+ - CFSERL CAIF Serial layer. Handles concatenation/split of frames
+ into CAIF Frames with correct length.
+
+ - CFSHML CAIF Shared Memory layer.
+
+
+
+ +---------+
+ | Config |
+ | CFCNFG |
+ +---------+
+ !
+ +---------+ +---------+ +---------+
+ | AT | | Control | | Datagram|
+ | CFVEIL | | CFCTRL | | CFDGML |
+ +---------+ +---------+ +---------+
+ \_____________!______________/
+ !
+ +---------+
+ | MUX |
+ | |
+ +---------+
+ _____!_____
+ / \
+ +---------+ +---------+
+ | CFFRML | | CFFRML |
+ | Framing | | Framing |
+ +---------+ +---------+
+ ! !
+ +---------+ +---------+
+ | Sh-Mem | | Serial |
+ | CFSHML | | CFSERL |
+ +---------+ +---------+
+
+
+
+In this layered approach the following "rules" applies.
+ - All layers embedd the same structure 'struct layer'
+ - Layer do not depend on any others layer private data.
+ - Layers are stacked by setting the pointers
+ layer->up , layer->dn
+ - In order to send data upwards each layer should do
+ layer->up->receive(layer->up, packet);
+ - In oder to send data downwards each layer should do
+ layer->dn->transmit(layer->dn, packet);
+
+
+
+Linux Driver Implementation
+===========================
+
+Linux GPRS Net Device and Character Devices are implemented on top of the
+Generic CAIF protocol. The Net device and Chr device has an instance of
+'struct layer' as the generic caif protocol stack.
+Net and Chr device implements the 'receive()' function defined by
+'struct layer' as the rest of the CAIF stack. In this way transmit and
+reception of packets is handled as the rest of the layers, 'dn->transmit()'
+function is called in order to tranmit data.
+
+The layer on top of the Generic CAIF is called an "adaptation layer".
+
+
+Configuration of Drivers
+------------------------
+
+Configuration is the most complex part of the CAIF protocol.
+Configuration is controlled by the Misc device 'caifconfig'
+implemented in caif_chr. A device is created when a IOCTL
+command for creation is received containing information about
+the CAIF Channel type to be created and the type of device to instanciate
+(Net Device or Character Device).
+
+The Net Device and Character Device will register into the 'caifconfig'
+device by calling 'caif_register_netdev' and 'caif_register_chrdev'.
+When registered the 'caifconfig' module will keep function pointers
+to the devices used when IOCTL creates new devices.
+
+
+The CAIF Configuration module CFCNFG is responsible for connecting and
+setting up the entire CAIF stack.
+
+The function 'cfcnfg_add_adapt_layer' is used to connect a Linux Driver
+to the ST-Ericsson modem. This function will trigger the setup of CAIF
+Channel by sending a "LinkSetup" message to the modem. When the
+"LinkSetupResponse" is received the CAIF protocol for the requested
+CAIF Service will be set up.
+
+The CAIF Channel configuration parameters will be given as input.
+
+
+
+Configuration of Link Layer
+---------------------------
+The Link Layer (or Phy Layer) must implement the 'transmit' function
+defined by 'struct layer' in order to send payload. When data is received
+the Link Layer calls 'up->receive()'.
+Configuring the link layer is done by the function 'cfcnfg_add_phy_layer'.
+This function will set up the CAIF Layers for the new Link Layer.
+
+
+The physical Link Layers registers intself into 'caifconfig' by
+calling the function 'caif_phy_register()'.
diff --git a/Documentation/CAIF/README b/Documentation/CAIF/README
new file mode 100644
index 0000000..aa04150
--- /dev/null
+++ b/Documentation/CAIF/README
@@ -0,0 +1,60 @@
+copyright (C) ST-Ericsson AB 2009
+Author: Sjur Brendeland/ sjur.brandeland@stericsson.com
+ Kim Lilliestierna Kim.xx.Lilliestierna@ericsson.com
+License terms: GNU General Public License (GPL), version 2.
+
+=== Start ===
+Copy the .ko files onto the board, and do insmod:
+
+insmod caif.ko
+insmod phyif_msl.ko
+insmod chnl_chr.ko
+insmod chnl_net.ko
+ifconfig caif0 <your-home-address> up
+
+
+=== Test Loopback on net device ===
+insmod chnl_net.ko loop=yes
+ifconfig caif0 192.168.0.1 up
+ping -c 10 -s 1000 192.168.0.2
+
+=== Preparing the setup.===
+
+Make sure that the kernel is built with module support.
+
+There are some things that need to be
+tweaked to get the host TTY correctly setup to talk to the modem.
+Since the CAIF stack is running in the kernel and we want to use the existing
+TTY we are installing our physical serial driver as a line discipline above
+the TTY device.
+
+To achieve this we need the help of a daemon program called ldiscd.
+The benefit is that we can hook up to any TTY, the downside is that we need
+an extra operation in order to install the line discipline.
+
+Getting the host TTY to behave (This should only be necessary when running
+on the emulator, otherwise the ldiscd should correctly configure the UART).
+
+Retrieve the current settings:
+
+$ stty -a -F /dev/ttyUSB1
+
+Make sure that we are having 115200, 8n1, CTS/RTS (for example if CTS/RTS is missing):
+$ stty -F /dev/ttyUSB1 ctsrts
+
+Build the line discipline daemon. (You need to change CAIF_LDISC_TTY
+if your not using /dev/ttyS0.)
+
+$ gcc ldiscd.c -o ldisc
+
+Install the line discipline (daemon)
+$ ldisc
+
+Install the VEI channel (this will enumerate and do the linksetup of the first VEI channel. If this goes well you should see /dev/chn*) (There are printks logging all buffers that can be checked with dmesg):
+$ modprobe chnl_chr
+
+The AT (VEI) channel is ready to use (you can now send AT commands on it):
+$ echo -e "AT\r\n" > /dev/chnlat10
+
+Verify that you got an OK response (There are printks logging all buffers that can be checked with dmesg):
+$ cat /dev/chnlat10
diff --git a/Documentation/CAIF/chardevconfig/Makefile b/Documentation/CAIF/chardevconfig/Makefile
new file mode 100644
index 0000000..5bd7d90
--- /dev/null
+++ b/Documentation/CAIF/chardevconfig/Makefile
@@ -0,0 +1,11 @@
+CFLAGS=-g -Wall -I ../../../include -I../../../include/linux/caif
+
+PROGS=chardevconfig
+OBJS=chardevconfig.o caif_cmd_parse.o
+all: $(PROGS)
+
+chardevconfig: chardevconfig.o caif_cmd_parse.o
+ $(CC) $(CFLAGS) -o chardevconfig $(OBJS)
+
+clean:
+ rm -f $(PROGS) $(OBJS)
diff --git a/Documentation/CAIF/chardevconfig/README b/Documentation/CAIF/chardevconfig/README
new file mode 100644
index 0000000..bc7013b
--- /dev/null
+++ b/Documentation/CAIF/chardevconfig/README
@@ -0,0 +1,39 @@
+Usage: chardevconfig configdevice configfile
+Usage: chardevconfig configdevice -
+
+The program will read commands from the configfile (or stdin), parse them and
+do ioctl calls to the configdevice. One command per line. Lines with syntax
+errors (e.g. starting with a #) will be skipped.
+
+Examples:
+
+CREATE TYPE=AT NAME=chnlat10 DEVTYPE=CHAR PHYPREF=LAT PRIO=HI
+CREATE TYPE=AT NAME=chnlat11 DEVTYPE=CHAR PHYPREF=LAT PRIO=HI
+CREATE TYPE=AT NAME=chnlat12 DEVTYPE=CHAR PHYPREF=LAT PRIO=HI
+CREATE TYPE=AT NAME=chnlat13 DEVTYPE=CHAR PHYPREF=LAT PRIO=HI
+
+CREATE TYPE=RFM NAME=chn_rfm DEVTYPE=CHAR PHYPREF=BW CONNID=1 VOLUME=rfm
+CREATE TYPE=RFM NAME=chn_afs DEVTYPE=CHAR PHYPREF=BW CONNID=1 VOLUME=/afs
+CREATE TYPE=RFM NAME=chn_ifs DEVTYPE=CHAR PHYPREF=BW CONNID=1 VOLUME=/ifs
+CREATE TYPE=RFM NAME=chn_sys DEVTYPE=CHAR PHYPREF=BW CONNID=1 VOLUME=/sys"
+
+CREATE TYPE=UTIL NAME=chn_psocktest DEVTYPE=CHAR PHYPREF=LAT SOCK=CAIF_PSOC_TEST PARAM=01
+
+CREATE TYPE=DGM NAME=chn_datagram DEVTYPE=CHAR PHYPREF=BW CONNID=1
+CREATE TYPE=DGM NAME=datagram_raw_ip DEVTYPE=CHAR PHYPREF=BW CONNID=1
+
+CREATE TYPE=DGMLOOP NAME=datagram_loop DEVTYPE=CHAR PHYPREF=BW
+
+
+DELETE NAME=chnlat10 DEVTYPE=CHAR
+DELETE NAME=chnlat11 DEVTYPE=CHAR
+DELETE NAME=chnlat12 DEVTYPE=CHAR
+DELETE NAME=chnlat13 DEVTYPE=CHAR
+DELETE NAME=chn_rfm DEVTYPE=CHAR
+DELETE NAME=chn_afs DEVTYPE=CHAR
+DELETE NAME=chn_ifs DEVTYPE=CHAR
+DELETE NAME=chn_sys DEVTYPE=CHAR
+DELETE NAME=chn_psocktest DEVTYPE=CHAR
+DELETE NAME=chn_datagram DEVTYPE=CHAR
+DELETE NAME=datagram_loop DEVTYPE=CHAR
+DELETE NAME=datagram_raw_ip DEVTYPE=CHAR
diff --git a/Documentation/CAIF/chardevconfig/caif_cmd_parse.c b/Documentation/CAIF/chardevconfig/caif_cmd_parse.c
new file mode 100644
index 0000000..a52ded0
--- /dev/null
+++ b/Documentation/CAIF/chardevconfig/caif_cmd_parse.c
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2009
+ *
+ * Author: Daniel Martensson / Daniel.Martensson@stericsson.com
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <caif_config.h>
+#include <caif_ioctl.h>
+
+
+#define CFLOG_TRACE(a)
+
+
+/** Strips of blanks */
+void skip_blanks(char **c)
+{
+ while (**c == ' ')
+ (*c)++;
+
+}
+
+/** Parses the specified command.
+ * @param[in/out] in Pointer to where to start parsing, and if parsing successfull position behind parsed tex
+ * @param[in] cmd String to parse
+ * @return 1 on success, 0 on error
+ */
+int cmd_parse(char **in, char *cmd, int *err)
+{
+
+ char *pos = *in;
+ skip_blanks(&pos);
+ if (strncmp(pos, cmd, strlen(cmd)) == 0) {
+ pos += strlen(cmd);
+ skip_blanks(&pos);
+ *in = pos;
+ CFLOG_TRACE(("arg_parse: Match '%s'\n", cmd));
+
+ return 1;
+ }
+ return 0;
+}
+
+/** Parses a value pair on the format <CMD> = <arg>
+ * @param[in/out] in Pointer to where to start parsing, and if parsing successfull position behind parsed tex
+ * @param[in] cmd String to parse
+ * @param[out] arg Argument on the right side of '='
+ * @return 1 on success, 0 on error
+ */
+int arg_parse(char **in, char *cmd, char *arg, int arglen, int *err)
+{
+ char *pos = *in;
+ skip_blanks(&pos);
+ if (strncmp(pos, cmd, strlen(cmd)) == 0) {
+ pos += strlen(cmd);
+ skip_blanks(&pos);
+ if (*pos != '=') {
+ *err = 1;
+ return 0;
+ }
+ pos++;
+ while (*pos && *pos != ' ')
+ *arg++ = *pos++;
+ *arg = 0;
+ skip_blanks(&pos);
+ *in = pos;
+ CFLOG_TRACE(("arg_parse: Match '%s' Arg: '%s'\n", cmd, pos));
+ return 1;
+ }
+ return 0;
+}
+
+/** Parses a value pair on the format <CMD> = <int>
+ * @param[in/out] in Pointer to where to start parsing, and if parsing successfull position behind parsed tex
+ * @param[in] cmd
+ * @param[out] val Parsed integer value
+ * @return 1 on success, 0 on error
+ */
+int int_parse(char **in, char *cmd, int *val, int *err)
+{
+ char *pos = *in;
+ char arg[100];
+ if (arg_parse(&pos, cmd, arg, sizeof(arg), err)) {
+ sscanf(arg, "%d", val);
+ *in = pos;
+ CFLOG_TRACE(("int_parse: Match '%s' '%d'\n", cmd, *val));
+
+ return 1;
+ }
+ return 0;
+}
+
+/** Parses a value pair on the format <CMD> = <hex-string>
+ * @param[in/out] in Pointer to where to start parsing, and if parsing successfull position behind parsed tex
+ * @param[in] cmd Integer to parse
+ * @param[out] tobuf Parsed hex string
+ * @param[in] maxlen Max-len of the binary parsed hex string
+ * @param[out] buflen Length of the parsed hex sting (in bytes)
+ * @return 1 on success, 0 on error
+ */
+
+int
+hex_parse(char **in, char *cmd, unsigned char *tobuf, int maxlen,
+ int *buflen, int *err)
+{
+ char *pos = *in;
+ char tmp[3];
+ int start = maxlen - 1;
+ int val = 0;
+ int len = 0;
+ int i;
+ char hexstr[100];
+ unsigned char buf[256];
+ if (arg_parse(&pos, cmd, hexstr, sizeof(hexstr), err)) {
+
+ i = strlen(hexstr);
+ while (i > 0) {
+ tmp[0] = hexstr[i - 2];
+ tmp[1] = hexstr[i - 1];
+ tmp[2] = 0;
+ sscanf(tmp, "%x", &val);
+ buf[start--] = (unsigned char) (val & 0xff);
+ len++;
+ i -= 2;
+ }
+ *buflen = len;
+ for (i = 0; i < len; i++)
+ tobuf[i] = buf[maxlen - len + i];
+
+ *in = pos;
+ CFLOG_TRACE(("hex_parse: Match '%s' '%s'\n", cmd, tmp));
+ return 1;
+ }
+ return 0;
+}
+
+/** Parses a value pair on the format <CMD> = <token>
+ * @param[in/out] in Pointer to where to start parsing, and if parsing successfull position behind parsed tex
+ * @param[in] cmd Integer to parse
+ * @param[out] toktype Specify the class of tokens to be parsed
+ * @param[in] val Specify the value (in enums) corresponding to a token
+ * @return 1 on success, 0 on error
+ */
+
+int tok_parse(char **in, char *cmd, char *toktype, int *val, int *err)
+{
+ struct {
+ char *tok;
+ char *toktype;
+ int val;
+ } tokens[] = {
+ {
+ "LAT", "PHYPREF", CAIF_PHYPREF_LOW_LAT}, {
+ "BW", "PHYPREF", CAIF_PHYPREF_HIGH_BW}, {
+ "LOOP", "PHYPREF", _CAIF_PHYPREF_LOOP}, {
+ "LOW", "PRIO", CAIF_PRIO_LOW}, {
+ "NORM", "PRIO", CAIF_PRIO_NORMAL}, {
+ "HI", "PRIO", CAIF_PRIO_HIGH}, {
+ "AT", "CHTY", CAIF_CHTY_AT}, {
+ "DGM", "CHTY", CAIF_CHTY_DATAGRAM}, {
+ "DGMLOOP", "CHTY", CAIF_CHTY_DATAGRAM_LOOP}, {
+ "VIDEO", "CHTY", CAIF_CHTY_VIDEO}, {
+ "DEBUG", "CHTY", CAIF_CHTY_DEBUG}, {
+ "TRACE", "CHTY", CAIF_CHTY_DEBUG_TRACE}, {
+ "IDEBUG", "CHTY", CAIF_CHTY_DEBUG_INTERACT}, {
+ "RFM", "CHTY", CAIF_CHTY_RFM}, {
+ "UTIL", "CHTY", CAIF_CHTY_UTILITY}, {
+ "YES", "BOOL", 1}, {
+ "NO", "BOOL", 0}, {
+ "CHAR", "DEVTY", CAIF_DEV_CHR}, {
+ "NET", "DEVTY", CAIF_DEV_NET}, {
+
+ NULL, 0}
+ };
+
+ char tok[100];
+ char *pos = *in;
+
+ if (arg_parse(&pos, cmd, tok, sizeof(tok), err)) {
+ int i;
+ for (i = 0; tokens[i].tok != NULL; i++) {
+ if (strcmp(tokens[i].toktype, toktype) == 0
+ && strcmp(tokens[i].tok, tok) == 0) {
+
+ *val = tokens[i].val;
+ *in = pos;
+ CFLOG_TRACE(("tok_parse:"
+ " Match '%s' '%s' (%s)->%d\n",
+ cmd, tok, toktype, *val));
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/** Parses a command string from user.
+ * @param[in] cmd The command string to be parsed
+ * @param[out] action The type of action of this string
+ * @param[in] param The action parameters for this command
+ * @return 0 on success, < 0 on error.
+ */
+int caif_cmd_parse(char *cmd, int *action, union caif_action *param)
+{
+
+ int err = 0;
+ char *pos = cmd;
+ int val;
+ int len;
+ unsigned char *u;
+ int phy_specified = 0;
+ memset(param, 0, sizeof(*param));
+ if (cmd_parse(&pos, "HELP", &err))
+ return 0;
+
+ if (cmd_parse(&pos, "DELETE", &err)) {
+ struct caif_device_name *cf = ¶m->delete_channel;
+ *action = CAIF_IOC_REMOVE_DEVICE;
+
+ CFLOG_TRACE(("DELETE - pos='%s'\n", pos));
+
+ if (arg_parse(&pos, "NAME", cf->name, sizeof(cf->name), &err)) {
+ CFLOG_TRACE(("NAME - arg='%s' pos='%s'\n", cf->name,
+ pos));
+
+ } else {
+ CFLOG_TRACE(("Parse Error for DELETE: '%s'\n", pos));
+ return -1;
+ }
+
+ if (tok_parse(&pos, "DEVTYPE", "DEVTY", &val, &err)) {
+ cf->devtype = val;
+ CFLOG_TRACE(("DEVTYPE - arg='%d' pos='%s'\n", val,
+ pos));
+
+ } else {
+ CFLOG_TRACE(("DEVTYPE REQUIRED\n"));
+ return -1;
+ }
+
+
+ if (strlen(pos) > 0) {
+ CFLOG_TRACE(("Could not parse string '%s'\n", pos));
+ return -1;
+ }
+ } else if (cmd_parse(&pos, "CREATE", &err)) {
+ struct caif_channel_create_action *cf = ¶m->create_channel;
+ *action = CAIF_IOC_CONFIG_DEVICE;
+ CFLOG_TRACE(("CREATE - pos='%s'\n", pos));
+
+
+ if (tok_parse(&pos, "TYPE", "CHTY", &val, &err)) {
+ cf->config.type = val;
+ CFLOG_TRACE(("TYPE - arg='%d' pos='%s'\n", val, pos));
+
+ } else {
+ CFLOG_TRACE(("TYPE REQUIRED\n"));
+ return -1;
+ }
+
+ if (arg_parse
+ (&pos, "NAME", cf->name.name, sizeof(cf->name.name),
+ &err)) {
+ CFLOG_TRACE(("NAME - arg='%s' pos='%s'\n",
+ cf->name.name, pos));
+
+ } else {
+ CFLOG_TRACE(("NAME REQUIRED\n"));
+ return -1;
+ }
+
+ if (tok_parse(&pos, "DEVTYPE", "DEVTY", &val, &err)) {
+ cf->name.devtype = val;
+ CFLOG_TRACE(("DEVTYPE - arg='%d' pos='%s'\n", val,
+ pos));
+
+ } else {
+ CFLOG_TRACE(("DEVTYPE REQUIRED\n"));
+ return -1;
+ }
+
+
+ if (arg_parse(&pos, "PHYNAME", cf->config.phy_name,
+ sizeof(cf->config.phy_name), &err)) {
+ phy_specified = 1;
+ CFLOG_TRACE(("PHYNAME - arg='%s' pos='%s'\n",
+ cf->config.phy_name, pos));
+
+ }
+ if (tok_parse(&pos, "PHYPREF", "PHYPREF", &val, &err)) {
+ cf->config.phy_pref = val;
+ phy_specified = 1;
+ CFLOG_TRACE(("NAME - val='%d' pos='%s'\n", val, pos));
+
+ }
+
+ if (!phy_specified) {
+ CFLOG_TRACE(("PHYNAME or PHYPREF REQUIRED\n"));
+ return -1;
+ }
+
+ if (tok_parse(&pos, "PRIO", "PRIO", &val, &err)) {
+ cf->config.priority = val;
+ CFLOG_TRACE(("PRIO - val='%d' pos='%s'\n", val, pos));
+
+ }
+ if (cf->config.type == CAIF_CHTY_DATAGRAM
+ && int_parse(&pos, "CONNID", &val, &err)) {
+ cf->config.u.dgm.connection_id = val;
+ CFLOG_TRACE(("CONNID - val='%d' pos='%s'\n", val,
+ pos));
+
+ }
+ if (cf->config.type == CAIF_CHTY_RFM
+ && int_parse(&pos, "CONNID", &val, &err)) {
+ cf->config.u.rfm.connection_id = val;
+ CFLOG_TRACE(("CONNID - val=%d pos='%s'\n", val, pos));
+
+ }
+ if (cf->config.type == CAIF_CHTY_RFM
+ && arg_parse(&pos, "VOLUME", cf->config.u.rfm.volume,
+ sizeof(cf->config.u.rfm.volume), &err)) {
+ cf->config.u.rfm.connection_id = val;
+ CFLOG_TRACE(("VOLUME - arg='%s' pos='%s'\n",
+ cf->config.u.rfm.volume, pos));
+
+ }
+ if (cf->config.type == CAIF_CHTY_UTILITY
+ && arg_parse(&pos, "SOCK", cf->config.u.utility.name,
+ sizeof(cf->config.u.utility.name), &err)) {
+ cf->config.u.rfm.connection_id = val;
+ CFLOG_TRACE(("SOCK - arg='%s' pos='%s'\n",
+ cf->config.u.utility.name, pos));
+
+ }
+
+ if (cf->config.type == CAIF_CHTY_UTILITY
+ && hex_parse(&pos, "PARAM",
+ cf->config.u.utility.params,
+ sizeof(cf->config.u.utility.params), &len,
+ &err)) {
+ cf->config.u.utility.paramlen = len;
+ u = cf->config.u.utility.params;
+ CFLOG_TRACE(("PARAM %02x,%02x,%02x,%02x- pos='%s'\n",
+ u[0], u[1], u[2], u[3], pos));
+
+ }
+
+ if (strlen(pos) > 0) {
+ CFLOG_TRACE(("Could not parse string '%s'\n", pos));
+ return -1;
+ }
+
+ } else {
+ CFLOG_TRACE(("UNRECOGNIZED COMMAND '%s'\n", pos));
+ return -1;
+ }
+ return 0;
+
+}
diff --git a/Documentation/CAIF/chardevconfig/chardevconfig.c b/Documentation/CAIF/chardevconfig/chardevconfig.c
new file mode 100644
index 0000000..cb09b0d
--- /dev/null
+++ b/Documentation/CAIF/chardevconfig/chardevconfig.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2009
+ *
+ * Author: Per Sigmond / Per.Sigmond@stericsson.com
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ *
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <ctype.h>
+#include <caif_ioctl.h>
+
+
+#define BUFFERLENGTH 512
+
+
+int caif_cmd_parse(char *cmd, int *action, union caif_action *param);
+
+void usage(char *argv0)
+{
+ fprintf(stderr, "Usage: %s device configfile\n", argv0);
+ fprintf(stderr, "Usage: %s device -\n", argv0);
+}
+
+int main(int argc, char *argv[])
+{
+
+ int fd;
+ FILE *configfile;
+ char *config_devname;
+ union caif_action param;
+ int request;
+ int ret;
+ char *cmd;
+ int bytes_read;
+
+ if (argc < 3) {
+ usage(argv[0]);
+ exit(-1);
+ }
+ config_devname = argv[1];
+
+ if (!strncmp(argv[2], "-", 1)) {
+ /* stdin */
+ configfile = stdin;
+ } else {
+ configfile = fopen(argv[2], "r");
+ }
+ if (configfile == NULL) {
+ fprintf(stderr, "fopen %s: %s\n", argv[2], strerror(errno));
+ exit(-1);
+ }
+
+ cmd = malloc(BUFFERLENGTH);
+ if (cmd == NULL) {
+ fprintf(stderr, "malloc error: %s\n", strerror(errno));
+ exit(-1);
+ }
+
+ while (fgets(cmd, BUFFERLENGTH, configfile) != NULL) {
+
+ bytes_read = strlen(cmd);
+ while (isspace(cmd[bytes_read - 1])) {
+ cmd[bytes_read - 1] = 0;
+ bytes_read--;
+ }
+
+ ret = caif_cmd_parse(cmd, &request, ¶m);
+
+ if (ret != 0) {
+ fprintf(stderr, "'%s'\n", cmd);
+ fprintf(stderr, "Error parsing config string.\n");
+ continue;
+ }
+
+ fd = open(config_devname, O_RDWR);
+ if (fd < 0) {
+ fprintf(stderr, "open %s: %s\n", config_devname,
+ strerror(errno));
+ exit(-1);
+ }
+
+ if (ioctl(fd, request, ¶m) < 0) {
+ fprintf(stderr, "'%s'\n", cmd);
+ fprintf(stderr, "ioctl: %s\n", strerror(errno));
+ }
+ close(fd);
+
+ if (request == CAIF_IOC_CONFIG_DEVICE) {
+ printf
+ ("Create device: name = %s, "
+ "major = %d, minor = %d\n",
+ param.create_channel.name.name,
+ param.create_channel.major,
+ param.create_channel.minor);
+ }
+ }
+
+ fclose(configfile);
+ free(cmd);
+
+ return 0;
+}
diff --git a/Documentation/CAIF/chardevconfig/create_devices.config b/Documentation/CAIF/chardevconfig/create_devices.config
new file mode 100644
index 0000000..645ba8d
--- /dev/null
+++ b/Documentation/CAIF/chardevconfig/create_devices.config
@@ -0,0 +1,12 @@
+CREATE TYPE=AT NAME=chnlat10 DEVTYPE=CHAR PHYPREF=LAT PRIO=HI
+CREATE TYPE=AT NAME=chnlat11 DEVTYPE=CHAR PHYPREF=LAT PRIO=HI
+CREATE TYPE=AT NAME=chnlat12 DEVTYPE=CHAR PHYPREF=LAT PRIO=HI
+CREATE TYPE=AT NAME=chnlat13 DEVTYPE=CHAR PHYPREF=LAT PRIO=HI
+CREATE TYPE=RFM NAME=chn_rfm DEVTYPE=CHAR PHYPREF=BW CONNID=1 VOLUME=rfm
+CREATE TYPE=RFM NAME=chn_afs DEVTYPE=CHAR PHYPREF=BW CONNID=1 VOLUME=/afs
+CREATE TYPE=RFM NAME=chn_ifs DEVTYPE=CHAR PHYPREF=BW CONNID=1 VOLUME=/ifs
+CREATE TYPE=RFM NAME=chn_sys DEVTYPE=CHAR PHYPREF=BW CONNID=1 VOLUME=/sys"
+CREATE TYPE=UTIL NAME=chn_psocktest DEVTYPE=CHAR PHYPREF=LAT SOCK=CAIF_PSOCK_TEST PARAM=01
+CREATE TYPE=DGM NAME=chn_datagram DEVTYPE=CHAR PHYPREF=BW CONNID=1
+CREATE TYPE=DGMLOOP NAME=datagram_loop DEVTYPE=CHAR PHYPREF=BW
+CREATE TYPE=DGM NAME=datagram_raw_ip DEVTYPE=CHAR PHYPREF=BW CONNID=1
diff --git a/Documentation/CAIF/chardevconfig/delete_devices.config b/Documentation/CAIF/chardevconfig/delete_devices.config
new file mode 100644
index 0000000..220c40a
--- /dev/null
+++ b/Documentation/CAIF/chardevconfig/delete_devices.config
@@ -0,0 +1,12 @@
+DELETE NAME=chnlat10 DEVTYPE=CHAR
+DELETE NAME=chnlat11 DEVTYPE=CHAR
+DELETE NAME=chnlat12 DEVTYPE=CHAR
+DELETE NAME=chnlat13 DEVTYPE=CHAR
+DELETE NAME=chn_rfm DEVTYPE=CHAR
+DELETE NAME=chn_afs DEVTYPE=CHAR
+DELETE NAME=chn_ifs DEVTYPE=CHAR
+DELETE NAME=chn_sys DEVTYPE=CHAR
+DELETE NAME=chn_psocktest DEVTYPE=CHAR
+DELETE NAME=chn_datagram DEVTYPE=CHAR
+DELETE NAME=datagram_loop DEVTYPE=CHAR
+DELETE NAME=datagram_raw_ip DEVTYPE=CHAR
diff --git a/Documentation/CAIF/ldiscd/ldiscd.c b/Documentation/CAIF/ldiscd/ldiscd.c
new file mode 100644
index 0000000..9e3483d
--- /dev/null
+++ b/Documentation/CAIF/ldiscd/ldiscd.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2009
+ *
+ * Author: Daniel Martensson / Daniel.Martensson@stericsson.com
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ *
+ */
+
+
+
+
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <string.h>
+#include <termios.h>
+#include <asm/ioctls.h>
+
+#define CAIF_LDISC_TTY "/dev/ttyS0"
+
+int main(void)
+{
+
+ /* Our process ID and Session ID */
+ pid_t pid, sid;
+
+ /* Termios structure for UART configuration. */
+ struct termios tio;
+
+ /* File handle to the tty device node */
+ int fd;
+
+ /* Result */
+ int result;
+
+ /* Line discipline number to use (N_MOUSE) */
+ int ldiscnr = 2;
+
+ /* Fork off the parent process */
+ pid = fork();
+ if (pid < 0)
+ exit(EXIT_FAILURE);
+
+ /* If we got a good PID, then
+ we can exit the parent process. */
+ if (pid > 0)
+ exit(EXIT_SUCCESS);
+
+
+ /* Change the file mode mask */
+ umask(0);
+
+ /* Open any logs here */
+
+ /* Create a new SID for the child process */
+ sid = setsid();
+ if (sid < 0)
+ /* Log the failure */
+ exit(EXIT_FAILURE);
+
+
+ /* Change the current working directory */
+ if ((chdir("/")) < 0)
+ /* Log the failure */
+ exit(EXIT_FAILURE);
+
+
+ /* Close out the standard file descriptors */
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+
+ /* Daemon-specific initialization goes here */
+
+ /* Open the tty device node */
+ fd = open(CAIF_LDISC_TTY, O_RDWR);
+ if (fd < 0) {
+ /* Log the failure */
+ exit(EXIT_FAILURE);
+ }
+
+ /* Configure UART settings. */
+ memset(&tio, 0, sizeof(tio));
+
+ /* 115200 baud, 8n1, CTS/RTS flow control. */
+ tio.c_cflag = B115200 | CRTSCTS | CS8 | CLOCAL | CREAD;
+
+ /* Flush TTY and set new termios. */
+ result = tcflush(fd, TCIOFLUSH);
+ if (result)
+ /* Log the failure */
+ exit(EXIT_FAILURE);
+
+
+ result = tcsetattr(fd, TCSANOW, &tio);
+ if (result)
+ /* Log the failure */
+ exit(EXIT_FAILURE);
+
+
+ /* Change line discipline for the tty device and keep it open */
+ result = ioctl(fd, TIOCSETD, &ldiscnr);
+ if (result < 0)
+ /* Log the failure */
+ exit(EXIT_FAILURE);
+
+
+ /* The Big Loop */
+ while (1)
+ sleep(0x7FFFFFFF);
+
+
+ close(fd);
+
+ exit(EXIT_SUCCESS);
+}
--
1.6.0.4
^ permalink raw reply related
* [PATCH 6/8] [RFC] CAIF Protocol Stack
From: sjur.brandeland @ 2009-09-23 17:31 UTC (permalink / raw)
To: netdev; +Cc: Kim.xx.Lilliestierna, sjur.brandeland
From: Kim Lilliestierna <Kim.xx.Lilliestierna@ericsson.com>
Signed-off-by: sjur.brandeland@stericsson.com
---
drivers/net/caif/Kconfig | 64 +++
drivers/net/caif/Makefile | 29 ++
drivers/net/caif/chnl_tty.c | 220 +++++++++++
drivers/net/caif/phyif_loop.c | 309 +++++++++++++++
drivers/net/caif/phyif_ser.c | 189 +++++++++
drivers/net/caif/phyif_shm.c | 870 +++++++++++++++++++++++++++++++++++++++++
drivers/net/caif/shm.h | 95 +++++
drivers/net/caif/shm_cfgifc.c | 60 +++
drivers/net/caif/shm_mbxifc.c | 98 +++++
drivers/net/caif/shm_smbx.c | 81 ++++
10 files changed, 2015 insertions(+), 0 deletions(-)
create mode 100644 drivers/net/caif/Kconfig
create mode 100644 drivers/net/caif/Makefile
create mode 100644 drivers/net/caif/chnl_tty.c
create mode 100644 drivers/net/caif/phyif_loop.c
create mode 100644 drivers/net/caif/phyif_ser.c
create mode 100644 drivers/net/caif/phyif_shm.c
create mode 100644 drivers/net/caif/shm.h
create mode 100644 drivers/net/caif/shm_cfgifc.c
create mode 100644 drivers/net/caif/shm_mbxifc.c
create mode 100644 drivers/net/caif/shm_smbx.c
diff --git a/drivers/net/caif/Kconfig b/drivers/net/caif/Kconfig
new file mode 100644
index 0000000..3cbe302
--- /dev/null
+++ b/drivers/net/caif/Kconfig
@@ -0,0 +1,64 @@
+#
+# CAIF net configurations
+#
+
+if CAIF
+
+# Include physical drivers
+# should be broken out into its own config file
+# source "drivers/net/caif/Kconfig"
+
+# Some options here should be mande platform dependent
+
+comment "CAIF physical drivers"
+
+config CAIF_TTY
+ tristate "CAIF TTY transport driver "
+ default CAIF
+ ---help---
+ The CAIF TTY transport driver
+ If sou say yes here you will also need to build a users space utility to set the line disicpline on the the
+ tty, see Documentation/net/caif/examples/linedsc
+
+config CAIF_SHM
+ tristate "CAIF shared memory transport driver"
+ default n
+ ---help---
+ The caif low level driver for the shared memory driver
+ Be aware that if you enable this you need to also enable a low level shared memory driver
+ the default is to include the loopback test driver.
+
+config CAIF_LOOPBACK
+ tristate "CAIF loopback driver test driver"
+ default CAIF
+ ---help---
+ Loopback test driver
+
+if CAIF_SHM
+
+comment "CAIF shared memory low level physical drivers"
+
+config CAIF_SHM_LOOPBACK
+ tristate "Caif shared memory loopback driver"
+ default CAIF_USE_SHM
+ ---help---
+ loop back driver that emulates a real shared memory transport
+ mainly used for debugging.
+
+config CAIF_MBXIF
+ tristate "Caif shared mailbox interface"
+ default CAIF_SHM
+ ---help---
+ Generic shared mailbox interface
+
+config CAIF_SMBX
+ tristate "Use Simluated Mail box"
+ default CAIF_MBXIF
+ ---help---
+ Answer y if you whant to use a simulated mail box interface for caif shared memory transport
+ Mainly used for debugging and as example driver
+ This can also be built as a module
+
+endif #CAIF_USE_SHM
+
+endif # CAIF
diff --git a/drivers/net/caif/Makefile b/drivers/net/caif/Makefile
new file mode 100644
index 0000000..f983289
--- /dev/null
+++ b/drivers/net/caif/Makefile
@@ -0,0 +1,29 @@
+ifeq ($(CONFIG_CAIF_DEBUG),1)
+CAIF_FLAGS+=-DCAIF_DEBUG_ON
+endif
+
+
+ccflags-y := -DCAIF_KERNEL -DKERN_VERSION_2_6_27 $(CAIF_FLAGS) -Iinclude/net/caif -Iinclude/linux/caif -Iinclude/net/caif/generic
+
+
+clean-dirs:= .tmp_versions
+clean-files:= Module.symvers modules.order *.cmd *~ \
+
+
+# --- Physical drivers --
+# Serial interface
+obj-$(CONFIG_CAIF_TTY) += phyif_ser.o
+
+# Loop back
+obj-$(CONFIG_CAIF_LOOPBACK) += phyif_loop.o
+
+# Shared memmory
+obj-$(CONFIG_CAIF_SHM) += phyif_shm.o
+
+# Mail box geneirc
+obj-$(CONFIG_CAIF_MBXIF) += shm_mbxifc.o
+
+# Simulated mail box
+obj-$(CONFIG_CAIF_SMBX) += shm_smbx.o
+
+export-objs := caif_chr.o caif_test_chdev.o caif_kapi_test.o
diff --git a/drivers/net/caif/chnl_tty.c b/drivers/net/caif/chnl_tty.c
new file mode 100644
index 0000000..03fe0d3
--- /dev/null
+++ b/drivers/net/caif/chnl_tty.c
@@ -0,0 +1,220 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Daniel Martensson / Daniel.Martensson@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+
+
+
+
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#include "caif_layer.h"
+#include "cfcnfg.h"
+#include "cfpkt.h"
+
+#include "caif_chr.h"
+#include "caif_ioctl.h"
+
+MODULE_LICENSE("GPL");
+
+#define TTY_CHNL_DEVICES 1
+
+static bool tty_registered;
+
+struct chnl_tty {
+ layer_t chnl;
+ struct tty_driver *tty_drv;
+ struct tty_struct *tty;
+};
+
+static struct chnl_tty ttydev;
+
+static int chnl_recv_cb(layer_t *layr, cfpkt_t *pkt)
+{
+ unsigned char *flip = NULL;
+ caif_packet_funcs_t f;
+ int len;
+ size_t max;
+ int count;
+
+ /* Get caif packet functions. */
+ f = cfcnfg_get_packet_funcs();
+
+ len = cfpkt_getlen(pkt);
+
+ printk(KERN_INFO "AT received: %d bytes.\n", len);
+
+ /* Get a buffer from the TTY framework. */
+ count = tty_prepare_flip_string(ttydev.tty, &flip, len);
+
+ if (len > count) {
+ printk(KERN_INFO
+ "TTY buffer to small, dropping %d bytes.\n",
+ (len - count));
+ }
+
+ /* Check max length that can be copied. */
+ max = len > count ? count : len;
+
+ /* Extract packet to the TTY buffer. */
+ f.cfpkt_extract(pkt, flip, count, &max);
+
+ /* Liberate packet. */
+ f.cfpkt_destroy(pkt);
+
+ /* Push data to the ldisc. */
+ tty_schedule_flip(ttydev.tty);
+
+ return 0;
+}
+
+static void chnl_flowctrl_cb(layer_t *layr, caif_flowctrl_t on)
+{
+ printk("AT flowctrl func called flow: %s.\n",
+ on == CAIF_FLOWCTRL_ON ? "ON" : "OFF or INIT");
+
+ if (on == CAIF_FLOWCTRL_INIT && tty_registered == false) {
+ /* Register a tty device. */
+ struct device *dev =
+ tty_register_device(ttydev.tty_drv, 0, NULL);
+ if (IS_ERR(dev)) {
+ int result = PTR_ERR(dev);
+ printk(KERN_WARNING
+ "chnl: err: %d, can't register tty device.\n",
+ result);
+ goto err_tty_device_register_failed;
+ }
+ tty_registered = true;
+ }
+
+err_tty_device_register_failed:
+ return;
+}
+
+int chnl_tty_open(struct tty_struct *tty, struct file *filp)
+{
+ ttydev.tty = tty;
+
+ return 0;
+}
+
+void chnl_tty_close(struct tty_struct *tty, struct file *filp)
+{
+ ttydev.tty = NULL;
+}
+
+int chnl_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
+{
+ cfpkt_t *pkt = NULL;
+ caif_packet_funcs_t f;
+
+ /* Get caif packet functions. */
+ f = cfcnfg_get_packet_funcs();
+
+ /* Create a caif packet based on the tty buffer. */
+ pkt = f.cfpkt_create_xmit_pkt(buf, count);
+ if (!pkt) {
+ printk(KERN_INFO "chnl_tty_write: cfpkt_create failed.\n");
+ goto err_cfpkt_create;
+ }
+
+ /* Send the packet down the stack. */
+ ttydev.chnl.dn->transmit(ttydev.chnl.dn, NULL, pkt);
+
+ return count;
+
+err_cfpkt_create:
+ return 0;
+}
+
+struct tty_operations chnl_tty_ops = {
+ .open = chnl_tty_open,
+ .close = chnl_tty_close,
+ .write = chnl_tty_write,
+};
+
+void chnl_tty_exit_module(void)
+{
+ if (ttydev.tty) {
+ tty_unregister_device(ttydev.tty_drv, 0);
+ ttydev.tty = NULL;
+ }
+ tty_unregister_driver(ttydev.tty_drv);
+ put_tty_driver(ttydev.tty_drv);
+ ttydev.tty_drv = NULL;
+}
+
+int chnl_tty_init_module(void)
+{
+ struct caif_service_config config;
+ int result;
+
+ ttydev.tty_drv = alloc_tty_driver(TTY_CHNL_DEVICES);
+ if (ttydev.tty_drv == NULL) {
+ printk(KERN_WARNING
+ "chnl_tty_init_module: err: "
+ "can't allocate tty driver.\n");
+ result = -ENOMEM;
+ goto err_alloc_tty_driver_failed;
+ }
+
+ ttydev.tty_drv->driver_name = "caif_tty";
+ ttydev.tty_drv->name = "cftty";
+ ttydev.tty_drv->type = TTY_DRIVER_TYPE_SERIAL;
+ ttydev.tty_drv->subtype = SERIAL_TYPE_NORMAL;
+ ttydev.tty_drv->init_termios = tty_std_termios;
+ ttydev.tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+
+ /* Register the TTY driver. */
+ tty_set_operations(ttydev.tty_drv, &chnl_tty_ops);
+
+ result = tty_register_driver(ttydev.tty_drv);
+ if (result < 0) {
+ printk(KERN_WARNING
+ "chnl_tty_init_module: err: "
+ "%d, can't register tty driver.\n",
+ result);
+ goto err_tty_driver_register_failed;
+ }
+
+ /* Fill in channel information. */
+ ttydev.chnl.receive = chnl_recv_cb;
+ ttydev.chnl.flowctrl = chnl_flowctrl_cb;
+
+ memset(&config, 0, sizeof(config));
+ config.service = CAIF_SRVC_AT;
+ config.phy_type = CAIF_PHY_LOW_LAT;
+ config.priority = CAIF_PRIO_NORMAL;
+
+ /* Register this channel. */
+ result = caifdev_adapt_register(&config, &ttydev.chnl);
+ if (result < 0) {
+ printk(KERN_WARNING
+ "chnl_tty_init_module: err:"
+ " %d, can't register channel.\n",
+ result);
+ goto err_register_chnl;
+ }
+
+ return result;
+
+err_register_chnl:
+ tty_unregister_driver(ttydev.tty_drv);
+err_tty_driver_register_failed:
+ put_tty_driver(ttydev.tty_drv);
+ ttydev.tty_drv = NULL;
+err_alloc_tty_driver_failed:
+ return -ENODEV;
+}
+
+module_init(chnl_tty_init_module);
+module_exit(chnl_tty_exit_module);
diff --git a/drivers/net/caif/phyif_loop.c b/drivers/net/caif/phyif_loop.c
new file mode 100644
index 0000000..2bfa42b
--- /dev/null
+++ b/drivers/net/caif/phyif_loop.c
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2009
+ *
+ * Author: Daniel Martensson / Daniel.Martensson@stericsson.com
+ * Per Sigmond / Per.Sigmond@stericsson.com
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
+
+
+#include <linux/semaphore.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+/* Caif header files. */
+#include "caif_log.h"
+#include "caif_layer.h"
+#include "cfcnfg.h"
+#include "cfpkt.h"
+
+#include "caif_chr.h"
+
+#include <linux/delay.h>
+
+
+MODULE_LICENSE("GPL");
+
+
+
+static int reentrant;
+static int direct;
+static int serial;
+module_param(reentrant, bool, S_IRUGO);
+module_param(direct, bool, S_IRUGO);
+module_param(serial, bool, S_IRUGO);
+MODULE_PARM_DESC(reentrant,
+ "Reentrant or not (defualt is workqueue implementation)");
+MODULE_PARM_DESC(direct,
+ "Direct mode, looping packets directly back up the stack");
+
+static layer_t cf_phy;
+static layer_t loop_phy;
+static spinlock_t ring_buffer_lock;
+
+
+/* Start ring buffer */
+#define RING_MAX_BUFFERS 16384
+
+struct ring_buffer_element {
+ struct _cfpkt_t *cfpkt;
+};
+
+static struct {
+ struct ring_buffer_element ring_buffer[RING_MAX_BUFFERS];
+ int head_index;
+ int tail_index;
+} my_ring_buffer;
+
+#define ring_buffer_index_plus_one(index) \
+ ((index+1) < RING_MAX_BUFFERS ? (index + 1) : 0)
+
+#define ring_buffer_increment_tail(rb) \
+ ((rb)->tail_index = ring_buffer_index_plus_one((rb)->tail_index))
+
+#define ring_buffer_increment_head(rb) \
+ ((rb)->head_index = ring_buffer_index_plus_one((rb)->head_index))
+
+#define ring_buffer_empty(rb) ((rb)->head_index == (rb)->tail_index)
+#define ring_buffer_full(rb) (ring_buffer_index_plus_one((rb)->head_index)\
+ == (rb)->tail_index)
+#define ring_buffer_tail_element(rb) ((rb)->ring_buffer[(rb)->tail_index])
+#define ring_buffer_head_element(rb) ((rb)->ring_buffer[(rb)->head_index])
+#define ring_buffer_size(rb) (((rb)->head_index >= (rb)->tail_index)) ?\
+ ((rb)->head_index - (rb)->tail_index) : \
+ (RING_MAX_BUFFERS - ((rb)->tail_index - (rb)->head_index))
+/* End ring buffer */
+
+
+
+static void work_func(struct work_struct *work);
+static struct workqueue_struct *ploop_work_queue;
+static DECLARE_WORK(loop_work, work_func);
+static wait_queue_head_t buf_available;
+
+
+#define phyif_assert(assert) BUG_ON(!(assert))
+
+
+
+
+
+
+
+
+
+
+
+
+
+static void work_func(struct work_struct *work)
+{
+
+ CAIFLOG_ENTER("");
+
+ while (!ring_buffer_empty(&my_ring_buffer)) {
+ struct _cfpkt_t *cfpkt;
+
+ /* Get packet */
+ cfpkt = ring_buffer_tail_element(&my_ring_buffer).cfpkt;
+ ring_buffer_tail_element(&my_ring_buffer).cfpkt = NULL;
+
+ ring_buffer_increment_tail(&my_ring_buffer);
+
+ /* Wake up writer */
+ wake_up_interruptible(&buf_available);
+
+
+ /* Push received packet up the caif stack. */
+ cf_phy.up->receive(cf_phy.up, cfpkt);
+
+ }
+
+ /* Release access to loop queue. */
+ CAIFLOG_EXIT("");
+}
+
+static int cf_phy_modemcmd(layer_t *layr, caif_modemcmd_t ctrl)
+{
+ switch (ctrl) {
+ case _CAIF_MODEMCMD_PHYIF_USEFULL:
+ CAIFLOG_TRACE("phyif_loop:Usefull");
+
+ try_module_get(THIS_MODULE);
+ break;
+ case _CAIF_MODEMCMD_PHYIF_USELESS:
+ CAIFLOG_TRACE("phyif_loop:Useless");
+ module_put(THIS_MODULE);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int cf_phy_tx(layer_t *layr, transmt_info *info, cfpkt_t *pkt)
+{
+ int ret;
+ CAIFLOG_ENTER("");
+
+ /* Push received packet up the loop stack. */
+ ret = loop_phy.up->receive(loop_phy.up, pkt);
+
+ CAIFLOG_EXIT("");
+ return ret;
+}
+
+
+static int
+loop_phy_tx_reent(layer_t *layr, transmt_info *info, struct _cfpkt_t *cfpkt)
+{
+ CAIFLOG_ENTER("");
+
+ /* Push received packet up the caif stack. */
+ cf_phy.up->receive(cf_phy.up, cfpkt);
+
+ CAIFLOG_EXIT("");
+ return 0;
+}
+
+
+static int
+loop_phy_tx(layer_t *layr, transmt_info *info, struct _cfpkt_t *cfpkt)
+{
+
+ CAIFLOG_ENTER("");
+
+ /* Block writer as long as ring buffer is full */
+
+ spin_lock(&ring_buffer_lock);
+ /*phyif_assert( !ring_buffer_full(&my_ring_buffer) );*/
+
+ while (ring_buffer_full(&my_ring_buffer)) {
+ spin_unlock(&ring_buffer_lock);
+
+ if (wait_event_interruptible
+ (buf_available,
+ !ring_buffer_full(&my_ring_buffer)) == -ERESTARTSYS) {
+ printk(KERN_WARNING
+ "loop_phy_tx: "
+ "wait_event_interruptible woken by a signal\n");
+ return -ERESTARTSYS;
+ }
+
+
+ spin_lock(&ring_buffer_lock);
+ }
+
+
+ ring_buffer_head_element(&my_ring_buffer).cfpkt = cfpkt;
+ ring_buffer_increment_head(&my_ring_buffer);
+ spin_unlock(&ring_buffer_lock);
+
+ /* Add this work to the queue as we don't want to
+ * loop in the same context.
+ */
+ (void) queue_work(ploop_work_queue, &loop_work);
+
+ CAIFLOG_EXIT("");
+ return 0;
+}
+
+static int cf_phy_tx_direct(layer_t *layr, transmt_info *info, cfpkt_t *pkt)
+{
+ int ret;
+
+ CAIFLOG_ENTER("");
+ CAIFLOG_TRACE("[%s] up:%p pkt:%p\n", __func__, cf_phy.up,
+ pkt);
+ /* Push received packet back up the caif stack,
+ * via loop_phy_tx's work-queue */
+ ret = loop_phy_tx(layr, info, pkt);
+ CAIFLOG_EXIT("");
+ return ret;
+}
+
+
+static int __init phyif_loop_init(void)
+{
+ int result;
+
+ CAIFLOG_ENTER("");
+ printk("\nCompiled:%s:%s\nreentrant=%s direct=%s\n",
+ __DATE__, __TIME__,
+ (reentrant ? "yes" : "no"),
+ (direct ? "yes" : "no"));
+ /* Fill in some information about our PHYs. */
+ if (direct) {
+ cf_phy.transmit = cf_phy_tx_direct;
+ cf_phy.receive = NULL;
+ } else {
+ cf_phy.transmit = cf_phy_tx;
+ cf_phy.receive = NULL;
+ }
+ if (reentrant)
+ loop_phy.transmit = loop_phy_tx_reent;
+ else
+ loop_phy.transmit = loop_phy_tx;
+
+ loop_phy.receive = NULL;
+ cf_phy.modemcmd = cf_phy_modemcmd;
+
+ /* Create work thread. */
+ ploop_work_queue = create_singlethread_workqueue("phyif_loop");
+
+ init_waitqueue_head(&buf_available);
+
+ /* Initialize ring buffer */
+ memset(&my_ring_buffer, 0, sizeof(my_ring_buffer));
+ spin_lock_init(&ring_buffer_lock);
+ cf_phy.id = -1;
+ if (serial)
+ result =
+ caifdev_phy_register(&cf_phy, CFPHYTYPE_SERIAL,
+ CFPHYPREF_UNSPECIFIED);
+ else
+ result =
+ caifdev_phy_register(&cf_phy, CFPHYTYPE_MSL,
+ CFPHYPREF_UNSPECIFIED);
+
+ printk(KERN_WARNING "phyif_loop: ID = %d\n", cf_phy.id);
+
+ if (result < 0) {
+ printk(KERN_WARNING
+ "phyif_loop: err: %d, can't register phy.\n", result);
+ }
+
+ if (serial)
+ result =
+ caifdev_phy_loop_register(&loop_phy, CFPHYTYPE_SERIAL);
+ else
+ result = caifdev_phy_loop_register(&loop_phy, CFPHYTYPE_MSL);
+
+ if (result < 0) {
+ printk(KERN_WARNING
+ "phyif_loop: err: %d, can't register loop phy.\n",
+ result);
+ }
+
+ printk(KERN_WARNING "phyif_loop: ID = %d\n", cf_phy.id);
+ CAIFLOG_EXIT("");
+ return result;
+}
+
+static void phyif_loop_exit(void)
+{
+ printk(KERN_WARNING "phyif_loop: ID = %d\n", cf_phy.id);
+
+ caifdev_phy_unregister(&cf_phy);
+ cf_phy.id = -1;
+}
+
+module_init(phyif_loop_init);
+module_exit(phyif_loop_exit);
diff --git a/drivers/net/caif/phyif_ser.c b/drivers/net/caif/phyif_ser.c
new file mode 100644
index 0000000..5dc3da6
--- /dev/null
+++ b/drivers/net/caif/phyif_ser.c
@@ -0,0 +1,189 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Daniel Martensson / Daniel.Martensson@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+
+
+
+
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/tty.h>
+
+#include "caif_layer.h"
+#include "cfcnfg.h"
+#include "cfpkt.h"
+#include "caif_chr.h"
+
+
+MODULE_LICENSE("GPL");
+
+module_param(serial_use_stx, bool, S_IRUGO);
+MODULE_PARM_DESC(serial_use_stx, "STX enabled or not.");
+
+#define WRITE_BUF_SIZE 256
+#define READ_BUF_SIZE 256
+
+unsigned char sbuf_wr[WRITE_BUF_SIZE];
+
+layer_t ser_phy;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+static struct tty_ldisc_ops phyif_ldisc;
+#else
+static struct tty_ldisc phyif_ldisc;
+#endif /* KERN_VERSION_2_6_27 */
+
+struct tty_struct *pser_tty;
+
+
+static bool tx_started;
+
+static int ser_open(struct tty_struct *tty)
+{
+ int result;
+
+ pser_tty = tty;
+
+ /* Configure the attached TTY. */
+
+ /* Register physical interface. */
+ result =
+ caifdev_phy_register(&ser_phy, CFPHYTYPE_SERIAL,
+ CFPHYPREF_LOW_LAT);
+ if (result < 0) {
+ printk(KERN_WARNING
+ "phyif_ser: err: %d, can't register phy.\n", result);
+ }
+
+ return result;
+}
+
+static void ser_receive(struct tty_struct *tty, const u8 *data,
+ char *flags, int count)
+{
+ cfpkt_t *pkt = NULL;
+ caif_packet_funcs_t f;
+ /*int i; */
+
+ /* Get caif packet functions. */
+ f = cfcnfg_get_packet_funcs();
+
+
+ /* Workaround for garbage at start of transmission,
+ * only enable if STX handling is not enables */
+ if (!serial_use_stx && !tx_started) {
+ printk(KERN_WARNING
+ "Bytes received before first transmission."
+ " Bytes discarded. \n");
+ return;
+ }
+
+ /* Get a suitable caif packet and copy in data. */
+ pkt = f.cfpkt_create_recv_pkt(data, count);
+
+ /* Push received packet up the stack. */
+ ser_phy.up->receive(ser_phy.up, pkt);
+}
+
+
+int ser_phy_tx(layer_t *layr, transmt_info *info, struct _cfpkt_t *cfpkt)
+{
+ size_t tty_wr, actual_len;
+ bool cont;
+ caif_packet_funcs_t f;
+ /*int i; */
+
+ if (!pser_tty)
+ return CFGLU_ENOTCONN;
+
+ /* Get caif packet functions. */
+ f = cfcnfg_get_packet_funcs();
+
+ /* NOTE: This workaround is not really needed when STX is enabled.
+ * Remove? */
+ if (tx_started == false)
+ tx_started = true;
+
+
+ do {
+ char *bufp;
+ /* By default we assume that we will extract
+ * all data in one go. */
+ cont = false;
+
+ /* Extract data from the packet. */
+ f.cfpkt_extract(cfpkt, sbuf_wr, WRITE_BUF_SIZE, &actual_len);
+
+ /* Check if we need to extract more data. */
+ if (actual_len == WRITE_BUF_SIZE)
+ cont = true;
+
+ bufp = sbuf_wr;
+ /* Write the data on the tty driver.
+ * NOTE: This loop will be spinning until UART is ready for
+ * sending data.
+ * It might be looping forever if we get UART problems.
+ * This part should be re-written!
+ */
+ do {
+ tty_wr =
+ pser_tty->ops->write(pser_tty, bufp, actual_len);
+ /* When not whole buffer is written,
+ * forward buffer pointer and try again */
+ actual_len -= tty_wr;
+ bufp += tty_wr;
+ } while (actual_len);
+ } while (cont == true);
+
+ /* The packet is sent. As we have come to the end of the
+ * line we need to free the packet. */
+ f.cfpkt_destroy(cfpkt);
+
+ return 0;
+}
+
+static int __init phyif_ser_init(void)
+{
+ int result;
+
+ /* Fill in some information about our PHY. */
+ ser_phy.transmit = ser_phy_tx;
+ ser_phy.receive = NULL;
+ ser_phy.ctrlcmd = NULL;
+ ser_phy.modemcmd = NULL;
+
+ memset(&phyif_ldisc, 0, sizeof(phyif_ldisc));
+ phyif_ldisc.magic = TTY_LDISC_MAGIC;
+ phyif_ldisc.name = "n_phyif";
+ phyif_ldisc.open = ser_open;
+ phyif_ldisc.receive_buf = ser_receive;
+ phyif_ldisc.owner = THIS_MODULE;
+
+ result = tty_register_ldisc(N_MOUSE, &phyif_ldisc);
+
+ if (result < 0) {
+ printk(KERN_WARNING
+ "phyif_ser: err: %d, can't register ldisc.\n", result);
+ return result;
+ }
+
+ return result;
+}
+
+static void phyif_ser_exit(void)
+{
+ (void) tty_unregister_ldisc(N_MOUSE);
+}
+
+module_init(phyif_ser_init);
+module_exit(phyif_ser_exit);
+
+MODULE_ALIAS_LDISC(N_MOUSE);
diff --git a/drivers/net/caif/phyif_shm.c b/drivers/net/caif/phyif_shm.c
new file mode 100644
index 0000000..c192d11
--- /dev/null
+++ b/drivers/net/caif/phyif_shm.c
@@ -0,0 +1,870 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Daniel Martensson / Daniel.Martensson@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+#include <linux/semaphore.h>
+#else
+#include <linux/semaphore.h>
+#endif /* KERN_VERSION_2_6_27 */
+#include <linux/list.h>
+#include <linux/workqueue.h>
+
+#ifdef PHYIF_SHM_USE_DMA
+#include <linux/dma-mapping.h>
+#endif /* PHYIF_SHM_USE_DMA */
+#include <linux/io.h>
+
+/* Caif header files. */
+#include "caif_layer.h"
+#include "cfcnfg.h"
+#include "cfpkt.h"
+
+/* Caif linux header files. */
+#include "caif_chr.h"
+#include "shm.h"
+
+MODULE_LICENSE("GPL");
+
+#define SHM_INSTANCES 1
+
+static char *mbxifc_name = "cfmbx";
+module_param(mbxifc_name, charp, S_IRUGO);
+MODULE_PARM_DESC(mbxifc_name,
+ "Name of the shared memory mailbox interface.");
+
+static char *mbxcfg_name = "cfcfg";
+module_param(mbxcfg_name, charp, S_IRUGO);
+MODULE_PARM_DESC(mbxcfg_name,
+ "Name of the shared memory configuration interface.");
+
+#define SHM_CMD_DUMMY 0x00
+
+#define SHM_CMD_MASK (0x3F << 10)
+#define SHM_FULL_MASK (0x0F << 0)
+#define SHM_EMPTY_MASK (0x0F << 4)
+
+#define SHM_SET_CMD(x) ((x & 0x3F) << 10)
+#define SHM_GET_CMD(x) ((x >> 10) & 0x3F)
+
+#define SHM_SET_FULL(x) (((x+1) & 0x0F) << 0)
+#define SHM_GET_FULL(x) (((x >> 0) & 0x0F) - 1)
+
+#define SHM_SET_EMPTY(x) (((x+1) & 0x0F) << 4)
+#define SHM_GET_EMPTY(x) (((x >> 4) & 0x0F) - 1)
+
+typedef struct {
+ /* Offset from start of shared memory area to start of
+ * shared memory CAIF frame. */
+ uint32 frm_ofs;
+ /* Length of CAIF frame. */
+ uint32 frm_len;
+} shm_pck_desc_t, *pshm_pck_desc_t;
+
+typedef struct {
+ /* Number of bytes of padding before the CAIF frame. */
+ uint8 hdr_ofs;
+} shm_caif_frm_t, *pshm_caif_frm_t;
+
+/* Maximum number of CAIF buffers per shared memory buffer. */
+#define SHM_MAX_CAIF_FRMS_PER_BUF 10
+
+/* Size in bytes of the descriptor area
+ * (With end of descriptor signalling). */
+#define SHM_CAIF_DESC_SIZE ((SHM_MAX_CAIF_FRMS_PER_BUF + 1) * \
+ sizeof(shm_pck_desc_t))
+
+/* Offset to the first CAIF frame within a shared memory buffer.
+ * Aligned on 32 bytes. */
+#define SHM_CAIF_FRM_OFS (SHM_CAIF_DESC_SIZE + (SHM_CAIF_DESC_SIZE % 32))
+/* Number of bytes for CAIF shared memory header. */
+#define SHM_HDR_LEN 1
+/* Number of bytes for alignment of the CAIF service layer payload. */
+#define SHM_PAYLOAD_ALIGN_LEN 4
+/* Number of padding bytes for the complete CAIF frame. */
+#define SHM_FRM_PAD_LEN 4
+
+typedef struct _shm_layer_t {
+ layer_t shm_phy;
+ shm_cfgifc_t *cfg_ifc;
+ shm_mbxifc_t *mbx_ifc;
+ char cfg_name[16];
+ shm_mbxclient_t mbx_client;
+ char mbx_name[16];
+#ifdef PHYIF_SHM_USE_DMA
+ struct dmaifc_t *dma_ifc;
+#endif /* PHYIF_SHM_USE_DMA */
+ /* Lists for shared memory buffer synchronization and handling. */
+ struct list_head tx_empty_list;
+ struct list_head rx_empty_list;
+ struct list_head tx_pend_list;
+ struct list_head rx_pend_list;
+ struct list_head tx_full_list;
+ struct list_head rx_full_list;
+ struct work_struct sig_work;
+ struct work_struct rx_work;
+ struct work_struct flow_work;
+ struct workqueue_struct *sig_work_queue;
+ struct workqueue_struct *rx_work_queue;
+ struct workqueue_struct *flow_work_queue;
+
+ /* wait queue for sender to check for space to write in mailbox */
+ wait_queue_head_t mbx_space_wq;
+ int tx_empty_available;
+
+} shm_layer_t;
+
+static shm_layer_t shm_layer[SHM_INSTANCES];
+
+/* Shared memory buffer structure. */
+typedef struct {
+ int index;
+ int len;
+ int frames;
+ unsigned char *desc_ptr;
+ int frm_ofs;
+ int phy_addr;
+#ifdef PHYIF_SHM_USE_DMA
+ dma_addr_t *dma_ptr;
+#endif /* PHYIF_SHM_USE_DMA */
+ struct list_head list;
+} shm_buf_t;
+
+static DEFINE_SPINLOCK(lock);
+
+static void phyif_shm_sig_work_func(struct work_struct *work);
+static void phyif_shm_rx_work_func(struct work_struct *work);
+static void phyif_shm_flow_work_func(struct work_struct *work);
+
+static void phyif_shm_sig_work_func(struct work_struct *work)
+{
+ /* TODO: We assume that this call is not reentrant as
+ * that might change the order of the buffers which
+ * is not allowed. Option is to lock the whole function. */
+
+ int ret;
+ uint16 mbox_msg;
+ shm_layer_t *pshm = container_of(work, shm_layer_t, sig_work);
+
+ do {
+ shm_buf_t *pbuf;
+ unsigned long flags;
+
+ /* Initialize mailbox message. */
+ mbox_msg = 0x00;
+
+ spin_lock(&lock);
+
+ /* Check for pending transmit buffers. */
+ if (!list_empty(&pshm->tx_pend_list)) {
+ pbuf =
+ list_entry(pshm->tx_pend_list.next,
+ shm_buf_t, list);
+ list_del_init(&pbuf->list);
+
+ /* Release mutex. */
+ spin_unlock(&lock);
+
+ /* Grab spin lock. */
+ spin_lock_irqsave(&lock, flags);
+
+ list_add_tail(&pbuf->list, &pshm->tx_full_list);
+
+ /* Release spin lock. */
+ spin_unlock_irqrestore(&lock, flags);
+
+ /* Value index is never changed,
+ * read access should be safe. */
+ mbox_msg |= SHM_SET_FULL(pbuf->index);
+
+ spin_lock(&lock);
+ }
+
+ /* Check for pending receive buffers. */
+ if (!list_empty(&pshm->rx_pend_list)) {
+
+ pbuf = list_entry(pshm->rx_pend_list.next,
+ shm_buf_t, list);
+ list_del_init(&pbuf->list);
+
+ /* Release mutex. */
+ spin_unlock(&lock);
+
+ /* Grab spin lock. */
+ spin_lock_irqsave(&lock, flags);
+
+ list_add_tail(&pbuf->list, &pshm->rx_empty_list);
+
+ /* Release spin lock. */
+ spin_unlock_irqrestore(&lock, flags);
+
+ /* Value index is never changed,
+ * read access should be safe. */
+ mbox_msg |= SHM_SET_EMPTY(pbuf->index);
+
+ spin_lock(&lock);
+ }
+
+ /* Release mutex. */
+ spin_unlock(&lock);
+
+ if (mbox_msg) {
+ do {
+ long timeout = 3;
+ ret = pshm->mbx_ifc->send_msg(mbox_msg,
+ pshm->mbx_ifc->priv);
+
+ if (ret) {
+ interruptible_sleep_on_timeout(
+ &pshm->mbx_space_wq, timeout);
+ }
+
+ } while (ret);
+ }
+
+ } while (mbox_msg);
+}
+
+static void phyif_shm_rx_work_func(struct work_struct *work)
+{
+ shm_buf_t *pbuf;
+ caif_packet_funcs_t f;
+ struct _cfpkt_t *pkt;
+ unsigned long flags;
+ shm_layer_t *pshm;
+ /* TODO: We assume that this call is not reentrant as that might
+ * change the order of the buffers which is not possible.
+ * Option is to lock the whole function. */
+
+ pshm = container_of(work, shm_layer_t, rx_work);
+
+ /* Get caif packet functions. */
+ f = cfcnfg_get_packet_funcs();
+
+ do {
+ pshm_pck_desc_t pck_desc;
+
+ /* Grab spin lock. */
+ spin_lock_irqsave(&lock, flags);
+
+ /* Check for received buffers. */
+ if (list_empty(&pshm->rx_full_list)) {
+ /* Release spin lock. */
+ spin_unlock_irqrestore(&lock, flags);
+ break;
+ }
+ pbuf =
+ list_entry(pshm->rx_full_list.next, shm_buf_t, list);
+ list_del_init(&pbuf->list);
+
+ /* Release spin lock. */
+ spin_unlock_irqrestore(&lock, flags);
+
+ /* Retrieve pointer to start of the packet descriptor area. */
+ pck_desc = (pshm_pck_desc_t) pbuf->desc_ptr;
+
+ /* Check if descriptor contains a CAIF shared memory frame. */
+ while (pck_desc->frm_ofs) {
+ unsigned int frm_buf_ofs;
+ unsigned int frm_pck_ofs;
+ unsigned int frm_pck_len;
+
+ /* Check if offset is within buffer limits (lower). */
+ if (pck_desc->frm_ofs <
+ (pbuf->phy_addr - shm_base_addr)) {
+ printk(KERN_WARNING
+ "phyif_shm_rx_work_func:"
+ " Frame offset too small: %d\n",
+ pck_desc->frm_ofs);
+ break;
+ }
+
+ /* Check if offset is within buffer limits (higher). */
+ if (pck_desc->frm_ofs >
+ ((pbuf->phy_addr - shm_base_addr) +
+ pbuf->len)) {
+ printk(KERN_WARNING
+ "phyif_shm_rx_work_func:"
+ " Frame offset too big: %d\n",
+ pck_desc->frm_ofs);
+ break;
+ }
+
+ /* Calculate offset from start of buffer. */
+ frm_buf_ofs =
+ pck_desc->frm_ofs - (pbuf->phy_addr -
+ shm_base_addr);
+
+ /* Calculate offset and length of CAIF packet while
+ * taking care of the shared memory header. */
+ frm_pck_ofs =
+ frm_buf_ofs + SHM_HDR_LEN +
+ (*(pbuf->desc_ptr + frm_buf_ofs));
+ frm_pck_len =
+ (pck_desc->frm_len - SHM_HDR_LEN -
+ (*(pbuf->desc_ptr + frm_buf_ofs)));
+
+ /* Check if CAIF packet is within buffer limits. */
+ if ((frm_pck_ofs + pck_desc->frm_len) > pbuf->len) {
+ printk(KERN_WARNING
+ "phyif_shm_rx_work_func: "
+ "caif packet too big: offset:"
+ "%d, len: %d\n",
+ frm_pck_ofs, pck_desc->frm_len);
+ break;
+ }
+
+ /* Get a suitable caif packet and copy in data. */
+ pkt =
+ f.cfpkt_create_recv_pkt((pbuf->desc_ptr +
+ frm_pck_ofs),
+ frm_pck_len);
+
+ /* Push received packet up the stack. */
+ pshm->shm_phy.up->receive(pshm->shm_phy.up, pkt);
+
+ /* Move to next packet descriptor. */
+ pck_desc++;
+ };
+
+ spin_lock(&lock);
+ list_add_tail(&pbuf->list, &pshm->rx_pend_list);
+ spin_unlock(&lock);
+
+ } while (1);
+
+ /* Schedule signaling work queue. */
+ (void) queue_work(pshm->sig_work_queue, &pshm->sig_work);
+}
+
+static void phyif_shm_flow_work_func(struct work_struct *work)
+{
+ shm_layer_t *pshm;
+
+ pshm = container_of(work, shm_layer_t, flow_work);
+
+ if (!pshm->shm_phy.up->ctrlcmd) {
+ printk(KERN_WARNING "phyif_shm_flow_work_func: No flow up.\n");
+ return;
+ }
+
+ /* Re-enable the flow.*/
+ pshm->shm_phy.up->ctrlcmd(pshm->shm_phy.up,
+ _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND, 0);
+}
+
+static int phyif_shm_mbx_msg_cb(u16 mbx_msg, void *priv)
+{
+ shm_layer_t *pshm;
+ shm_buf_t *pbuf;
+ unsigned long flags;
+
+ pshm = (shm_layer_t *) priv;
+ /* TODO: Do we need the spin locks since this is assumed to be
+ * called from an IRQ context. */
+
+ /* We are also assuming that this call is not be reentrant as
+ * that might change the order of the buffers which is not
+ * possible. Option is to lock the whole function. */
+
+ /* Check for received buffers. */
+ if (mbx_msg & SHM_FULL_MASK) {
+ int idx;
+
+ /* Grab spin lock. */
+ spin_lock_irqsave(&lock, flags);
+
+ /* Check if we have any outstanding buffers. */
+ if (list_empty(&pshm->rx_empty_list)) {
+ /* Release spin lock. */
+ spin_unlock_irqrestore(&lock, flags);
+
+ /* We print even in IRQ context... */
+ printk(KERN_WARNING
+ "phyif_shm_mbx_msg_cb:"
+ " No empty Rx buffers to fill: msg:%x \n",
+ mbx_msg);
+
+ /* Bail out. */
+ goto err_rx_sync;
+ }
+
+ pbuf =
+ list_entry(pshm->rx_empty_list.next, shm_buf_t, list);
+ idx = pbuf->index;
+
+ /* Check buffer synchronization. */
+ if (idx != SHM_GET_FULL(mbx_msg)) {
+ /* Release spin lock. */
+ spin_unlock_irqrestore(&lock, flags);
+
+ /* We print even in IRQ context... */
+ printk(KERN_WARNING
+ "phyif_shm_mbx_msg_cb: RX full out of sync:"
+ " idx:%d, msg:%x \n",
+ idx, mbx_msg);
+
+ /* Bail out. */
+ goto err_rx_sync;
+ }
+
+ list_del_init(&pbuf->list);
+ list_add_tail(&pbuf->list, &pshm->rx_full_list);
+
+ /* Release spin lock. */
+ spin_unlock_irqrestore(&lock, flags);
+
+ /* Schedule RX work queue. */
+ (void) queue_work(pshm->rx_work_queue, &pshm->rx_work);
+ }
+
+err_rx_sync:
+
+ /* Check for emptied buffers. */
+ if (mbx_msg & SHM_EMPTY_MASK) {
+ int idx;
+
+ /* Grab spin lock. */
+ spin_lock_irqsave(&lock, flags);
+
+ /* Check if we have any outstanding buffers. */
+ if (list_empty(&pshm->tx_full_list)) {
+ /* Release spin lock. */
+ spin_unlock_irqrestore(&lock, flags);
+
+ /* We print even in IRQ context... */
+ printk(KERN_WARNING
+ "phyif_shm_mbx_msg_cb:"
+ " No TX to empty: msg:%x \n",
+ mbx_msg);
+
+ /* Bail out. */
+ goto err_tx_sync;
+ }
+
+ pbuf =
+ list_entry(pshm->tx_full_list.next, shm_buf_t, list);
+ idx = pbuf->index;
+
+ /* Check buffer synchronization. */
+ if (idx != SHM_GET_EMPTY(mbx_msg)) {
+ /* Release spin lock. */
+ spin_unlock_irqrestore(&lock, flags);
+
+ /* We print even in IRQ context... */
+ printk(KERN_WARNING
+ "phyif_shm_mbx_msg_cb: TX empty out of sync:"
+ " idx:%d, msg:%x \n",
+ idx, mbx_msg);
+
+ /* Bail out. */
+ goto err_tx_sync;
+ }
+
+ list_del_init(&pbuf->list);
+
+ /* Reset buffer parameters. */
+ pbuf->frames = 0;
+ pbuf->frm_ofs = SHM_CAIF_FRM_OFS;
+
+ list_add_tail(&pbuf->list, &pshm->tx_empty_list);
+
+ /* Check if we have to wake up transmitter. */
+ if (!pshm->tx_empty_available) {
+ pshm->tx_empty_available = 1;
+
+ /* Release spin lock. */
+ spin_unlock_irqrestore(&lock, flags);
+
+ /* Schedule flow re-enable work queue. */
+ (void) queue_work(pshm->flow_work_queue,
+ &pshm->flow_work);
+
+ } else {
+ /* Release spin lock. */
+ spin_unlock_irqrestore(&lock, flags);
+ }
+ }
+
+err_tx_sync:
+
+ /* Check for command buffers.
+ if (mbx_msg & SHM_CMD_MASK) {
+ }
+ */
+
+ return ESUCCESS;
+}
+
+void shm_phy_fctrl(layer_t *layr, caif_ctrlcmd_t on, int phyid)
+{
+ /* We have yet not added flow control. */
+}
+
+static int shm_send_pkt(shm_layer_t *pshm, cfpkt_t *cfpkt, bool append)
+{
+ unsigned long flags;
+ shm_buf_t *pbuf;
+ unsigned int extlen;
+ unsigned int frmlen = 0;
+ pshm_pck_desc_t pck_desc;
+ pshm_caif_frm_t frm;
+ caif_packet_funcs_t f;
+
+ /* Get caif packet functions. */
+ f = cfcnfg_get_packet_funcs();
+
+ /* Grab spin lock. */
+ spin_lock_irqsave(&lock, flags);
+
+ if (append) {
+ if (list_empty(&pshm->tx_pend_list)) {
+ /* Release spin lock. */
+ spin_unlock_irqrestore(&lock, flags);
+ return CFGLU_ERETRY;
+ }
+
+ /* Get the last pending buffer. */
+ pbuf = list_entry(pshm->tx_pend_list.prev, shm_buf_t, list);
+
+ /* Check that we don't exceed the descriptor area. */
+ if (pbuf->frames >= SHM_MAX_CAIF_FRMS_PER_BUF) {
+ /* Release spin lock. */
+ spin_unlock_irqrestore(&lock, flags);
+ return CFGLU_ERETRY;
+ }
+ } else {
+ if (list_empty(&pshm->tx_empty_list)) {
+ /* Update blocking condition. */
+ pshm->tx_empty_available = 0;
+
+ /* Release spin lock. */
+ spin_unlock_irqrestore(&lock, flags);
+
+ if (!pshm->shm_phy.up->ctrlcmd) {
+ printk(KERN_WARNING "shm_phy_tx: No flow up.\n");
+ return CFGLU_ERETRY;
+ }
+
+ pshm->shm_phy.up->ctrlcmd(pshm->shm_phy.up,
+ _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND, 0);
+
+ return CFGLU_ERETRY;
+ }
+
+ /* Get the first free buffer. */
+ pbuf = list_entry(pshm->tx_empty_list.next, shm_buf_t, list);
+ }
+
+ list_del_init(&pbuf->list);
+
+ /* Release spin lock. */
+ spin_unlock_irqrestore(&lock, flags);
+
+ /* TODO: The CAIF stack will have to give a hint on what is
+ * payload in order to align it. */
+ frm = (pshm_caif_frm_t) (pbuf->desc_ptr + pbuf->frm_ofs);
+ frm->hdr_ofs = 0;
+ frmlen += SHM_HDR_LEN + frm->hdr_ofs;
+
+ /* Add length of CAIF frame. */
+ frmlen += f.cfpkt_getlen(cfpkt);
+
+ /* Add tail padding if needed. */
+ if (frmlen % SHM_FRM_PAD_LEN)
+ frmlen += SHM_FRM_PAD_LEN - (frmlen % SHM_FRM_PAD_LEN);
+
+
+ /* Verify that packet, header and additional padding can fit
+ * within the buffer frame area. */
+ if (frmlen >= (pbuf->len - pbuf->frm_ofs)) {
+ if (append) {
+ /* Put back packet as end of pending queue. */
+ list_add_tail(&pbuf->list, &pshm->tx_pend_list);
+ return CFGLU_ENOSPC;
+ } else {
+ /* Put back packet as start of empty queue. */
+ list_add(&pbuf->list, &pshm->tx_empty_list);
+ return CFGLU_ENOSPC;
+ }
+ }
+
+ /* Extract data from the packet to the shared memory CAIF frame
+ * taking into account the shared memory header byte and possible
+ * payload alignment bytes. */
+
+ f.cfpkt_extract(cfpkt,
+ (pbuf->desc_ptr + pbuf->frm_ofs + SHM_HDR_LEN +
+ frm->hdr_ofs), pbuf->len, &extlen);
+
+ /* Fill in the shared memory packet descriptor area. */
+ pck_desc = (pshm_pck_desc_t) (pbuf->desc_ptr);
+ /* Forward to current frame. */
+ pck_desc += pbuf->frames;
+ pck_desc->frm_ofs =
+ (pbuf->phy_addr - shm_base_addr) + pbuf->frm_ofs;
+ pck_desc->frm_len = frmlen;
+
+ /* Terminate packet descriptor area. */
+ pck_desc++;
+ pck_desc->frm_ofs = 0;
+
+ /* Update buffer parameters. */
+ pbuf->frames++;
+ pbuf->frm_ofs += frmlen + (frmlen % 32);
+
+ spin_lock(&lock);
+ /* Assign buffer as pending. */
+ list_add_tail(&pbuf->list, &pshm->tx_pend_list);
+ spin_unlock(&lock);
+
+ /* Schedule signaling work queue. */
+ (void) queue_work(pshm->sig_work_queue, &pshm->sig_work);
+
+ /* The packet is sent. As we have come to the end of the line we need
+ * to free the packet. */
+ f.cfpkt_destroy(cfpkt);
+
+ return ESUCCESS;
+}
+
+int shm_phy_tx(layer_t *layr, transmt_info *info, cfpkt_t *cfpkt)
+{
+ shm_layer_t *pshm;
+ int result;
+
+ /* TODO: We need a mutex here if this function can be called from
+ * different contexts. */
+
+ pshm = container_of(layr, shm_layer_t, shm_phy);
+
+ /* First try to append the frame. */
+ result = shm_send_pkt(pshm, cfpkt, true);
+ if (!result)
+ return result;
+
+ /* Try a new buffer. */
+ result = shm_send_pkt(pshm, cfpkt, false);
+
+ return result;
+}
+
+static int __init phyif_shm_init(void)
+{
+ int result = -1;
+ int i, j;
+
+ /* Initialize the shared memory instances. */
+ for (i = 0; i < SHM_INSTANCES; i++) {
+ /* Initialize structures in a clean state. */
+ memset(&shm_layer[i], 0, sizeof(shm_layer[i]));
+
+ /* Fill in some information about our PHY. */
+ shm_layer[i].shm_phy.transmit = shm_phy_tx;
+ shm_layer[i].shm_phy.receive = NULL;
+ shm_layer[i].shm_phy.ctrlcmd = shm_phy_fctrl;
+
+ /* TODO: Instance number should be appended. */
+ sprintf(shm_layer[i].cfg_name, "%s%d", mbxcfg_name, i);
+ sprintf(shm_layer[i].mbx_name, "%s%d", mbxifc_name, i);
+
+ /* Initialize queues. */
+ INIT_LIST_HEAD(&(shm_layer[i].tx_empty_list));
+ INIT_LIST_HEAD(&(shm_layer[i].rx_empty_list));
+ INIT_LIST_HEAD(&(shm_layer[i].tx_pend_list));
+ INIT_LIST_HEAD(&(shm_layer[i].rx_pend_list));
+ INIT_LIST_HEAD(&(shm_layer[i].tx_full_list));
+ INIT_LIST_HEAD(&(shm_layer[i].rx_full_list));
+
+ INIT_WORK(&shm_layer[i].sig_work, phyif_shm_sig_work_func);
+ INIT_WORK(&shm_layer[i].rx_work, phyif_shm_rx_work_func);
+ INIT_WORK(&shm_layer[i].flow_work, phyif_shm_flow_work_func);
+
+ /* Create the RX work thread.
+ * TODO: Instance number should be appended. */
+ shm_layer[i].rx_work_queue =
+ create_singlethread_workqueue("phyif_shm_rx");
+
+ /* Create the signaling work thread.
+ * TODO: Instance number should be appended. */
+
+ shm_layer[i].sig_work_queue =
+ create_singlethread_workqueue("phyif_shm_sig");
+
+ /* Create the flow re-enable work thread.
+ * TODO: Instance number should be appended. */
+ shm_layer[i].flow_work_queue =
+ create_singlethread_workqueue("phyif_shm_flow");
+
+ init_waitqueue_head(&(shm_layer[i].mbx_space_wq));
+ shm_layer[i].tx_empty_available = 1;
+
+ /* Connect to the shared memory configuration module. */
+ /*shm_layer[i].cfg_ifc = cfgifc_get(shm_layer[i].cfg_name);*/
+
+ /* Initialize the shared memory transmit buffer queues. */
+ for (j = 0; j < shm_nr_tx_buf; j++) {
+ shm_buf_t *tx_buf =
+ kmalloc(sizeof(shm_buf_t), GFP_KERNEL);
+ tx_buf->index = j;
+ tx_buf->len = shm_tx_buf_len;
+ if ((i % 2) == 0) {
+ tx_buf->phy_addr =
+ shm_tx_addr + (shm_tx_buf_len * j);
+ } else {
+ tx_buf->phy_addr =
+ shm_rx_addr + (shm_tx_buf_len * j);
+ }
+ tx_buf->desc_ptr =
+ ioremap(tx_buf->phy_addr, shm_tx_buf_len);
+ tx_buf->frames = 0;
+ tx_buf->frm_ofs = SHM_CAIF_FRM_OFS;
+ list_add_tail(&tx_buf->list,
+ &shm_layer[i].tx_empty_list);
+ }
+
+ /* Initialize the shared memory receive buffer queues. */
+ for (j = 0; j < shm_nr_rx_buf; j++) {
+ shm_buf_t *rx_buf =
+ kmalloc(sizeof(shm_buf_t), GFP_KERNEL);
+ rx_buf->index = j;
+ rx_buf->len = shm_rx_buf_len;
+ if ((i % 2) == 0) {
+ rx_buf->phy_addr =
+ shm_rx_addr + (shm_rx_buf_len * j);
+ } else {
+ rx_buf->phy_addr =
+ shm_tx_addr + (shm_rx_buf_len * j);
+ }
+ rx_buf->desc_ptr =
+ ioremap(rx_buf->phy_addr, shm_rx_buf_len);
+ list_add_tail(&rx_buf->list,
+ &shm_layer[i].rx_empty_list);
+ }
+
+ /* Connect to the shared memory mailbox module. */
+ shm_layer[i].mbx_ifc = mbxifc_get(shm_layer[i].mbx_name);
+ if (!shm_layer[i].mbx_ifc) {
+ printk(KERN_WARNING
+ "phyif_shm_init: can't find mailbox: %s.\n",
+ shm_layer[i].mbx_name);
+ /* CLEANUP !!! */
+ return CFGLU_ENXIO;
+ }
+
+ /* Fill in some info about ourselves. */
+ shm_layer[i].mbx_client.cb = phyif_shm_mbx_msg_cb;
+ shm_layer[i].mbx_client.priv = (void *) &shm_layer[i];
+
+ shm_layer[i].mbx_ifc->init(&shm_layer[i].mbx_client,
+ shm_layer[i].mbx_ifc->priv);
+
+ if ((i % 2) == 0) {
+ /* Register physical interface. */
+ result =
+ caifdev_phy_register(&shm_layer[i].shm_phy,
+ CFPHYTYPE_SHM,
+ CFPHYPREF_UNSPECIFIED);
+ } else {
+ /* Register loop interface. */
+ result =
+ caifdev_phy_loop_register(&shm_layer
+ [i].shm_phy,
+ CFPHYTYPE_SHM);
+ }
+
+ if (result < 0) {
+ printk(KERN_WARNING
+ "phyif_shm_init: err: %d, "
+ "can't register phy layer.\n",
+ result);
+ /* CLEANUP !!! */
+ }
+
+ }
+
+ return result;
+}
+
+static void phyif_shm_exit(void)
+{
+ shm_buf_t *pbuf;
+ uint8 i = 0;
+ for (i = 0; i < SHM_INSTANCES; i++) {
+
+ /* unregister callbacks from mailbox interface */
+ shm_layer[i].mbx_client.cb = NULL;
+
+ /* TODO: wait for completetion or timeout */
+ while (!(list_empty(&shm_layer[i].tx_pend_list))) {
+ pbuf =
+ list_entry(shm_layer[i].tx_pend_list.next,
+ shm_buf_t, list);
+
+ kfree(pbuf);
+ list_del(&pbuf->list);
+ }
+
+ /* TODO: wait for completetion or timeout */
+ while (!(list_empty(&shm_layer[i].tx_full_list))) {
+ pbuf =
+ list_entry(shm_layer[i].tx_full_list.next,
+ shm_buf_t, list);
+ kfree(pbuf);
+ list_del(&pbuf->list);
+ }
+
+ while (!(list_empty(&shm_layer[i].tx_empty_list))) {
+ pbuf =
+ list_entry(shm_layer[i].tx_empty_list.next,
+ shm_buf_t, list);
+ kfree(pbuf);
+ list_del(&pbuf->list);
+ }
+
+ /* TODO: wait for completetion or timeout */
+ while (!(list_empty(&shm_layer[i].rx_full_list))) {
+ pbuf =
+ list_entry(shm_layer[i].tx_full_list.next,
+ shm_buf_t, list);
+ kfree(pbuf);
+ list_del(&pbuf->list);
+ }
+
+ /* TODO: wait for completetion or timeout */
+ while (!(list_empty(&shm_layer[i].rx_pend_list))) {
+ pbuf =
+ list_entry(shm_layer[i].tx_pend_list.next,
+ shm_buf_t, list);
+ kfree(pbuf);
+ list_del(&pbuf->list);
+ }
+
+ while (!(list_empty(&shm_layer[i].rx_empty_list))) {
+ pbuf =
+ list_entry(shm_layer[i].rx_empty_list.next,
+ shm_buf_t, list);
+ kfree(pbuf);
+ list_del(&pbuf->list);
+ }
+ }
+
+ /* destroy work queues */
+ destroy_workqueue(shm_layer[i].rx_work_queue);
+ destroy_workqueue(shm_layer[i].sig_work_queue);
+}
+
+module_init(phyif_shm_init);
+module_exit(phyif_shm_exit);
diff --git a/drivers/net/caif/shm.h b/drivers/net/caif/shm.h
new file mode 100644
index 0000000..d117b0a
--- /dev/null
+++ b/drivers/net/caif/shm.h
@@ -0,0 +1,95 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Daniel Martensson / Daniel.Martensson@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+#ifndef SHM_H_
+#define SHM_H_
+
+#include <linux/list.h>
+/*#include <linux/init.h>
+#include <linux/workqueue.h>*/
+#include "caif_layer.h"
+
+#define ESUCCESS 0
+
+typedef int (*mbxifc_cb_t) (u16 mbx_msg, void *priv);
+
+/*FIXME:why is the client here??*/
+/* Shared memory mailbox client structure. */
+typedef struct _shm_mbxclient_t {
+ mbxifc_cb_t cb;
+ void *priv;
+} shm_mbxclient_t;
+
+typedef int (*mbxifc_init_t) (shm_mbxclient_t *mbx_client, void *priv);
+
+typedef int (*mbxifc_send_t) (u16 mbx_msg, void *priv);
+
+/* Shared memory mailbox interface structure. */
+typedef struct _shm_mbxifc_t {
+ mbxifc_init_t init;
+ mbxifc_send_t send_msg;
+ char name[16];
+ void *priv;
+ shm_mbxclient_t *client;
+ struct list_head list;
+} shm_mbxifc_t;
+
+int mbxifc_register(shm_mbxifc_t *client);
+
+int mbxifc_send(u16 mbx_msg, void *priv);
+shm_mbxifc_t *mbxifc_get(unsigned char *name);
+void mbxifc_put(shm_mbxifc_t *mbx_ifc);
+
+/* emardan: Use B380 setup for the first CAIF channel.*/
+/* TODO: This needs to go into a shared memory interface. */
+static int shm_base_addr = 0x81F00000;
+module_param(shm_base_addr, int, S_IRUGO);
+MODULE_PARM_DESC(shm_base_addr,
+ "Physical base address of the shared memory area");
+
+static int shm_tx_addr = 0x81F00000;
+module_param(shm_tx_addr, int, S_IRUGO);
+MODULE_PARM_DESC(shm_tx_addr, "Physical start address for transmission area");
+
+static int shm_rx_addr = 0x81F0C000;
+module_param(shm_rx_addr, int, S_IRUGO);
+MODULE_PARM_DESC(shm_rx_addr, "Physical start address for reception area");
+
+static int shm_nr_tx_buf = 6;
+module_param(shm_nr_tx_buf, int, S_IRUGO);
+MODULE_PARM_DESC(shm_nr_tx_buf, "number of transmit buffers");
+
+static int shm_nr_rx_buf = 6;
+module_param(shm_nr_rx_buf, int, S_IRUGO);
+MODULE_PARM_DESC(shm_nr_rx_buf, "number of receive buffers");
+
+static int shm_tx_buf_len = 0x2000;
+module_param(shm_tx_buf_len, int, S_IRUGO);
+MODULE_PARM_DESC(shm_tx_buf_len, "size of transmit buffers");
+
+static int shm_rx_buf_len = 0x2000;
+module_param(shm_rx_buf_len, int, S_IRUGO);
+MODULE_PARM_DESC(shm_rx_buf_len, "size of receive buffers");
+
+/* Shared memory interface structure. */
+typedef struct _shm_cfgifc_t {
+ int base_addr;
+ int tx_addr;
+ int rx_addr;
+ int nr_tx_buf;
+ int nr_rx_buf;
+ int tx_buf_len;
+ int rx_buf_len;
+ char name[16];
+ struct list_head list;
+} shm_cfgifc_t;
+
+shm_cfgifc_t *cfgifc_get(unsigned char *name);
+
+#endif /* SHM_H_ */
diff --git a/drivers/net/caif/shm_cfgifc.c b/drivers/net/caif/shm_cfgifc.c
new file mode 100644
index 0000000..bf00ce0
--- /dev/null
+++ b/drivers/net/caif/shm_cfgifc.c
@@ -0,0 +1,60 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Daniel Martensson / Daniel.Martensson@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+/* Standard includes. */
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/list.h>
+
+#include "shm.h"
+
+MODULE_LICENSE("GPL");
+
+static struct list_head cfgifc_list;
+
+void cfgifc_exit_module(void)
+{
+
+}
+
+int cfgifc_init_module(void)
+{
+ return -ENODEV;
+}
+
+int cfgifc_register(shm_cfgifc_t *mbx_ifc)
+{
+ return 0;
+}
+EXPORT_SYMBOL(cfgifc_register);
+
+shm_cfgifc_t *cfgifc_get(unsigned char *name)
+{
+ /* Hook up the adaptation layer. TODO: Make phy_type dynamic. */
+ cfcnfg_add_adapt_layer(caifdev.cfg, linktype, connid,
+ CFPHYTYPE_SERIAL, adap_layer);
+
+ return NULL;
+}
+EXPORT_SYMBOL(cfgifc_get);
+
+void cfgifc_put(shm_cfgifc_t *mbx_ifc)
+{
+ /* Hook up the adaptation layer. TODO: Make phy_type dynamic. */
+ cfcnfg_add_adapt_layer(caifdev.cfg, linktype, connid,
+ CFPHYTYPE_SERIAL, adap_layer);
+
+ return 0;
+}
+EXPORT_SYMBOL(cfgifc_put);
+
+
+module_init(cfgifc_init_module);
+module_exit(cfgifc_exit_module);
diff --git a/drivers/net/caif/shm_mbxifc.c b/drivers/net/caif/shm_mbxifc.c
new file mode 100644
index 0000000..f108799
--- /dev/null
+++ b/drivers/net/caif/shm_mbxifc.c
@@ -0,0 +1,98 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Daniel Martensson / Daniel.Martensson@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+/* Standard includes. */
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/list.h>
+
+#include "shm.h"
+
+MODULE_LICENSE("GPL");
+
+static struct list_head mbxifc_list;
+
+void mbxifc_exit_module(void)
+{
+
+}
+
+int mbxifc_init_module(void)
+{
+ INIT_LIST_HEAD(&mbxifc_list);
+
+ return ESUCCESS;
+}
+
+int mbxifc_register(shm_mbxifc_t *mbxifc)
+{
+ if (!mbxifc)
+ return -EINVAL;
+
+ list_add_tail(&mbxifc->list, &mbxifc_list);
+ printk(KERN_WARNING
+ "mbxifc_register: mailbox: %s at 0x%p added.\n",
+ mbxifc->name, mbxifc);
+
+ return ESUCCESS;
+}
+EXPORT_SYMBOL(mbxifc_register);
+
+int mbxifc_unregister(shm_mbxifc_t *mbx_ifc)
+{
+ shm_mbxifc_t *mbxifcreg;
+
+ while (!(list_empty(&mbxifc_list))) {
+ mbxifcreg = list_entry(mbxifc_list.next, shm_mbxifc_t, list);
+ if (mbxifcreg == mbx_ifc) {
+ list_del(&mbxifcreg->list);
+ break;
+ }
+ }
+
+ return ESUCCESS;
+}
+EXPORT_SYMBOL(mbxifc_unregister);
+
+shm_mbxifc_t *mbxifc_get(unsigned char *name)
+{
+ shm_mbxifc_t *mbxifc;
+ struct list_head *pos;
+ list_for_each(pos, &mbxifc_list) {
+ mbxifc = list_entry(pos, shm_mbxifc_t, list);
+ if (strcmp(mbxifc->name, name) == 0) {
+ list_del(&mbxifc->list);
+ printk(KERN_WARNING
+ "mbxifc_get: mailbox: %s at 0x%p found.\n",
+ mbxifc->name, mbxifc);
+ return mbxifc;
+ }
+ }
+
+ printk(KERN_WARNING "mbxifc_get: no mailbox: %s found.\n", name);
+
+ return NULL;
+}
+EXPORT_SYMBOL(mbxifc_get);
+
+void mbxifc_put(shm_mbxifc_t *mbx_ifc)
+{
+ if (!mbx_ifc)
+ return;
+
+ list_add_tail(&mbx_ifc->list, &mbxifc_list);
+
+ return;
+}
+EXPORT_SYMBOL(mbxifc_put);
+
+
+module_init(mbxifc_init_module);
+module_exit(mbxifc_exit_module);
diff --git a/drivers/net/caif/shm_smbx.c b/drivers/net/caif/shm_smbx.c
new file mode 100644
index 0000000..57770d4
--- /dev/null
+++ b/drivers/net/caif/shm_smbx.c
@@ -0,0 +1,81 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Daniel Martensson / Daniel.Martensson@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+/* Very simple simulated mailbox interface supporting
+ * multiple mailbox instances. */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+
+#include "shm.h"
+
+MODULE_LICENSE("GPL");
+
+#define MBX_NR_OF_INSTANCES 2
+#define MBX_SMBX_NAME "cfmbx"
+
+typedef struct _shm_smbx_t {
+ shm_mbxifc_t local;
+ shm_mbxifc_t *peer;
+} shm_smbx_t;
+
+static shm_smbx_t shm_smbx[MBX_NR_OF_INSTANCES];
+
+static int smbx_ifc_init(shm_mbxclient_t *mbx_client, void *priv)
+{
+ shm_smbx_t *mbx = (shm_smbx_t *) priv;
+
+ mbx->local.client = mbx_client;
+
+ return 0;
+}
+
+static int smbx_ifc_send_msg(u16 mbx_msg, void *priv)
+{
+ shm_smbx_t *mbx = (shm_smbx_t *) priv;
+
+ mbx->peer->client->cb(mbx_msg, mbx->peer->client->priv);
+
+ return 0;
+
+}
+
+static int __init shm_smbx_init(void)
+{
+ int i;
+
+ for (i = 0; i < MBX_NR_OF_INSTANCES; i++) {
+ /* Set up the mailbox interface. */
+ shm_smbx[i].local.init = smbx_ifc_init;
+ shm_smbx[i].local.send_msg = smbx_ifc_send_msg;
+ sprintf(shm_smbx[i].local.name, "%s%d", MBX_SMBX_NAME, i);
+ shm_smbx[i].local.priv = &shm_smbx[i];
+
+ /* Set up the correct peer.
+ * (0 is connected to one and so forth). */
+ if (i % 2)
+ shm_smbx[i].peer = &shm_smbx[i - 1].local;
+ else
+ shm_smbx[i].peer = &shm_smbx[i + 1].local;
+
+
+ mbxifc_register(&(shm_smbx[i].local));
+ }
+
+ return 0;
+}
+
+static void shm_smbx_exit(void)
+{
+ /* Nothing to do. */
+}
+
+module_init(shm_smbx_init);
+module_exit(shm_smbx_exit);
--
1.6.0.4
^ permalink raw reply related
* [PATCH 5/8] [RFC] CAIF Protocol Stack
From: sjur.brandeland @ 2009-09-23 17:31 UTC (permalink / raw)
To: netdev; +Cc: Kim.xx.Lilliestierna, sjur.brandeland
From: Kim Lilliestierna <Kim.xx.Lilliestierna@ericsson.com>
Signed-off-by: sjur.brandeland@stericsson.com
---
net/caif/Kconfig | 61 +++
net/caif/Makefile | 62 +++
net/caif/caif_chnlif.c | 219 ++++++++
net/caif/caif_chr.c | 378 ++++++++++++++
net/caif/caif_config_util.c | 167 +++++++
net/caif/chnl_chr.c | 1161 +++++++++++++++++++++++++++++++++++++++++++
net/caif/chnl_net.c | 464 +++++++++++++++++
7 files changed, 2512 insertions(+), 0 deletions(-)
create mode 100644 net/caif/Kconfig
create mode 100644 net/caif/Makefile
create mode 100644 net/caif/caif_chnlif.c
create mode 100644 net/caif/caif_chr.c
create mode 100644 net/caif/caif_config_util.c
create mode 100644 net/caif/chnl_chr.c
create mode 100644 net/caif/chnl_net.c
diff --git a/net/caif/Kconfig b/net/caif/Kconfig
new file mode 100644
index 0000000..24151c1
--- /dev/null
+++ b/net/caif/Kconfig
@@ -0,0 +1,61 @@
+#
+# CAIF net configurations
+#
+
+#menu "Caif Support"
+comment "CAIF Support"
+
+menuconfig CAIF
+ tristate "Enable Caif support"
+ default m
+ ---help---
+ Say Y here if you need to a phone modem that uses CAIF as transport
+ You will also need to say yes to anny caif physical devices that your platform
+ supports.
+ This can be built as either built in or as a module, if you select to build it as module
+ the other CAIF parts also needs to built as modules
+ See Documentation/CAIF for a further explanation on how to use and configure.
+
+if CAIF
+
+config CAIF_CHARDEV
+ tristate "CAIF character device"
+ default CAIF
+ ---help---
+ Say Y if you will be using the CAIF character devices,
+ This is needed for AT type channels
+ If you select to build it as a built in then the main caif device must also be a builtin
+ If unsure say Y
+
+config CAIF_NETDEV
+ tristate "CAIF Network device"
+ default CAIF
+ ---help---
+ If you select to build it as a built in then the main caif device must also be a builtin
+ Say Y if you will be using the CAIF based network device
+ If unsure say Y
+
+
+config CAIF_USE_PLAIN
+ bool "Use plain buffers instead of SKB in caif"
+ default n
+ ---help---
+ Use plain buffer to transport data
+ Select what type of internal buffering CAIF should use,
+ skb or plain,
+ If unsure say y
+
+config CAIF_DEBUG
+ bool "Enable Debug"
+ default n
+ --- help ---
+ Enable the inclusion of debug code in the caif stack
+ be aware that doing this will impact performance
+ If unsure say n here.
+
+# Include physical drivers
+# should be broken out into its own config file
+# source "drivers/net/caif/Kconfig"
+source "drivers/net/caif/Kconfig"
+endif
+#endmenu
diff --git a/net/caif/Makefile b/net/caif/Makefile
new file mode 100644
index 0000000..49696ab
--- /dev/null
+++ b/net/caif/Makefile
@@ -0,0 +1,62 @@
+ifeq ($(CONFIG_CAIF_USE_PLAIN),1)
+CFPKT:=plain
+else
+CFPKT:=skbuff
+CAIF_FLAGS+=-DCAIF_USE_SKB
+endif
+
+ifeq ($(CONFIG_CAIF_DEBUG),1)
+CAIF_FLAGS+=-DCAIF_DEBUG_ON
+endif
+
+
+ccflags-y := -DCAIF_KERNEL $(CAIF_FLAGS) -Iinclude/net/caif/ -Iinclude/net/caif/generic/ -Iinclude/linux/caif/
+
+
+caif-objs := caif_chr.o caif_chnlif.o caif_config_util.o \
+ generic/cfcnfg.o generic/cfmuxl.o generic/cfctrl.o \
+ generic/cffrml.o generic/cfveil.o generic/cflist.o \
+ generic/fcs.o generic/cfserl.o generic/cfdgml.o \
+ generic/cfspil.o generic/cfrfml.o generic/cfvidl.o \
+ generic/cfmsll.o generic/cfutill.o generic/cfshml.o \
+ generic/cfloopcfg.o generic/cflooplayer.o generic/cfsrvl.o \
+ generic/cfpkt_$(CFPKT).o
+
+
+clean-dirs:= .tmp_versions
+
+clean-files:= Module.symvers modules.order *.cmd *~ \
+ generic/loopback/Module.symvers \
+ generic/loopback/modules.order \
+ generic/loopback/*.cmd \
+ generic/loopback/*.o \
+ generic/loopback/*~ \
+ generic/Module.symvers \
+ generic/modules.order \
+ generic/*.cmd \
+ generic/*.o \
+ generic/*~
+
+
+# Main caif module
+obj-$(CONFIG_CAIF) += caif.o
+
+# Character device
+obj-$(CAIF_CHRDEV) += chnl_chr.o
+
+# Net device
+obj-$(CAIF_NETDEV) += chnl_net.o
+
+export-objs := caif_chr.o
+
+## Indent.. to remove all DOS cruft like CRLF and trailing spaces, and to standardize the indenting style
+indent:
+ ${MAKE} -C generic indent
+ sh -c 'for F in *.[ch]; do cat $$F | tr -d "\r" |tr -c '\015'| sed "s/[ \t]*$$//;" > $$F.tmp; mv $$F.tmp $$F; done'
+ ${INDENT} -kr -i8 *.[ch]
+
+clean:
+ ${MAKE} -C generic clean
+ rm generic/modules.order generic/Module.symvers generic/*.cmd generic/*~ \
+ generic/modules.order generic/Module.symvers \
+ generic/*.o generic/loopback/*.o
diff --git a/net/caif/caif_chnlif.c b/net/caif/caif_chnlif.c
new file mode 100644
index 0000000..e53ca7a
--- /dev/null
+++ b/net/caif/caif_chnlif.c
@@ -0,0 +1,219 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Daniel Martensson / Daniel.Martensson@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+#include <linux/skbuff.h>
+#include "caif_kernel.h"
+#include "caif_layer.h"
+#include "caif_config_util.h"
+#include "caif_log.h"
+#include "cfpkt.h"
+#include "cfcnfg.h"
+#include "cfglue.h"
+
+
+
+struct caif_kernelif {
+ layer_t layer;
+ struct caif_device *dev;
+ cfctrl_link_param_t param;
+};
+static cfcnfg_t *cnfg;
+/**
+ * func chnlif_set_cnfg - Set the global config
+ * @cfg: Config structure to set
+*/
+
+void chnlif_set_cnfg(cfcnfg_t *cfg)
+{
+ cnfg = cfg;
+}
+EXPORT_SYMBOL(chnlif_set_cnfg);
+
+/**
+ * func caif_create_skb - Creates a caif skb buffer
+ * @data: data to add to buffer
+ * @data_length: lenht of data
+*/
+struct sk_buff *caif_create_skb(unsigned char *data, unsigned int data_length)
+{
+ /* NOTE: Make room for CAIF headers when using SKB inside CAIF. */
+ struct sk_buff *skb =
+ alloc_skb(data_length + CAIF_SKB_HEAD_RESERVE +
+ CAIF_SKB_TAIL_RESERVE, GFP_ATOMIC);
+ skb_reserve(skb, CAIF_SKB_HEAD_RESERVE);
+ if (skb == NULL)
+ return NULL;
+
+ memcpy(skb_put(skb, data_length), data, data_length);
+ return skb;
+}
+EXPORT_SYMBOL(caif_create_skb);
+
+int
+caif_extract_and_destroy_skb(struct sk_buff *skb, unsigned char *data,
+ unsigned int max_length)
+{
+ unsigned int len;
+ len = skb->len;
+ skb_linearize(skb);
+ if (skb->len > max_length)
+ return CFGLU_EOVERFLOW;
+ memcpy(data, skb->data, skb->len);
+ kfree_skb(skb);
+ return len;
+}
+EXPORT_SYMBOL(caif_extract_and_destroy_skb);
+
+/* NOTE: transmit takes ownership of the SKB.
+ * i.e. transmit only fails on severe errors.
+ * flow_off is not checked on transmit this is clients responcibility.
+ */
+
+int caif_transmit(struct caif_device *dev, struct sk_buff *skb)
+{
+ struct caif_kernelif *chnlif =
+ (struct caif_kernelif *) dev->_caif_handle;
+ cfpkt_t *pkt;
+#ifdef CAIF_USE_SKB
+ pkt = cfpkt_fromnative(CAIF_DIR_OUT, (void *) skb);
+#else
+ pkt = cfpkt_create(skb->len);
+ cfpkt_add_body(pkt, skb->data, skb->len);
+ kfree_skb(skb);
+#endif
+ CAIFLOG_TRACE2("Transmit (%p)", chnlif->layer.dn);
+ return chnlif->layer.dn->transmit(chnlif->layer.dn, NULL, pkt);
+}
+EXPORT_SYMBOL(caif_transmit);
+
+
+int caif_flow_control(struct caif_device *dev, enum caif_flowctrl flow)
+{
+ caif_modemcmd_t modemcmd;
+ struct caif_kernelif *chnlif =
+ (struct caif_kernelif *) dev->_caif_handle;
+ switch (flow) {
+ case CAIF_FLOWCTRL_ON:
+ modemcmd = CAIF_MODEMCMD_FLOW_ON_REQ;
+ break;
+ case CAIF_FLOWCTRL_OFF:
+ modemcmd = CAIF_MODEMCMD_FLOW_OFF_REQ;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return chnlif->layer.dn->modemcmd(chnlif->layer.dn, modemcmd);
+}
+EXPORT_SYMBOL(caif_flow_control);
+
+
+static int chnlif_receive(layer_t *layr, struct _cfpkt_t *cfpkt)
+{
+ /* FIXME: Use container of */
+ struct caif_kernelif *chnl = (struct caif_kernelif *) layr;
+
+ struct sk_buff *skb;
+#ifndef CAIF_USE_SKB
+ unsigned int pktlen = cfpkt_getlen(cfpkt);
+ unsigned int actual_len;
+ skb = alloc_skb(pktlen, GFP_ATOMIC);
+ if (skb == NULL)
+ return CFGLU_ENOMEM;
+
+ /* Extract data from the caif packet and copy it to the skb. */
+ cfpkt_extract(cfpkt, skb_put(skb, pktlen), pktlen, &actual_len);
+ cfglu_assert(pktlen == actual_len);
+ cfpkt_destroy(cfpkt);
+#else
+ skb = (struct sk_buff *) cfpkt_tonative(cfpkt);
+#endif
+ chnl->dev->receive_cb(chnl->dev, skb);
+ return CFGLU_EOK;
+}
+
+static void chnlif_flowctrl(layer_t *layr, caif_ctrlcmd_t ctrl, int phyid)
+{
+ struct caif_kernelif *chnl = (struct caif_kernelif *) layr;
+ enum caif_control ctl;
+
+ CAIFLOG_TRACE("Flow Control received %d\n", ctrl);
+
+ switch (ctrl) {
+ case CAIF_CTRLCMD_FLOW_OFF_IND:
+ ctl = CAIF_CONTROL_FLOW_OFF;
+ break;
+ case CAIF_CTRLCMD_FLOW_ON_IND:
+ ctl = CAIF_CONTROL_FLOW_ON;
+ break;
+ case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND:
+ ctl = CAIF_CONTROL_REMOTE_SHUTDOWN;
+ break;
+ case CAIF_CTRLCMD_DEINIT_RSP:
+ ctl = CAIF_CONTROL_DEV_DEINIT;
+ chnl->dev->_caif_handle = NULL;
+ chnl->dev->control_cb(chnl->dev, ctl);
+ memset(chnl, 0, sizeof(chnl));
+ cfglu_free(chnl);
+ return;
+
+ case CAIF_CTRLCMD_INIT_RSP:
+ ctl = CAIF_CONTROL_DEV_INIT;
+ break;
+ case CAIF_CTRLCMD_INIT_FAIL_RSP:
+ ctl = CAIF_CONTROL_DEV_INIT_FAILED;
+ break;
+ default:
+ return;
+ }
+ chnl->dev->control_cb(chnl->dev, ctl);
+}
+
+
+int caif_add_device(struct caif_device *dev)
+{
+ int ret;
+ struct caif_kernelif *chnl = cfglu_alloc(sizeof(struct caif_kernelif));
+ chnl->dev = dev;
+ chnl->layer.ctrlcmd = chnlif_flowctrl;
+ chnl->layer.receive = chnlif_receive;
+ ret =
+ channel_config_2_link_param(cnfg, &dev->caif_config, &chnl->param);
+
+
+ if (ret < 0) {
+ CAIFLOG_WARN("Bad Channel Configuration\n");
+ ret = CFGLU_EBADPARAM;
+ goto error;
+ }
+
+ if (!cfcnfg_add_adaptation_layer(cnfg, &chnl->param, &chnl->layer)) {
+ ret = CFGLU_ENOTCONN;
+ goto error;
+ }
+
+ dev->_caif_handle = chnl;
+
+ return CFGLU_EOK;
+error:
+ chnl->dev->_caif_handle = NULL;
+ memset(chnl, 0, sizeof(chnl));
+ cfglu_free(chnl);
+ return ret;
+}
+EXPORT_SYMBOL(caif_add_device);
+
+int caif_remove_device(struct caif_device *caif_dev)
+{
+
+ struct caif_kernelif *chnl =
+ container_of(caif_dev->_caif_handle, struct caif_kernelif, layer);
+ bool ok = cfcnfg_del_adapt_layer(cnfg, &chnl->layer);
+ return ok ? CFGLU_EOK : CFGLU_EIO;
+}
+EXPORT_SYMBOL(caif_remove_device);
diff --git a/net/caif/caif_chr.c b/net/caif/caif_chr.c
new file mode 100644
index 0000000..92c60cd
--- /dev/null
+++ b/net/caif/caif_chr.c
@@ -0,0 +1,378 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Daniel Martensson / Daniel.Martensson@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+
+
+
+
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/ioctl.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+
+#include "caif_actions.h"
+#include "caif_chr.h"
+#include "cfloopcfg.h"
+#include "cfpkt.h"
+#include "cfcnfg.h"
+#include "caif_config_util.h"
+#include "caif_log.h"
+#include "caif_ioctl.h"
+
+#define caif_assert(assert) BUG_ON(!(assert))
+
+MODULE_LICENSE("GPL");
+int (*chrdev_mgmt_func) (int action, union caif_action *param);
+int (*netdev_mgmt_func) (int action, union caif_action *param);
+
+struct caif_chr {
+ cfcnfg_t *cfg;
+ cfloopcfg_t *loop;
+ struct miscdevice misc;
+};
+
+int caif_dbg_level = CAIFLOG_LEVEL_WARNING;
+EXPORT_SYMBOL(caif_dbg_level);
+
+static struct caif_chr caifdev;
+
+struct class caif_class = {
+ .name = "caif",
+};
+
+static ssize_t dbg_lvl_show(struct class *class, char *buf)
+{
+ return sprintf(buf, "%d\n", caif_dbg_level);
+}
+
+static ssize_t dbg_lvl_store(struct class *class, const char *buf,
+ size_t count)
+{
+ int val;
+
+ sscanf(buf, "%d", &val);
+ if ((val < CAIFLOG_MIN_LEVEL) || (val > CAIFLOG_MAX_LEVEL)) {
+ printk(KERN_WARNING "caif_dbg_level: Invalid value\n");
+ return -EINVAL;
+ }
+
+ caif_dbg_level = val;
+
+ return count;
+}
+
+CLASS_ATTR(dbg_lvl, 0644, dbg_lvl_show, dbg_lvl_store);
+
+int caifdev_open(struct inode *inode, struct file *filp)
+{
+ printk(KERN_WARNING "caifdev_open: Entered\n");
+ return 0;
+}
+
+int caifdev_release(struct inode *inode, struct file *filp)
+{
+ printk(KERN_WARNING "caifdev_release: Entered\n");
+ return 0;
+}
+
+static int caifdev_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long argp)
+{
+ union caif_action param;
+ int ret;
+ int (*mgmt_func) (int action, union caif_action *param) = NULL;
+ int type;
+ int size;
+ int operation;
+ enum caif_dev_type devtype;
+
+ printk(KERN_WARNING "caifdev_ioctl: Entered\n");
+
+ if (argp == 0) {
+ printk(KERN_INFO "caifdev_ioctl: argument is null\n");
+ return -EINVAL;
+ }
+
+ type = _IOC_TYPE(cmd);
+ printk(KERN_INFO "caifdev_ioctl: type = %d\n", type);
+
+ if (type != CAIF_IOC_MAGIC) {
+ printk(KERN_INFO "caifdev_ioctl: unknown ioctl type\n");
+ return -EINVAL;
+ }
+
+ /* Check if command is valid before copying anything */
+ switch (cmd) {
+ case CAIF_IOC_CONFIG_DEVICE:
+ case CAIF_IOC_REMOVE_DEVICE:
+ break;
+ default:
+ printk(KERN_INFO "caifdev_ioctl: unknown ioctl command\n");
+ return -EINVAL;
+ }
+
+ size = _IOC_SIZE(cmd);
+ printk(KERN_INFO "caifdev_ioctl: size = %d\n", size);
+
+ if (copy_from_user(¶m, (void *) argp, size)) {
+ printk(KERN_WARNING
+ "caifdev_ioctl: copy_from_user returned non zero.\n");
+ return -EINVAL;
+ }
+
+ switch (cmd) {
+
+ case CAIF_IOC_CONFIG_DEVICE:
+
+ operation = CAIF_ACT_CREATE_DEVICE;
+ devtype = param.create_channel.name.devtype;
+ break;
+
+ case CAIF_IOC_REMOVE_DEVICE:
+
+ operation = CAIF_ACT_DELETE_DEVICE;
+ devtype = param.delete_channel.devtype;
+ break;
+
+ default:
+ printk(KERN_INFO
+ "caifdev_ioctl: OTHER ACTIONS NOT YET IMPLEMENTED\n");
+ return -EINVAL;
+ }
+
+ if (devtype == CAIF_DEV_CHR) {
+ printk(KERN_INFO "caifdev_ioctl: device type CAIF_DEV_CHR\n");
+ mgmt_func = chrdev_mgmt_func;
+ } else if (devtype == CAIF_DEV_NET) {
+ printk(KERN_INFO "caifdev_ioctl: device type CAIF_DEV_NET\n");
+ mgmt_func = netdev_mgmt_func;
+ }
+
+ if (mgmt_func == NULL) {
+ printk(KERN_WARNING
+ "caifdev_ioctl: DevType %s is not registered\n",
+ devtype == CAIF_DEV_CHR ? "CHAR" :
+ devtype == CAIF_DEV_NET ? "NET" : "UNKNOWN");
+ return -EINVAL;
+ }
+
+ ret = (*mgmt_func) (operation, ¶m);
+
+ if (ret < 0) {
+ printk(KERN_INFO
+ "caifdev_ioctl: error performing device operation\n");
+ return ret;
+ }
+ if (copy_to_user((void *) argp, ¶m, size)) {
+ printk(KERN_WARNING
+ "caifdev_ioctl: copy_to_user returned non zero.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+const struct file_operations caifdev_fops = {
+ .owner = THIS_MODULE,
+ .open = caifdev_open,
+ .ioctl = caifdev_ioctl,
+ .release = caifdev_release,
+};
+
+void caifdev_exit_module(void)
+{
+ class_remove_file(&caif_class, &class_attr_dbg_lvl);
+ class_unregister(&caif_class);
+ misc_deregister(&caifdev.misc);
+}
+
+int caifdev_init_module(void)
+{
+ int result;
+ /* FIXME: Reverse the directon, rather make chnlif do a get! */
+ extern void chnlif_set_cnfg(cfcnfg_t *cfg);
+
+ caifdev.cfg = cfcnfg_create();
+ if (!caifdev.cfg) {
+ printk(KERN_WARNING "caifdev: err: can't create cfcnfg.\n");
+ goto err_cfcnfg_create_failed;
+ }
+
+ chnlif_set_cnfg(caifdev.cfg);
+
+ caifdev.misc.minor = MISC_DYNAMIC_MINOR;
+ /*FIXME: Rename device to "caifconfig" */
+ caifdev.misc.name = "caifconfig";
+ caifdev.misc.fops = &caifdev_fops;
+
+ result = misc_register(&caifdev.misc);
+
+ if (result < 0) {
+ printk(KERN_WARNING
+ "caifdev: err: %d, can't register misc.\n", result);
+ goto err_misc_register_failed;
+ }
+
+ /* Register class for SYSFS. */
+ result = class_register(&caif_class);
+ if (unlikely(result)) {
+ printk(KERN_WARNING
+ "caifdev: err: %d, can't create sysfs node.\n", result);
+ goto err_class_register_failed;
+ }
+
+ /* Create SYSFS nodes. */
+ result = class_create_file(&caif_class, &class_attr_dbg_lvl);
+ if (unlikely(result)) {
+ printk(KERN_WARNING
+ "caifdev: err: %d, can't create sysfs node.\n", result);
+ goto err_sysfs_create_failed;
+ }
+
+ return result;
+
+err_sysfs_create_failed:
+ class_unregister(&caif_class);
+err_class_register_failed:
+ misc_deregister(&caifdev.misc);
+err_misc_register_failed:
+err_cfcnfg_create_failed:
+ return -ENODEV;
+}
+
+int caifdev_phy_register(layer_t *phyif, cfcnfg_phy_type_t phy_type,
+ cfcnfg_phy_preference_t phy_pref)
+{
+ uint16 phyid;
+
+ /* Hook up the physical interface.
+ * Right now we are not using the returned id. */
+ cfcnfg_add_phy_layer(caifdev.cfg, phy_type, phyif, &phyid, phy_pref);
+ printk(KERN_WARNING "caifdev_phy_register: phy:%p id:%d\n",
+ phyif, phyid);
+
+ printk(KERN_WARNING "caifdev_phy_register: %p ID %d == %d\n",
+ (void *)phyif, phyid, phyif->id);
+ return 0;
+}
+EXPORT_SYMBOL(caifdev_phy_register);
+
+int caifdev_phy_unregister(layer_t *phyif)
+{
+ printk(KERN_WARNING "caifdev_phy_unregister: phy:%p id:%d\n",
+ phyif, phyif->id);
+ cfcnfg_del_phy_layer(caifdev.cfg, phyif);
+ return 0;
+}
+EXPORT_SYMBOL(caifdev_phy_unregister);
+
+
+int caifdev_phy_loop_register(layer_t *phyif, cfcnfg_phy_type_t phy_type)
+{
+ /* Create the loop stack. */
+ caifdev.loop = cfloopcfg_create();
+
+ /* Hook up the loop layer. */
+ cfloopcfg_add_phy_layer(caifdev.loop, phy_type, phyif);
+
+ return 0;
+}
+EXPORT_SYMBOL(caifdev_phy_loop_register);
+
+int caifdev_phy_spi_xmitlen(cfspil_t *layr)
+{
+ return cfspil_xmitlen(layr);
+}
+EXPORT_SYMBOL(caifdev_phy_spi_xmitlen);
+
+cfpkt_t *caifdev_phy_spi_getxmitpkt(cfspil_t *layr)
+{
+ return cfspil_getxmitpkt(layr);
+}
+EXPORT_SYMBOL(caifdev_phy_spi_getxmitpkt);
+
+/* FIXME: Generally comment on the new
+ * and old fashion functions for phy registration.
+ */
+
+int caifdev_adapt_register(struct caif_channel_config *config,
+ layer_t *adap_layer)
+{
+ cfctrl_link_param_t param;
+
+ if (channel_config_2_link_param(caifdev.cfg, config, ¶m) == 0)
+ /* Hook up the adaptation layer. */
+ if (cfcnfg_add_adaptation_layer(caifdev.cfg,
+ ¶m, adap_layer))
+ return 0;
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(caifdev_adapt_register);
+
+
+/* FIXME: Comment on this function being new and not yet in use */
+int caifdev_adapt_unregister(layer_t *adap_layer)
+{
+ if (cfcnfg_del_adapt_layer(caifdev.cfg, adap_layer))
+ return 0;
+ else
+ return -1;
+}
+EXPORT_SYMBOL(caifdev_adapt_unregister);
+
+int caif_register_chrdev(int (*chrdev_mgmt)
+ (int action, union caif_action *param))
+{
+
+ chrdev_mgmt_func = chrdev_mgmt;
+ return 0;
+}
+EXPORT_SYMBOL(caif_register_chrdev);
+
+int caif_register_netdev(int (*netdev_mgmt)
+ (int action, union caif_action *param))
+{
+ netdev_mgmt_func = netdev_mgmt;
+ return 0;
+}
+EXPORT_SYMBOL(caif_register_netdev);
+
+
+void caif_unregister_chrdev()
+{
+ chrdev_mgmt_func = NULL;
+}
+EXPORT_SYMBOL(caif_unregister_chrdev);
+
+void caif_unregister_netdev()
+{
+ netdev_mgmt_func = NULL;
+}
+EXPORT_SYMBOL(caif_unregister_netdev);
+
+/* Exported CAIF functions. */
+EXPORT_SYMBOL(cfcnfg_get_packet_funcs);
+
+
+extern int serial_use_stx;
+EXPORT_SYMBOL(serial_use_stx);
+
+extern cfglu_atomic_t cfpkt_packet_count;
+EXPORT_SYMBOL(cfpkt_packet_count);
+module_exit(caifdev_exit_module);
+module_init(caifdev_init_module);
diff --git a/net/caif/caif_config_util.c b/net/caif/caif_config_util.c
new file mode 100644
index 0000000..093d1be
--- /dev/null
+++ b/net/caif/caif_config_util.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2009
+ *
+ * Author: Daniel Martensson / Daniel.Martensson@stericsson.com
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ *
+ */
+
+#include "cfglue.h"
+#include "cfctrl.h"
+#include "cfcnfg.h"
+#include "caif_config_util.h"
+#include "caif_config.h"
+#include "caif_ioctl.h"
+#include "caif_actions.h"
+
+
+
+int
+channel_config_2_link_param(cfcnfg_t *cnfg, struct caif_channel_config *s,
+ cfctrl_link_param_t *l)
+{
+
+ cfcnfg_phy_preference_t pref;
+
+ memset(l, 0, sizeof(*l));
+
+ l->priority = s->priority;
+
+ if (s->phy_name[0] != '\0') {
+ l->phyid = cfcnfg_get_named(cnfg, s->phy_name);
+ CFLOG_TRACE(("PHYNAME: '%s' -> id:%d\n", s->phy_name,
+ l->phyid));
+
+ } else {
+ switch (s->phy_pref) {
+ case CAIF_PHYPREF_UNSPECIFIED:
+ pref = CFPHYPREF_UNSPECIFIED;
+ break;
+ case CAIF_PHYPREF_LOW_LAT:
+ pref = CFPHYPREF_LOW_LAT;
+ break;
+ case CAIF_PHYPREF_HIGH_BW:
+ pref = CFPHYPREF_HIGH_BW;
+ break;
+ case _CAIF_PHYPREF_LOOP:
+ pref = CFPHYPREF_LOOP;
+ break;
+ case _CAIF_PHYPREF_RAW_LOOP:
+ pref = CFPHYPREF_RAW_LOOP;
+ break;
+ default:
+ CFLOG_TRACE(("PHYPREF: "
+ "No Preferered Device Specified '%d'\n",
+ s->phy_pref));
+ return -CFGLU_ENODEV;
+ }
+ l->phyid = cfcnfg_get_phyid(cnfg, pref);
+ CFLOG_TRACE(("PHYPREF: '%d' -> id:%d\n", pref, l->phyid));
+ }
+
+ switch (s->type) {
+ case CAIF_CHTY_AT:
+ l->linktype = CFCTRL_SRV_VEI;
+ l->chtype = 0x02;
+ l->endpoint = 0x00;
+ CFLOG_TRACE(("CHTY: AT\n"));
+ break;
+
+ case CAIF_CHTY_AT_CTRL:
+ CFLOG_TRACE(("CHTY: AT_CTRL\n"));
+ l->linktype = CFCTRL_SRV_VEI;
+ l->chtype = 0x00;
+ l->endpoint = 0x00;
+ break;
+
+ case CAIF_CHTY_AT_PAIRED:
+ CFLOG_TRACE(("CHTY: AT_PAIRED\n"));
+ l->linktype = CFCTRL_SRV_VEI;
+ l->chtype = 0x03;
+ l->endpoint = 0x00;
+ break;
+
+ case CAIF_CHTY_VIDEO:
+ CFLOG_TRACE(("CHTY: VIDEO\n"));
+ l->linktype = CFCTRL_SRV_VIDEO;
+ l->chtype = 0x00;
+ l->endpoint = 0x00;
+ l->u.video.connid = s->u.dgm.connection_id;
+ break;
+
+ case CAIF_CHTY_DATAGRAM:
+ CFLOG_TRACE(("CHTY: DATAGRAM\n"));
+ l->linktype = CFCTRL_SRV_DATAGRAM;
+ l->chtype = 0x00;
+ l->u.datagram.connid = s->u.dgm.connection_id;
+ break;
+
+ case CAIF_CHTY_DATAGRAM_LOOP:
+ CFLOG_TRACE(("CHTY: DATAGRAM\n"));
+ l->linktype = CFCTRL_SRV_DATAGRAM;
+ l->chtype = 0x03;
+ l->endpoint = 0x00;
+ l->u.datagram.connid = s->u.dgm.connection_id;
+ break;
+
+ case CAIF_CHTY_DEBUG:
+ CFLOG_TRACE(("CHTY: DEBUG\n"));
+ l->linktype = CFCTRL_SRV_DBG;
+ l->endpoint = 0x01; /* ACC SIDE */
+ l->chtype = 0x00; /* Single channel with interactive
+ debug and print-out mixed */
+ break;
+
+ case CAIF_CHTY_DEBUG_INTERACT:
+ CFLOG_TRACE(("CHTY: IDEBUG\n"));
+ l->linktype = CFCTRL_SRV_DBG;
+ l->endpoint = 0x01; /* ACC SIDE */
+ l->chtype = 0x02; /* Interactive debug only */
+ break;
+
+ case CAIF_CHTY_DEBUG_TRACE:
+ CFLOG_TRACE(("CHTY: TRACE\n"));
+ l->linktype = CFCTRL_SRV_DBG;
+ l->endpoint = 0x01; /* ACC SIDE */
+ l->chtype = 0x01; /* Debug print-out only */
+ break;
+
+ case CAIF_CHTY_RFM:
+
+ CFLOG_TRACE(("CHTY: RFN\n"));
+ l->linktype = CFCTRL_SRV_RFM;
+ l->u.datagram.connid = s->u.rfm.connection_id;
+ strncpy(l->u.rfm.volume, s->u.rfm.volume,
+ sizeof(l->u.rfm.volume));
+ break;
+
+ case CAIF_CHTY_UTILITY:
+ CFLOG_TRACE(("CHTY: UTILTY\n"));
+ l->linktype = CFCTRL_SRV_UTIL;
+ l->endpoint = 0x00;
+ l->chtype = 0x00;
+ l->u.utility.fifosize_bufs = s->u.utility.fifosize_bufs;
+ l->u.utility.fifosize_kb = s->u.utility.fifosize_kb;
+ strncpy(l->u.utility.name, s->u.utility.name,
+ sizeof(l->u.utility.name));
+ l->u.utility.paramlen = s->u.utility.paramlen;
+ if (l->u.utility.paramlen > sizeof(l->u.utility.params))
+ l->u.utility.paramlen = sizeof(l->u.utility.params);
+ memcpy(l->u.utility.params, s->u.utility.params,
+ l->u.utility.paramlen);
+ break;
+
+ case CAIF_CHTY_RAW:
+ l->linktype = s->u._raw.channeltype;
+ l->endpoint = s->u._raw.endpoint;
+ l->chtype = s->u._raw.subtype;
+ break;
+
+ default:
+ CFLOG_TRACE(("CAIF_CHTY: Specified bad channel type '%d'\n",
+ s->type));
+ return -CFGLU_EINVAL;
+ }
+ return 0;
+}
diff --git a/net/caif/chnl_chr.c b/net/caif/chnl_chr.c
new file mode 100644
index 0000000..b45e93c
--- /dev/null
+++ b/net/caif/chnl_chr.c
@@ -0,0 +1,1161 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Per Sigmond / Per.Sigmond@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/poll.h>
+#include <linux/uaccess.h>
+#include <asm/atomic.h>
+
+/* Caif header files. */
+#include "caif_layer.h"
+#include "cfcnfg.h"
+#include "cfpkt.h"
+#include "cffrml.h"
+#include "caif_chr.h"
+#include "caif_config.h"
+#include "caif_config_util.h"
+#include "caif_actions.h"
+#include "caif_log.h"
+MODULE_LICENSE("GPL");
+
+#define COUNTER_DEBUG 0
+
+/* Seconds */
+#define CAIF_CONNECT_TIMEOUT 30
+
+#define CHNL_CHR_READ_QUEUE_HIGH 2000
+#define CHNL_CHR_READ_QUEUE_LOW 100
+
+static LIST_HEAD(caif_chrdev_list);
+static spinlock_t list_lock;
+
+#define STATE_IS_OPEN_BIT 1
+#define STATE_OPEN_PENDING_BIT 2
+#define STATE_IS_CLOSED_BIT 3
+#define STATE_CLOSE_PENDING_BIT 4
+#define STATE_TX_FLOW_ON 5
+#define STATE_RX_FLOW_OFF 6
+
+
+
+#define CHR_READ_FLAG 0x01
+#define CHR_WRITE_FLAG 0x02
+
+#define chnl_assert(assert) BUG_ON(!(assert))
+
+
+struct caif_char_dev {
+ layer_t layer;
+ unsigned int status;
+ cfpktq_t *pktq;
+ char name[256]; /* Redundnt! Already in struct miscdevice */
+ struct miscdevice misc;
+ int file_mode;
+ caif_packet_funcs_t pktf;
+ struct caif_channel_config config;
+ /* Access to this struct and below layers */
+ struct mutex mutex;
+ int read_queue_len;
+ spinlock_t read_queue_len_lock;
+ wait_queue_head_t read_wq;
+ wait_queue_head_t mgmt_wq;
+ /* List of misc test devices */
+ struct list_head list_field;
+#if COUNTER_DEBUG
+ unsigned long counter;
+ int mismatch_reported;
+#endif
+
+};
+
+/** Packet Receive Callback function called from CAIF Stack */
+
+static int caif_chrrecv_cb(layer_t *layr, cfpkt_t *pkt)
+{
+ struct caif_char_dev *dev;
+#if COUNTER_DEBUG
+ unsigned long *data_p;
+#endif
+
+ CAIFLOG_ENTER("");
+ dev = container_of(layr, struct caif_char_dev, layer);
+
+ CAIFLOG_TRACE2("[%s] data received: %d bytes.\n",
+ __func__, dev->pktf.cfpkt_getlen(pkt));
+
+ /** NOTE: This function may be called in Tasklet context! */
+
+
+
+#if COUNTER_DEBUG
+ dev->pktf.cfpkt_raw_extract(pkt, (void **) &data_p, 0);
+
+ if (data_p[0] == 1) {
+ dev->counter = data_p[0];
+ dev->mismatch_reported = 0;
+ }
+
+ if ((dev->counter != data_p[0]) && !dev->mismatch_reported) {
+ CAIFLOG_TRACE
+ ("WARNING: caif_chrrecv_cb() - " "sequence: expected "
+ "%ld, got %ld\n", dev->counter, data_p[0]);
+ dev->mismatch_reported = 1;
+ }
+
+ if (!(dev->counter % 100000))
+ CAIFLOG_TRACE("caif_chrrecv_cb(): %ld\n", dev->counter);
+
+
+ dev->counter++;
+#endif
+
+ /* The queue has its own lock */
+ dev->pktf.cfpkt_queue(dev->pktq, pkt, 0);
+ spin_lock(&dev->read_queue_len_lock);
+ dev->read_queue_len++;
+
+ if (!test_bit(STATE_RX_FLOW_OFF, (void *) &dev->status) &&
+ (dev->read_queue_len > CHNL_CHR_READ_QUEUE_HIGH)) {
+ set_bit(STATE_RX_FLOW_OFF, (void *) &dev->status);
+
+ /* Todo: send flow off (NOTE: must not sleep) */
+ CAIFLOG_TRACE2
+ ("caif_chrrecv_cb(): sending flow OFF (queue len = %d)\n",
+ dev->read_queue_len);
+ chnl_assert(dev->layer.dn);
+ chnl_assert(dev->layer.dn->ctrlcmd);
+ (void) dev->layer.dn->modemcmd(dev->layer.dn,
+ CAIF_MODEMCMD_FLOW_OFF_REQ);
+ }
+ spin_unlock(&dev->read_queue_len_lock);
+
+ /* Signal reader that data is available. */
+ wake_up_interruptible(&dev->read_wq);
+
+ return 0;
+}
+
+/** Packet Flow Control Callback function called from CAIF */
+static void caif_chrflowctrl_cb(layer_t *layr, caif_ctrlcmd_t flow, int phyid)
+{
+ struct caif_char_dev *dev;
+
+ CAIFLOG_ENTER("");
+
+ /** NOTE: This function may be called in Tasklet context! */
+ CAIFLOG_TRACE("AT flowctrl func called flow: %s.\n",
+ flow == CAIF_CTRLCMD_FLOW_ON_IND ? "ON" :
+ flow == CAIF_CTRLCMD_FLOW_OFF_IND ? "OFF" :
+ flow == CAIF_CTRLCMD_INIT_RSP ? "INIT_RSP" :
+ flow == CAIF_CTRLCMD_DEINIT_RSP ? "DEINIT_RSP" :
+ flow == CAIF_CTRLCMD_INIT_FAIL_RSP ? "INIT_FAIL_RSP" :
+ flow ==
+ CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND ? "REMOTE_SHUTDOWN" :
+ "UKNOWN CTRL COMMAND");
+
+ dev = container_of(layr, struct caif_char_dev, layer);
+
+ switch (flow) {
+ case CAIF_CTRLCMD_FLOW_ON_IND:
+ CAIFLOG_TRACE("[%s:%d] CAIF_CTRLCMD_FLOW_ON_IND\n",
+ __func__, __LINE__);
+ /* Signal reader that data is available. */
+ set_bit(STATE_TX_FLOW_ON, (void *) &dev->status);
+ wake_up_interruptible(&dev->mgmt_wq);
+ break;
+
+ case CAIF_CTRLCMD_FLOW_OFF_IND:
+ CAIFLOG_TRACE("[%s:%d] CAIF_CTRLCMD_FLOW_OFF_IND\n",
+ __func__, __LINE__);
+ clear_bit(STATE_TX_FLOW_ON, (void *) &dev->status);
+ break;
+
+ case CAIF_CTRLCMD_INIT_RSP:
+ CAIFLOG_TRACE("[%s:%d] CAIF_CTRLCMD_INIT_RSP\n",
+ __func__, __LINE__);
+ /* Signal reader that data is available. */
+ set_bit(STATE_TX_FLOW_ON, (void *) &dev->status);
+ set_bit(STATE_IS_OPEN_BIT, (void *) &dev->status);
+ clear_bit(STATE_OPEN_PENDING_BIT, (void *) &dev->status);
+ wake_up_interruptible(&dev->mgmt_wq);
+ break;
+
+ case CAIF_CTRLCMD_DEINIT_RSP:
+ CAIFLOG_TRACE("[%s:%d] CAIF_CTRLCMD_DEINIT_RSP\n",
+ __func__, __LINE__);
+ clear_bit(STATE_TX_FLOW_ON, (void *) &dev->status);
+ set_bit(STATE_IS_CLOSED_BIT, (void *) &dev->status);
+ clear_bit(STATE_CLOSE_PENDING_BIT, (void *) &dev->status);
+ wake_up_interruptible(&dev->mgmt_wq);
+ break;
+
+ case CAIF_CTRLCMD_INIT_FAIL_RSP:
+ CAIFLOG_TRACE("[%s:%d] CAIF_CTRLCMD_INIT_FAIL_RSP\n",
+ __func__, __LINE__);
+ clear_bit(STATE_TX_FLOW_ON, (void *) &dev->status);
+ set_bit(STATE_IS_CLOSED_BIT, (void *) &dev->status);
+ clear_bit(STATE_OPEN_PENDING_BIT, (void *) &dev->status);
+ wake_up_interruptible(&dev->mgmt_wq);
+ break;
+
+ case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND:
+ CAIFLOG_TRACE("[%s:%d] CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND\n",
+ __func__, __LINE__);
+ clear_bit(STATE_TX_FLOW_ON, (void *) &dev->status);
+ set_bit(STATE_IS_CLOSED_BIT, (void *) &dev->status);
+ clear_bit(STATE_CLOSE_PENDING_BIT, (void *) &dev->status);
+ clear_bit(STATE_OPEN_PENDING_BIT, (void *) &dev->status);
+ wake_up_interruptible(&dev->mgmt_wq);
+ break;
+
+ default:
+ CAIFLOG_TRACE("[%s:%d] Unexpected flow command %d\n",
+ __func__, __LINE__, flow);
+
+ }
+}
+
+/** Device Read function called from Linux Kernel */
+ssize_t caif_chrread(struct file *filp, char __user *buf, size_t count,
+ loff_t *f_pos)
+{
+ cfpkt_t *pkt = NULL;
+ unsigned char *rxbuf = NULL;
+ size_t len;
+ int result;
+ struct caif_char_dev *dev = filp->private_data;
+ ssize_t ret = -EIO;
+
+ CAIFLOG_ENTER("");
+
+ if (dev == NULL) {
+ CAIFLOG_TRACE("[%s:%d] private_data not set!\n",
+ __func__, __LINE__);
+ return -EBADFD;
+ }
+
+
+ /* I want to be alone on dev (except status and queue) */
+ if (mutex_lock_interruptible(&dev->mutex)) {
+ CAIFLOG_TRACE
+ ("caif_chrread: mutex_lock_interruptible got signalled\n");
+ return -ERESTARTSYS;
+ }
+
+ chnl_assert(dev->pktq);
+
+ if (!test_bit(STATE_IS_OPEN_BIT, (void *) &dev->status)) {
+ CAIFLOG_TRACE("[%s:%d] device not open\n", __func__, __LINE__);
+ ret = -EINVAL;
+ goto read_error;
+ }
+
+ if (test_bit(STATE_IS_CLOSED_BIT, (void *) &dev->status)) {
+ CAIFLOG_TRACE("[%s:%d] remote end shutdown!\n",
+ __func__, __LINE__);
+ ret = -EPIPE;
+ goto read_error;
+ }
+
+ if (test_bit(STATE_CLOSE_PENDING_BIT, (void *) &dev->status)) {
+ CAIFLOG_TRACE("[%s:%d] device is closing...\n",
+ __func__, __LINE__);
+ ret = -EPIPE;
+ goto read_error;
+ }
+
+
+ /* Block if we don't have any received buffers. */
+ /* The queue has its own lock */
+ while ((pkt = dev->pktf.cfpkt_qpeek(dev->pktq)) == NULL) {
+
+ if (filp->f_flags & O_NONBLOCK) {
+ CAIFLOG_TRACE("caif_chrread: O_NONBLOCK\n");
+ ret = -EAGAIN;
+ goto read_error;
+ }
+ CAIFLOG_TRACE2("[%s:%d] wait_event\n", __func__, __LINE__);
+
+ /* Let writers in */
+ mutex_unlock(&dev->mutex);
+
+ /* Block reader until data arrives. */
+ if (wait_event_interruptible(dev->read_wq,
+ dev->pktf.cfpkt_qpeek(dev->pktq)
+ || test_bit(STATE_IS_CLOSED_BIT,
+ (void *)
+ &dev->status)) ==
+ -ERESTARTSYS) {
+ CAIFLOG_TRACE
+ ("caif_chrread: wait_event_interruptible woken by "
+ "a signal, signal_pending(current) = %d\n",
+ signal_pending(current));
+ return -ERESTARTSYS;
+ }
+
+ CAIFLOG_TRACE2("[%s:%d] awake\n", __func__, __LINE__);
+
+ /* I want to be alone on dev (except status and queue) */
+ if (mutex_lock_interruptible(&dev->mutex)) {
+ CAIFLOG_TRACE
+ ("caif_chrread: "
+ "mutex_lock_interruptible got signalled\n");
+ return -ERESTARTSYS;
+ }
+
+ if (test_bit(STATE_IS_CLOSED_BIT, (void *) &dev->status)) {
+ /* someone closed the link, report error */
+ CAIFLOG_TRACE("[%s:%d] remote end shutdown!\n",
+ __func__, __LINE__);
+ ret = -EPIPE;
+ goto read_error;
+ }
+
+ }
+
+ /* The queue has its own lock */
+ len = dev->pktf.cfpkt_getlen(pkt);
+
+ /* Check max length that can be copied. */
+ if (len > count) {
+ CAIFLOG_TRACE("caif_chrread: user buffer too small\n");
+ ret = -EINVAL;
+ goto read_error;
+ }
+
+ /* Get packet from queue */
+ /* The queue has its own lock */
+ pkt = dev->pktf.cfpkt_dequeue(dev->pktq);
+
+ spin_lock(&dev->read_queue_len_lock);
+ dev->read_queue_len--;
+ if (test_bit(STATE_RX_FLOW_OFF, (void *) &dev->status) &&
+ (dev->read_queue_len < CHNL_CHR_READ_QUEUE_LOW)) {
+
+ clear_bit(STATE_RX_FLOW_OFF, (void *) &dev->status);
+
+ /* Todo: send flow on */
+ CAIFLOG_TRACE2
+ ("caif_chrread(): sending flow ON (queue len = %d)\n",
+ dev->read_queue_len);
+ chnl_assert(dev->layer.dn);
+ chnl_assert(dev->layer.dn->ctrlcmd);
+ (void) dev->layer.dn->modemcmd(dev->layer.dn,
+ CAIF_MODEMCMD_FLOW_ON_REQ);
+
+ chnl_assert(dev->read_queue_len >= 0);
+
+ }
+ spin_unlock(&dev->read_queue_len_lock);
+
+ result = dev->pktf.cfpkt_raw_extract(pkt, (void **) &rxbuf, len);
+
+ chnl_assert(result >= 0);
+
+ if (result < 0) {
+ CAIFLOG_TRACE("caif_chrread: cfpkt_raw_extract failed\n");
+ dev->pktf.cfpkt_destroy(pkt);
+ ret = -EINVAL;
+ goto read_error;
+ }
+
+ /* Copy data from the RX buffer to the user buffer. */
+ if (copy_to_user(buf, rxbuf, len)) {
+ CAIFLOG_TRACE("caif_chrread: copy_to_user returned non zero.");
+ dev->pktf.cfpkt_destroy(pkt);
+ ret = -EINVAL;
+ goto read_error;
+ }
+
+
+ /* Liberate packet. */
+ dev->pktf.cfpkt_destroy(pkt);
+
+
+ /* Let the others in */
+ mutex_unlock(&dev->mutex);
+ CAIFLOG_EXIT("");
+ return len;
+
+read_error:
+ mutex_unlock(&dev->mutex);
+ CAIFLOG_EXIT("");
+ return ret;
+}
+
+/** Device write function called from Linux Kernel (misc device) */
+ssize_t caif_chrwrite(struct file *filp, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ cfpkt_t *pkt = NULL;
+ struct caif_char_dev *dev = filp->private_data;
+ transmt_info info;
+ unsigned char *txbuf;
+ ssize_t ret = -EIO;
+ CAIFLOG_ENTER("");
+
+ if (dev == NULL) {
+ CAIFLOG_TRACE("[%s:%d] private_data not set!\n",
+ __func__, __LINE__);
+ ret = -EBADFD;
+ goto write_error_no_unlock;
+ }
+
+
+ if (count > CAIF_MAX_PAYLOAD_SIZE) {
+ CAIFLOG_TRACE("[%s:%d] buffer too long\n", __func__, __LINE__);
+ ret = -EINVAL;
+ goto write_error_no_unlock;
+ }
+
+ /* I want to be alone on dev (except status and queue) */
+ if (mutex_lock_interruptible(&dev->mutex)) {
+ CAIFLOG_TRACE
+ ("caif_chrwrite: mutex_lock_interruptible got signalled");
+ ret = -ERESTARTSYS;
+ goto write_error_no_unlock;
+ }
+
+ chnl_assert(dev->pktq);
+
+ if (!test_bit(STATE_IS_OPEN_BIT, (void *) &dev->status)) {
+ CAIFLOG_TRACE("[%s:%d] device not open", __func__, __LINE__);
+ ret = -EINVAL;
+ goto write_error;
+ }
+
+ if (test_bit(STATE_IS_CLOSED_BIT, (void *) &dev->status)) {
+ CAIFLOG_TRACE("[%s:%d] remote end shutdown!",
+ __func__, __LINE__);
+ ret = -EPIPE;
+ goto write_error;
+ }
+
+ if (test_bit(STATE_CLOSE_PENDING_BIT, (void *) &dev->status)) {
+ CAIFLOG_TRACE("[%s:%d] device is closing...",
+ __func__, __LINE__);
+ ret = -EPIPE;
+ goto write_error;
+ }
+
+ if (!test_bit(STATE_TX_FLOW_ON, (void *) &dev->status) &&
+ (filp->f_flags & O_NONBLOCK)) {
+ CAIFLOG_TRACE("caif_chrwrite: O_NONBLOCK");
+ ret = -EAGAIN;
+ goto write_error;
+ }
+
+ /* Let readers in */
+ mutex_unlock(&dev->mutex);
+
+ if (wait_event_interruptible(dev->mgmt_wq,
+ test_bit(STATE_TX_FLOW_ON,
+ (void *) &dev->status)
+ || test_bit(STATE_IS_CLOSED_BIT,
+ (void *) &dev->status)) ==
+ -ERESTARTSYS) {
+ CAIFLOG_TRACE
+ ("caif_chrwrite:"
+ " wait_event_interruptible woken by a signal");
+ ret = -ERESTARTSYS;
+ goto write_error_no_unlock;
+ }
+
+ /* I want to be alone on dev (except status and queue) */
+ if (mutex_lock_interruptible(&dev->mutex)) {
+ CAIFLOG_TRACE
+ ("caif_chrwrite: mutex_lock_interruptible got signalled\n");
+ ret = -ERESTARTSYS;
+ goto write_error_no_unlock;
+ }
+
+ if (test_bit(STATE_IS_CLOSED_BIT, (void *) &dev->status)) {
+ /* someone closed the link, report error */
+ CAIFLOG_TRACE("[%s:%d] remote end shutdown!\n",
+ __func__, __LINE__);
+ ret = -EPIPE;
+ goto write_error;
+ }
+
+ /* Create packet, buf=NULL means no copying */
+ pkt = dev->pktf.cfpkt_create_xmit_pkt((const unsigned char *) NULL,
+ count);
+
+ if (pkt == NULL) {
+ CAIFLOG_TRACE
+ ("caif_chrwrite: cfpkt_create_pkt returned NULL\n");
+ ret = -EIO;
+ goto write_error;
+ }
+
+ if (!dev->pktf.cfpkt_raw_append(pkt, (void **) &txbuf, count)) {
+ CAIFLOG_TRACE("caif_chrwrite: cfpkt_raw_append failed\n");
+ dev->pktf.cfpkt_destroy(pkt);
+ ret = -EINVAL;
+ goto write_error;
+ }
+
+ /* Copy data into buffer. */
+
+ if (copy_from_user(txbuf, buf, count)) {
+ CAIFLOG_TRACE
+ ("caif_chrwrite: copy_from_user returned non zero.\n");
+ dev->pktf.cfpkt_destroy(pkt);
+ ret = -EINVAL;
+ goto write_error;
+ }
+
+ memset(&info, 0, sizeof(info));
+
+ /* Send the packet down the stack. */
+ chnl_assert(dev->layer.dn);
+ chnl_assert(dev->layer.dn->transmit);
+
+ ret = dev->layer.dn->transmit(dev->layer.dn, &info, pkt);
+ if (ret < 0) {
+ dev->pktf.cfpkt_destroy(pkt);
+ CAIFLOG_TRACE("caif_chrwrite: transmit failed, error = %d\n",
+ ret);
+ goto write_error;
+ }
+
+
+ mutex_unlock(&dev->mutex);
+ CAIFLOG_EXIT("");
+ return count;
+
+write_error:
+ mutex_unlock(&dev->mutex);
+write_error_no_unlock:
+ CAIFLOG_EXIT("");
+ return ret;
+}
+
+
+static unsigned int caif_chrpoll(struct file *filp, poll_table *waittab)
+{
+ struct caif_char_dev *dev = filp->private_data;
+ unsigned int mask = 0;
+
+ CAIFLOG_ENTER("");
+
+ if (dev == NULL) {
+ CAIFLOG_TRACE("[%s:%d] private_data not set!\n",
+ __func__, __LINE__);
+ return -EBADFD;
+ }
+
+
+ /* I want to be alone on dev (except status and queue) */
+ if (mutex_lock_interruptible(&dev->mutex)) {
+ CAIFLOG_TRACE
+ ("caif_chrpoll: mutex_lock_interruptible got signalled\n");
+ goto poll_error;
+ }
+
+ chnl_assert(dev->pktq);
+
+ if (!test_bit(STATE_IS_OPEN_BIT, (void *) &dev->status)) {
+ CAIFLOG_TRACE("[%s:%d] device not open\n", __func__, __LINE__);
+ goto poll_error;
+ }
+
+ if (test_bit(STATE_IS_CLOSED_BIT, (void *) &dev->status)) {
+ CAIFLOG_TRACE("[%s:%d] remote end shutdown!\n",
+ __func__, __LINE__);
+ goto poll_error;
+ }
+
+ if (test_bit(STATE_CLOSE_PENDING_BIT, (void *) &dev->status)) {
+ CAIFLOG_TRACE("[%s:%d] device is closing...\n",
+ __func__, __LINE__);
+ goto poll_error;
+ }
+
+ poll_wait(filp, &dev->read_wq, waittab);
+
+ if (dev->pktf.cfpkt_qpeek(dev->pktq) != NULL)
+ mask |= (POLLIN | POLLRDNORM);
+
+
+ if (test_bit(STATE_TX_FLOW_ON, (void *) &dev->status))
+ mask |= (POLLOUT | POLLWRNORM);
+
+
+ mutex_unlock(&dev->mutex);
+ CAIFLOG_TRACE("[%s:%d] caif_chrpoll mask=0x%04x...\n",
+ __func__, __LINE__, mask);
+
+ CAIFLOG_EXIT("");
+ return mask;
+
+poll_error:
+ mask |= POLLERR;
+ mutex_unlock(&dev->mutex);
+ CAIFLOG_EXIT("");
+ return mask;
+}
+
+/* Usage:
+ minor >= 0 : find from minor
+ minor < 0 and name == name : find from name
+ minor < 0 and name == NULL : get first
+*/
+
+static struct caif_char_dev *find_device(int minor, char *name,
+ int remove_from_list)
+{
+ struct list_head *list_node;
+ struct list_head *n;
+ struct caif_char_dev *dev = NULL;
+ struct caif_char_dev *tmp;
+ CAIFLOG_ENTER("");
+ spin_lock(&list_lock);
+ CAIFLOG_TRACE("[%s:%d] start looping \n", __func__, __LINE__);
+ list_for_each_safe(list_node, n, &caif_chrdev_list) {
+ tmp = list_entry(list_node, struct caif_char_dev, list_field);
+ CAIFLOG_TRACE("[%s:%d] check %d,%d, %s, %s \n",
+ __func__, __LINE__, tmp->misc.minor, minor,
+ tmp->name, name);
+ if (minor >= 0) { /* find from minor */
+ if (tmp->misc.minor == minor)
+ dev = tmp;
+
+ } else if (name) { /* find from name */
+ if (!strncmp(tmp->name, name, sizeof(tmp->name)))
+ dev = tmp;
+ } else { /* take first */
+ dev = tmp;
+ }
+
+ if (dev) {
+ CAIFLOG_TRACE("[%s:%d] match %d, %s \n",
+ __func__, __LINE__, minor, name);
+ if (remove_from_list)
+ list_del(list_node);
+ break;
+ }
+ }
+ spin_unlock(&list_lock);
+ return dev;
+}
+
+#if CAIFLOG_ON
+static void print_device_list(void)
+{
+ int i = 0;
+ struct list_head *list_node;
+ struct caif_char_dev *tmp;
+ CAIFLOG_ENTER("");
+ spin_lock(&list_lock);
+ CAIFLOG_TRACE("List of devices:\n");
+ list_for_each(list_node, &caif_chrdev_list) {
+ tmp = list_entry(list_node, struct caif_char_dev, list_field);
+ CAIFLOG_TRACE("i = %d, minor= %d, name = %s, open = %d\n", i,
+ tmp->misc.minor, tmp->name,
+ test_bit(STATE_IS_OPEN_BIT,
+ (void *) &tmp->status) ? 1 : 0);
+ i++;
+ }
+ if (i == 0)
+ CAIFLOG_TRACE("list is empty\n");
+
+ spin_unlock(&list_lock);
+}
+#endif
+
+
+static void drain_queue(struct caif_char_dev *dev)
+{
+ cfpkt_t *pkt;
+
+ /* Empty the queue */
+ do {
+ /* The queue has its own lock */
+ pkt = dev->pktf.cfpkt_dequeue(dev->pktq);
+
+ if (!pkt)
+ break;
+
+ CAIFLOG_TRACE
+ ("[%s:%d] drain_queue(): freeing packet from read queue\n",
+ __func__, __LINE__);
+ dev->pktf.cfpkt_destroy(pkt);
+
+ } while (1);
+}
+
+
+int caif_chropen(struct inode *inode, struct file *filp)
+{
+ struct caif_char_dev *dev = NULL;
+ int result = -1;
+ int minor = iminor(inode);
+ int mode = 0;
+ int ret = -EIO;
+
+ CAIFLOG_ENTER("");
+
+ IF_CAIF_TRACE(print_device_list());
+
+ dev = find_device(minor, NULL, 0);
+
+ if (dev == NULL) {
+ CAIFLOG_TRACE("[%s] COULD NOT FIND DEVICE\n", __func__);
+ return -EBADF;
+ }
+
+ CAIFLOG_TRACE("[%s:%d] dev=%p \n", __func__, __LINE__, dev);
+
+ /* I want to be alone on dev (except status and queue) */
+ if (mutex_lock_interruptible(&dev->mutex)) {
+ CAIFLOG_TRACE
+ ("caif_chropen: mutex_lock_interruptible got signalled\n");
+ return -ERESTARTSYS;
+ }
+
+ filp->private_data = dev;
+
+ switch (filp->f_flags & O_ACCMODE) {
+ case O_RDONLY:
+ mode = CHR_READ_FLAG;
+ break;
+ case O_WRONLY:
+ mode = CHR_WRITE_FLAG;
+ break;
+ case O_RDWR:
+ mode = CHR_READ_FLAG | CHR_WRITE_FLAG;
+ break;
+ }
+
+ /* If close is pending we need to wait for its conclusion */
+ result = wait_event_interruptible_timeout(dev->mgmt_wq,
+ !test_bit
+ (STATE_CLOSE_PENDING_BIT,
+ (void *) &dev->status),
+ CAIF_CONNECT_TIMEOUT * HZ);
+ if (result == -ERESTARTSYS) {
+ CAIFLOG_TRACE
+ ("caif_chropen:"
+ " wait_event_interruptible woken by a signal (1)");
+ ret = -ERESTARTSYS;
+ goto open_error;
+ } else if (result == 0) {
+ /* Timed out */
+ /* Timeout */
+ CAIFLOG_TRACE("caif_chropen: close pending timed out\n");
+ ret = -ETIMEDOUT;
+ goto open_error;
+ }
+
+ if (!test_bit(STATE_IS_OPEN_BIT, (void *) &dev->status)) {
+
+ /* Test if we have already registered the channel,
+ * we could have been interrupted by a signal last time...
+ */
+ if (!test_bit(STATE_OPEN_PENDING_BIT, (void *) &dev->status)) {
+
+ /* First opening of file; connect lower layers: */
+
+ dev->layer.receive = caif_chrrecv_cb;
+
+ clear_bit(STATE_IS_CLOSED_BIT, (void *) &dev->status);
+ clear_bit(STATE_TX_FLOW_ON, (void *) &dev->status);
+ set_bit(STATE_OPEN_PENDING_BIT, (void *) &dev->status);
+
+ /* Register this channel. */
+ result =
+ caifdev_adapt_register(&dev->config, &dev->layer);
+ if (result < 0) {
+ CAIFLOG_TRACE
+ ("caif_chropen: can't register channel\n");
+ ret = -EIO;
+ goto open_error;
+ }
+ }
+
+ CAIFLOG_TRACE("[%s:%d] WAIT FOR CONNECT RESPONSE \n", __func__,
+ __LINE__);
+
+ result = wait_event_interruptible_timeout(dev->mgmt_wq,
+ test_bit
+ (STATE_IS_OPEN_BIT,
+ (void *)
+ &dev->status)
+ ||
+ test_bit
+ (STATE_IS_CLOSED_BIT,
+ (void *)
+ &dev->status),
+ CAIF_CONNECT_TIMEOUT
+ * HZ);
+ if (result == -ERESTARTSYS) {
+ CAIFLOG_TRACE
+ ("caif_chropen: "
+ "wait_event_interruptible woken by a signal (2)");
+ ret = -ERESTARTSYS;
+ goto open_error;
+ } else if (result == 0) {
+ /* Timed out */
+ /* Timeout */
+ CAIFLOG_TRACE("caif_open: connect timed out\n");
+ ret = -ETIMEDOUT;
+ goto open_error;
+ }
+
+ if (test_bit(STATE_IS_CLOSED_BIT, (void *) &dev->status)
+ ) {
+ /* Lower layers said "no" */
+ CAIFLOG_TRACE
+ ("[%s:%d] caif_chropen: CLOSED RECEIVED\n",
+ __func__, __LINE__);
+ ret = -EPIPE;
+ goto open_error;
+ }
+
+ CAIFLOG_TRACE("[%s:%d] caif_chropen: CONNECT RECEIVED\n",
+ __func__, __LINE__);
+ } else {
+ /* Already open */
+ CAIFLOG_TRACE
+ ("[%s:%d] Device is already opened (dev=%p) check access "
+ "f_flags = 0x%x file_mode = 0x%x\n",
+ __func__, __LINE__, dev, mode, dev->file_mode);
+
+ if (mode & dev->file_mode) {
+ CAIFLOG_TRACE
+ ("[%s:%d] Access mode already in use 0x%x \n",
+ __func__, __LINE__, mode);
+ ret = -EBUSY;
+ goto open_error;
+ }
+ }
+
+ /* Open is ok */
+ dev->file_mode |= mode;
+
+ CAIFLOG_TRACE("[%s:%d] Open - file mode = %x\n",
+ __func__, __LINE__, dev->file_mode);
+
+ CAIFLOG_TRACE("[%s:%d] CONNECTED \n", __func__, __LINE__);
+
+ mutex_unlock(&dev->mutex);
+ CAIFLOG_EXIT("");
+ return 0;
+
+open_error:
+ mutex_unlock(&dev->mutex);
+ CAIFLOG_EXIT("");
+ return ret;
+}
+
+int caif_chrrelease(struct inode *inode, struct file *filp)
+{
+ struct caif_char_dev *dev = NULL;
+ int minor = iminor(inode);
+ int result;
+ int mode = 0;
+ CAIFLOG_ENTER("");
+
+ IF_CAIF_TRACE(print_device_list());
+ dev = find_device(minor, NULL, 0);
+ if (dev == NULL) {
+ CAIFLOG_TRACE("[%s] COULD NOT FIND DEVICE\n", __func__);
+ return -EBADF;
+ }
+
+ /* I want to be alone on dev (except status queue) */
+ if (mutex_lock_interruptible(&dev->mutex)) {
+ CAIFLOG_TRACE
+ ("caif_chrrelease: "
+ "mutex_lock_interruptible got signalled\n");
+ return -ERESTARTSYS;
+ }
+
+ /* Don't need to test for CLOSE _PENDING because if set IS_OPEN is
+ always cleared
+ */
+ if (!test_bit(STATE_IS_OPEN_BIT, (void *) &dev->status)) {
+ CAIFLOG_TRACE("[%s:%d] Device not open (dev=%p) \n",
+ __func__, __LINE__, dev);
+ mutex_unlock(&dev->mutex);
+ return 0;
+ }
+
+ switch (filp->f_flags & O_ACCMODE) {
+ case O_RDONLY:
+ mode = CHR_READ_FLAG;
+ break;
+ case O_WRONLY:
+ mode = CHR_WRITE_FLAG;
+ break;
+ case O_RDWR:
+ mode = CHR_READ_FLAG | CHR_WRITE_FLAG;
+ break;
+ }
+
+
+ dev->file_mode &= ~mode;
+ if (dev->file_mode) {
+ CAIFLOG_TRACE
+ ("[%s:%d] Don't yet close CAIF connection - file_mode "
+ "= %x\n", __func__, __LINE__, dev->file_mode);
+ mutex_unlock(&dev->mutex);
+ return 0;
+ }
+
+ /* IS_CLOSED have double meaning:
+ * 1) Spontanous Remote Shutdown Request.
+ * 2) Ack on a channel teardown(disconnect)
+ * Must clear bit in case we previously received
+ * remote shudown request.
+ */
+
+ clear_bit(STATE_IS_CLOSED_BIT, (void *) &dev->status);
+ set_bit(STATE_CLOSE_PENDING_BIT, (void *) &dev->status);
+
+ result = caifdev_adapt_unregister(&dev->layer);
+
+ if (result < 0) {
+ /* Timeout */
+ CAIFLOG_TRACE
+ ("caif_chrrelease: caifdev_adapt_unregister() failed\n");
+ mutex_unlock(&dev->mutex);
+ return -EIO;
+ }
+
+ /* wait for flow control callback with flow == shutdown */
+ result = wait_event_interruptible_timeout(dev->mgmt_wq,
+ test_bit(STATE_IS_CLOSED_BIT,
+ (void *)
+ &dev->status),
+ CAIF_CONNECT_TIMEOUT * HZ);
+ if (result == -ERESTARTSYS) {
+ CAIFLOG_TRACE
+ ("caif_chrrelease: "
+ "wait_event_interruptible woken by signal,"
+ " signal_pending(current) = %d\n",
+ signal_pending(current));
+ } else if (result == 0) {
+ /* Timeout */
+ CAIFLOG_TRACE("caif_chrrelease: disconnect timed out\n");
+ mutex_unlock(&dev->mutex);
+ return -ETIMEDOUT;
+ } else {
+ CAIFLOG_TRACE("[%s:%d] caif_chrrelease: DISCONNECT RECEIVED\n",
+ __func__, __LINE__);
+ }
+
+ /* Empty the queue */
+ drain_queue(dev);
+ dev->file_mode = 0;
+ clear_bit(STATE_IS_OPEN_BIT, (void *) &dev->status);
+ clear_bit(STATE_TX_FLOW_ON, (void *) &dev->status);
+ IF_CAIF_TRACE(print_device_list());
+
+ mutex_unlock(&dev->mutex);
+ return 0;
+}
+
+const struct file_operations caif_chrfops = {
+ .owner = THIS_MODULE,
+ .read = caif_chrread,
+ .write = caif_chrwrite,
+ .open = caif_chropen,
+ .release = caif_chrrelease,
+ .poll = caif_chrpoll,
+};
+
+int chrdev_create(struct caif_channel_create_action *action)
+{
+
+ struct caif_char_dev *dev = NULL;
+ int result;
+ CAIFLOG_ENTER("");
+
+ IF_CAIF_TRACE(print_device_list());
+
+ /* Allocate device */
+ dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+
+ if (!dev) {
+ CAIFLOG_TRACE(KERN_ERR "chnl_chr: kmalloc failed.\n");
+ return -ENOMEM;
+ }
+
+ CAIFLOG_TRACE("[%s:%d] dev=%p \n", __func__, __LINE__, dev);
+ memset(dev, 0, sizeof(*dev));
+
+ mutex_init(&dev->mutex);
+ init_waitqueue_head(&dev->read_wq);
+ init_waitqueue_head(&dev->mgmt_wq);
+ spin_lock_init(&dev->read_queue_len_lock);
+
+ /* Fill in some information concerning the misc device. */
+ dev->misc.minor = MISC_DYNAMIC_MINOR;
+ strncpy(dev->name, action->name.name, sizeof(dev->name));
+ dev->misc.name = dev->name;
+ dev->misc.fops = &caif_chrfops;
+
+ /* Register the device */
+ result = misc_register(&dev->misc);
+
+ /* Lock in order to try to stop someone from opening the device
+ too early. The misc device has its own lock. We cannot take our
+ lock until misc_register() is finished, because in open() the
+ locks are taken in this order (misc first and then dev).
+ So anyone managing to open the device between the misc_register
+ and the mutex_lock will get a "device not found" error. Don't
+ think it can be avoided.
+ */
+ mutex_lock_interruptible(&dev->mutex);
+
+ if (result < 0) {
+ CAIFLOG_ERROR("chnl_chr: error - %d, can't register misc.\n",
+ result);
+ mutex_unlock(&dev->mutex);
+ goto err_failed;
+ }
+
+
+ dev->pktf = cfcnfg_get_packet_funcs();
+
+ dev->pktq = dev->pktf.cfpktq_create();
+ if (!dev->pktq) {
+ CAIFLOG_ERROR("chnl_chr: queue create failed.\n");
+ result = -ENOMEM;
+ mutex_unlock(&dev->mutex);
+ misc_deregister(&dev->misc);
+ goto err_failed;
+ }
+
+ CAIFLOG_TRACE("[%s:%d] pktf=%p\n", __func__, __LINE__, &dev->pktf);
+ CAIFLOG_TRACE("[%s:%d] cfpkt_create_xmit=%p\n", __func__,
+ __LINE__, dev->pktf.cfpkt_create_xmit_pkt);
+
+ strncpy(action->name.name, dev->misc.name, sizeof(action->name.name));
+ action->major = MISC_MAJOR;
+ action->minor = dev->misc.minor;
+
+ dev->config = action->config;
+ CAIFLOG_TRACE("dev: Registered dev with name=%s minor=%d, dev=%p\n",
+ dev->misc.name, dev->misc.minor, dev->misc.this_device);
+
+ dev->layer.ctrlcmd = caif_chrflowctrl_cb;
+
+ /* Add the device */
+ spin_lock(&list_lock);
+ list_add(&dev->list_field, &caif_chrdev_list);
+ spin_unlock(&list_lock);
+
+ IF_CAIF_TRACE(print_device_list());
+
+ CAIFLOG_EXIT("");
+ mutex_unlock(&dev->mutex);
+ return 0;
+err_failed:
+ action->name.name[0] = '\0';
+ action->major = -1;
+ action->minor = -1;
+ kfree(dev);
+ CAIFLOG_EXIT("");
+ return result;
+}
+
+
+int chrdev_remove(char *name)
+{
+ int ret = 0;
+
+ struct caif_char_dev *dev = NULL;
+ CAIFLOG_ENTER("");
+
+ IF_CAIF_TRACE(print_device_list());
+
+ /* Find device from name */
+ dev = find_device(-1, name, 0);
+ if (!dev)
+ return -EBADF;
+
+
+ mutex_lock_interruptible(&dev->mutex);
+
+ if (test_bit(STATE_IS_OPEN_BIT, (void *) &dev->status)) {
+ CAIFLOG_TRACE
+ ("[%s:%d] Device is opened (dev=%p) file_mode = 0x%x\n",
+ __func__, __LINE__, dev, dev->file_mode);
+ mutex_unlock(&dev->mutex);
+ return -EBUSY;
+ }
+
+ /* Remove from list */
+ (void) find_device(-1, name, 1);
+
+ drain_queue(dev);
+ ret = misc_deregister(&dev->misc);
+
+ cfglu_free(dev->pktq);
+ mutex_unlock(&dev->mutex);
+ kfree(dev);
+
+ IF_CAIF_TRACE(print_device_list());
+
+ return ret;
+}
+
+
+int chrdev_mgmt(int action, union caif_action *param)
+{
+
+ switch (action) {
+ case CAIF_ACT_CREATE_DEVICE:
+ return chrdev_create(¶m->create_channel);
+ case CAIF_ACT_DELETE_DEVICE:
+ return chrdev_remove(param->delete_channel.name);
+ default:
+ return -EINVAL;
+ }
+}
+
+int caif_chrinit_module(void)
+{
+ CAIFLOG_ENTER("");
+ CAIFLOG_TRACE("\nCompiled:%s:%s\n", __DATE__, __TIME__);
+ spin_lock_init(&list_lock);
+ caif_register_chrdev(chrdev_mgmt);
+ return 0;
+}
+
+void caif_chrexit_module(void)
+{
+ int result;
+
+ CAIFLOG_ENTER("");
+ IF_CAIF_TRACE(print_device_list());
+
+ do {
+ /* Remove any device (the first in the list) */
+ result = chrdev_remove(NULL);
+ } while (result == 0);
+
+ caif_unregister_chrdev();
+
+ IF_CAIF_TRACE(print_device_list());
+ CAIFLOG_EXIT("");
+}
+
+module_init(caif_chrinit_module);
+module_exit(caif_chrexit_module);
diff --git a/net/caif/chnl_net.c b/net/caif/chnl_net.c
new file mode 100644
index 0000000..1d80f09
--- /dev/null
+++ b/net/caif/chnl_net.c
@@ -0,0 +1,464 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Daniel Martensson / Daniel.Martensson@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2 or later.
+*
+*/
+
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+
+#include <linux/if_ether.h>
+#include <linux/moduleparam.h>
+#include <linux/ip.h>
+
+/* Caif header files. */
+#include "caif_layer.h"
+#include "cfcnfg.h"
+#include "cfpkt.h"
+
+#include "caif_chr.h"
+#include "caif_log.h"
+
+
+#define SIZE_MTU 1500
+#define SIZE_MTU_MAX 4080 /*FIXME: check this number*/
+#define SIZE_MTU_MIN 68 /*FIXME: check this number*/
+
+static LIST_HEAD(chnl_net_list);
+static spinlock_t list_lock;
+
+MODULE_LICENSE("GPL");
+
+static int loop;
+module_param(loop, bool, S_IRUGO);
+MODULE_PARM_DESC(loop,
+ "Loop enabled or not (looping will switch src "
+ "and dest of transmitted packages)");
+/*Flow status to remember and control the transmission*/
+static bool flowenabled;
+
+struct chnl_net {
+ layer_t chnl;
+ struct net_device_stats stats;
+ spinlock_t lock;
+ struct caif_channel_config config;
+ struct list_head list_field;
+ struct net_device *netdev;
+ char name[256];
+ bool deviceOpen;
+};
+
+struct net_device *netdevptr;
+
+static struct chnl_net *find_device(char *name, bool remove_from_list)
+{
+ struct list_head *list_node;
+ struct list_head *n;
+ struct chnl_net *dev = NULL;
+ struct chnl_net *tmp;
+ CAIFLOG_ENTER("");
+ spin_lock(&list_lock);
+ CAIFLOG_TRACE("[%s:%d] start looping \n", __func__,
+ __LINE__);
+ list_for_each_safe(list_node, n, &chnl_net_list) {
+ tmp =
+ list_entry(list_node, struct chnl_net,
+ list_field);
+ if (name) { /* find from name */
+ if (!strncmp(tmp->name, name, sizeof(tmp->name)))
+ dev = tmp;
+ else
+ /* Get the first element if name
+ * is not specified*/
+ dev = tmp;
+
+ if (dev) {
+ CAIFLOG_TRACE("[%s:%d] match %s \n",
+ __func__, __LINE__, name);
+ if (remove_from_list)
+ list_del(list_node);
+
+ break;
+ }
+ }
+ }
+ spin_unlock(&list_lock);
+ return dev;
+}
+
+
+
+static int chnl_recv_cb(layer_t *layr, cfpkt_t *pkt)
+{
+ struct sk_buff *skb;
+ caif_packet_funcs_t f;
+ struct chnl_net *priv = NULL;
+ int pktlen;
+
+#ifdef CAIF_USE_SKB
+#else
+ int actual_len;
+#endif
+ int err = 0;
+
+ priv = container_of(layr, struct chnl_net, chnl);
+
+ if (!priv) {
+ printk(KERN_INFO "chnl_recv_cb: netdev container not found\n");
+ return -EINVAL;
+ }
+
+ /* Get caif packet functions. */
+ f = cfcnfg_get_packet_funcs();
+
+ /* Get length of caif packet. */
+ pktlen = f.cfpkt_getlen(pkt);
+
+
+#ifdef CAIF_USE_SKB
+ /* TODO: Don't we make to much assumptions here ?
+ * Cast received packet to a native sk_buff. */
+ skb = (struct sk_buff *) f.cfpkt_tonative(pkt);
+#else
+ skb = dev_alloc_skb(pktlen);
+
+ if (!skb) {
+ printk(KERN_INFO "chnl_recv_cb: dev_alloc_skb failed.\n");
+ /* Update statistics. */
+ priv->netdev->stats.rx_dropped++;
+ err = -ENOMEM;
+ goto err_alloc_skb;
+ }
+
+ /* Extract data from the caif packet and copy it to the skb. */
+ f.cfpkt_extract(pkt, skb_put(skb, pktlen), pktlen, &actual_len);
+#endif
+ /* Pass some minimum information and send the
+ * packet to the net stack. */
+ skb->dev = priv->netdev;
+ skb->protocol = htons(ETH_P_IP);
+ /*FIXME: This is wrong checksum is needed!!*/
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ /*netif_rx(skb);*/
+ if (in_interrupt())
+ netif_rx(skb);
+ else
+ netif_rx_ni(skb);
+
+ /* Update statistics. */
+ priv->netdev->stats.rx_packets++;
+ priv->netdev->stats.rx_bytes += pktlen;
+
+#ifdef CAIF_USE_SKB
+#else
+
+err_alloc_skb:
+ f.cfpkt_destroy(pkt);
+#endif
+
+ return err;
+}
+
+static void chnl_flowctrl_cb(layer_t *layr, caif_ctrlcmd_t flow, int phyid)
+{
+ struct chnl_net *priv = NULL;
+ CAIFLOG_TRACE("NET flowctrl func called flow: %s.\n",
+ flow == CAIF_CTRLCMD_FLOW_ON_IND ? "ON" :
+ flow == CAIF_CTRLCMD_INIT_RSP ? "INIT" :
+ flow == CAIF_CTRLCMD_FLOW_OFF_IND ? "OFF" :
+ flow == CAIF_CTRLCMD_DEINIT_RSP ? "CLOSE/DEINIT" :
+ flow == CAIF_CTRLCMD_INIT_FAIL_RSP ? "OPEN_FAIL" :
+ flow == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND ?
+ "REMOTE_SHUTDOWN" : "UKNOWN CTRL COMMAND");
+
+ priv = container_of(layr, struct chnl_net, chnl);
+
+ switch (flow) {
+ case CAIF_CTRLCMD_FLOW_OFF_IND:
+ case CAIF_CTRLCMD_DEINIT_RSP:
+ case CAIF_CTRLCMD_INIT_FAIL_RSP:
+ case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND:
+ flowenabled = false;
+ netif_tx_disable(priv->netdev);
+ break;
+ case CAIF_CTRLCMD_FLOW_ON_IND:
+ case CAIF_CTRLCMD_INIT_RSP:
+ flowenabled = true;
+ netif_wake_queue(priv->netdev);
+ break;
+
+ default:
+ break;
+ }
+
+
+}
+
+int chnl_net_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct chnl_net *priv;
+ cfpkt_t *pkt = NULL;
+ int len;
+ caif_packet_funcs_t f;
+ int result = -1;
+
+ if (!flowenabled) {
+ #ifndef CAIF_USE_SKB
+ dev_kfree_skb(skb);
+ #endif
+ CAIFLOG_TRACE("Dropping packets Flow OFF\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ if (loop) {
+ struct iphdr *hdr;
+ __be32 swap;
+ /* Retrieve IP header. */
+ hdr = ip_hdr(skb);
+ /* Change source and destination address. */
+ swap = hdr->saddr;
+ hdr->saddr = hdr->daddr;
+ hdr->daddr = swap;
+ }
+
+ /* Store original skb length. */
+ len = skb->len;
+
+ /* Get caif packet functions. */
+ f = cfcnfg_get_packet_funcs();
+
+#ifdef CAIF_USE_SKB
+ /* TODO: Don't we make to much assumptions here ?
+ * Cast sk_buff to cfpck. */
+ pkt = f.cfpkt_fromnative(CAIF_DIR_OUT, (void *) skb);
+#else
+ /* Create a caif packet based on the skbuf. */
+ pkt = f.cfpkt_create_xmit_pkt(skb->data, skb->len);
+
+ if (!pkt) {
+ printk(KERN_INFO "chnl_write: cfpkt_create failed.\n");
+ goto err_cfpkt_create;
+ }
+#endif
+
+ /* Get our private data. */
+ priv = (struct chnl_net *)netdev_priv(dev);
+
+ if (!priv) {
+ printk(KERN_INFO "chnl_write: priv not found\n");
+ #ifndef CAIF_USE_SKB
+ goto err_cfpkt_create;
+ #else
+ return -ENOSPC;
+ #endif
+ }
+
+ /* Send the packet down the stack. */
+ result = priv->chnl.dn->transmit(priv->chnl.dn, NULL, pkt);
+ if (result) {
+ if (result == CFGLU_ERETRY)
+ result = NETDEV_TX_BUSY;
+
+ #ifndef CAIF_USE_SKB
+ f.cfpkt_destroy(pkt);
+ #endif
+ return result;
+ }
+ /* Update statistics. */
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += len;
+
+#ifndef CAIF_USE_SKB
+ dev_kfree_skb(skb);
+#endif
+ return NETDEV_TX_OK;
+
+#ifndef CAIF_USE_SKB
+err_cfpkt_create:
+ dev_kfree_skb(skb);/*FIXME: Freeing necessary here?*/
+ /*FIXME: Check if ENOMEM or ENOSPC should be used in CAIF?*/
+ return -ENOSPC;
+#endif
+}
+
+/*FIXME: MTU is not used, so why do we handle it in this function?
+ * who calls it?*/
+int chnl_net_change_mtu(struct net_device *dev, int mtu)
+{
+ unsigned long flags;
+ struct chnl_net *priv = netdev_priv(dev);
+ spinlock_t *lock = &priv->lock;
+
+ /* check range.
+ * MTU can not exceed maximum CAIF frame size (4095 bytes) */
+ if ((mtu < SIZE_MTU_MIN) || (mtu > SIZE_MTU_MAX))
+ return -EINVAL;
+
+ spin_lock_irqsave(lock, flags);
+ dev->mtu = mtu;
+ spin_unlock_irqrestore(lock, flags);
+ return 0;
+}
+
+
+int chnl_net_open(struct net_device *dev)
+{
+ struct chnl_net *priv = NULL;
+ int result;
+ CAIFLOG_ENTER("chnl_net_open:");
+ priv = (struct chnl_net *)netdev_priv(dev);
+ printk(KERN_INFO "chnl_net_open dev name: %s\n", priv->name);
+
+ if (!priv) {
+ printk(KERN_WARNING "chnl_net_open: no priv\n");
+ return -ENODEV;
+ }
+ result = caifdev_adapt_register(&priv->config, &priv->chnl);
+ if (result != 0) {
+ printk(KERN_WARNING
+ "chnl_net_open: err: Unable to open device, Err:%d\n", result);
+ return -ENODEV;
+ }
+ priv->deviceOpen = true;
+ CAIFLOG_EXIT("chnl_net_open:\n");
+ return 0;
+}
+
+int chnl_net_stop(struct net_device *dev)
+{
+ struct chnl_net *priv;
+ int result;
+ CAIFLOG_ENTER("chnl_net_stop:");
+ printk("chnl_net_stop: %s \n", dev->name);
+ priv = (struct chnl_net *)netdev_priv(dev);
+ result = caifdev_adapt_unregister(&priv->chnl);
+ if (result != 0) {
+ printk(KERN_WARNING
+ "chnl_net_stop: err: Unable to STOP device, Err:%d\n", result);
+ return -EBUSY;
+ }
+ priv->deviceOpen = false;
+ CAIFLOG_EXIT("chnl_net_stop:\n");
+ return 0;
+}
+
+
+void chnl_net_init(struct net_device *dev)
+{
+ CAIFLOG_ENTER("chnl_net_init:");
+ dev->open = chnl_net_open;
+ dev->stop = chnl_net_stop;
+ dev->hard_start_xmit = chnl_net_hard_start_xmit;
+ dev->flags |= IFF_NOARP; /*FIXME: Check additional flags*/
+ dev->mtu = SIZE_MTU;
+ CAIFLOG_EXIT("chnl_net_init:\n");
+}
+
+
+int netdev_create(struct caif_channel_create_action *action)
+{
+ struct chnl_net *priv;
+
+ int result = -1;
+
+ netdevptr = alloc_netdev(sizeof(struct chnl_net),
+ action->name.name, chnl_net_init);
+ priv = (struct chnl_net *)netdev_priv(netdevptr);
+ memset(&priv->config, 0, sizeof(priv->config));
+ priv->chnl.receive = chnl_recv_cb;
+ priv->chnl.ctrlcmd = chnl_flowctrl_cb;
+ priv->config = action->config;
+ priv->netdev = netdevptr;
+ strcpy(priv->name, action->name.name);
+ priv->deviceOpen = false;
+
+ /* Make sure flow is disabled until INIT_RSP is received. */
+ netif_tx_disable(priv->netdev);
+
+ spin_lock_init(&priv->lock);
+ /* Add the device */
+ spin_lock(&list_lock);
+ list_add(&priv->list_field, &chnl_net_list);
+ spin_unlock(&list_lock);
+
+ printk(KERN_WARNING "netdev_create: Creating channel: %s\n",
+ action->name.name);
+
+ if (!priv->netdev) {
+ printk(KERN_WARNING "chnl: can't allocate netdev.\n");
+ return -ENODEV;
+ }
+ result = register_netdev(priv->netdev);
+ if (result < 0) {
+ printk(KERN_WARNING
+ "chnl: err: %d, can't register netdev.\n", result);
+ free_netdev(priv->netdev);
+ return -ENODEV;
+ }
+ return 0;
+
+}
+
+int delete_device(struct chnl_net *dev)
+{
+ if (dev->netdev) {
+ unregister_netdev(dev->netdev);
+ free_netdev(dev->netdev);
+ }
+ printk(KERN_WARNING
+ "delete_device: Removing Device: %s\n", dev->name);
+ return 0;
+}
+
+int netdev_remove(char *name)
+{
+ struct chnl_net *dev = NULL;
+ /* Find device from name */
+ dev = find_device(name, true);
+ if (!dev)
+ return -EBADF;
+ else
+ if (delete_device(dev) != 0)
+ return -EBUSY;
+
+ return 0;
+}
+
+
+int netdev_mgmt(int action, union caif_action *param)
+{
+
+ switch (action) {
+ case CAIF_ACT_CREATE_DEVICE:
+ return netdev_create(¶m->create_channel);
+ case CAIF_ACT_DELETE_DEVICE:
+ return netdev_remove(param->delete_channel.name);
+ default:
+ return -EINVAL;
+ }
+}
+
+int chnl_init_module(void)
+{
+ caif_register_netdev(netdev_mgmt);
+ return 0; /*FIXME: Return error code for success*/
+}
+
+void chnl_exit_module(void)
+{
+ struct chnl_net *dev = NULL;
+
+ while ((dev = find_device(NULL, true)) != NULL) {
+ /* Remove any device (the first in the list) */
+ delete_device(dev);
+ };
+ caif_unregister_netdev();
+}
+
+module_init(chnl_init_module);
+module_exit(chnl_exit_module);
--
1.6.0.4
^ permalink raw reply related
* [PATCH 4/8] [RFC] CAIF Protocol Stack
From: sjur.brandeland @ 2009-09-23 17:31 UTC (permalink / raw)
To: netdev; +Cc: Kim.xx.Lilliestierna, sjur.brandeland
From: Kim Lilliestierna <Kim.xx.Lilliestierna@ericsson.com>
Signed-off-by: sjur.brandeland@stericsson.com
---
net/caif/generic/cfcnfg.c | 546 +++++++++++++++++++++++++++++++++
net/caif/generic/cfctrl.c | 641 +++++++++++++++++++++++++++++++++++++++
net/caif/generic/cfdgml.c | 119 ++++++++
net/caif/generic/cffrml.c | 146 +++++++++
net/caif/generic/cflist.c | 99 ++++++
net/caif/generic/cfloopcfg.c | 93 ++++++
net/caif/generic/cflooplayer.c | 116 +++++++
net/caif/generic/cfmsll.c | 55 ++++
net/caif/generic/cfmuxl.c | 263 ++++++++++++++++
net/caif/generic/cfpkt_skbuff.c | 590 +++++++++++++++++++++++++++++++++++
net/caif/generic/cfrfml.c | 112 +++++++
net/caif/generic/cfserl.c | 297 ++++++++++++++++++
net/caif/generic/cfshml.c | 67 ++++
net/caif/generic/cfspil.c | 245 +++++++++++++++
net/caif/generic/cfsrvl.c | 177 +++++++++++
net/caif/generic/cfutill.c | 115 +++++++
net/caif/generic/cfveil.c | 118 +++++++
net/caif/generic/cfvidl.c | 68 ++++
net/caif/generic/fcs.c | 58 ++++
19 files changed, 3925 insertions(+), 0 deletions(-)
create mode 100644 net/caif/generic/cfcnfg.c
create mode 100644 net/caif/generic/cfctrl.c
create mode 100644 net/caif/generic/cfdgml.c
create mode 100644 net/caif/generic/cffrml.c
create mode 100644 net/caif/generic/cflist.c
create mode 100644 net/caif/generic/cfloopcfg.c
create mode 100644 net/caif/generic/cflooplayer.c
create mode 100644 net/caif/generic/cfmsll.c
create mode 100644 net/caif/generic/cfmuxl.c
create mode 100644 net/caif/generic/cfpkt_skbuff.c
create mode 100644 net/caif/generic/cfrfml.c
create mode 100644 net/caif/generic/cfserl.c
create mode 100644 net/caif/generic/cfshml.c
create mode 100644 net/caif/generic/cfspil.c
create mode 100644 net/caif/generic/cfsrvl.c
create mode 100644 net/caif/generic/cfutill.c
create mode 100644 net/caif/generic/cfveil.c
create mode 100644 net/caif/generic/cfvidl.c
create mode 100644 net/caif/generic/fcs.c
diff --git a/net/caif/generic/cfcnfg.c b/net/caif/generic/cfcnfg.c
new file mode 100644
index 0000000..b69be77
--- /dev/null
+++ b/net/caif/generic/cfcnfg.c
@@ -0,0 +1,546 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2009
+ *
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ *
+ */
+
+
+#include "caif_layer.h"
+#include "cfpkt.h"
+#include "cfglue.h"
+#include "cflst.h"
+#include "cfcnfg.h"
+#include "cfctrl.h"
+#include "cfglue.h"
+#include "cfcnfg.h"
+#include "cfmuxl.h"
+#include "cffrml.h"
+#include "cfserl.h"
+#include "cfspil.h"
+#include "cfshml.h"
+#include "cfmsll.h"
+#include "cfsrvl.h"
+#include "cffrml.h"
+
+#define MAX_PHY_LAYERS 7
+#define PHY_NAME_LEN 20
+
+#define container_obj(layr) cfglu_container_of(layr, cfcnfg_t, layer)
+
+
+/* Information about CAIF physical interfaces held by Config Module in order
+ to manage physical interfaces */
+
+struct cfcnfg_phyinfo {
+ /** Type of physical layer e.g. UART or SPI */
+ cfcnfg_phy_type_t type;
+ /** Pointer to the layer below the MUX (framing layer) */
+ layer_t *frm_layer;
+ /** Pointer to the lowest actual physical layer */
+ layer_t *phy_layer;
+ /** Unique identifier of the physical interface */
+ unsigned int id;
+ /** Name of the physical interface */
+ char name[PHY_NAME_LEN];
+ /** Preference of the physical in interface */
+ cfcnfg_phy_preference_t pref;
+
+ /** Reference count, number of channels using the device */
+ int phy_ref_count;
+};
+
+
+struct _cfcnfg_t {
+ layer_t layer;
+ layer_t *ctrl;
+ layer_t *mux;
+ uint8 last_phyid;
+ struct cfcnfg_phyinfo phy_layers[MAX_PHY_LAYERS];
+ cfcnfg_phy_mgmt_t phy_registration[_CFPHYTYPE_MAX];
+};
+
+/**
+ * This variable is used as a global flag in order to set if STX is used on serial communication.
+ * NOTE: This is not a fully future proof solution.
+ */
+
+int serial_use_stx;
+
+static void cncfg_linkup_rsp(layer_t *layer, uint8 linkid,
+ cfctrl_srv_t serv, uint8 phyid,
+ layer_t *adapt_layer);
+static void cncfg_linkdestroy_rsp(layer_t *layer, uint8 linkid,
+ layer_t *client_layer);
+static void cncfg_reject_rsp(layer_t *layer, uint8 linkid,
+ layer_t *adapt_layer);
+static void cfctrl_resp_func(void);
+static void cfctrl_enum_resp(void);
+
+
+
+cfcnfg_t *cfcnfg_create()
+{
+
+ cfcnfg_t *this;
+ cfctrl_rsp_t resp;
+ /* Initiate response functions */
+ resp.enum_rsp = cfctrl_enum_resp;
+ resp.linkerror_ind = cfctrl_resp_func;
+ resp.linkdestroy_rsp = cncfg_linkdestroy_rsp;
+ resp.sleep_rsp = cfctrl_resp_func;
+ resp.wake_rsp = cfctrl_resp_func;
+ resp.restart_rsp = cfctrl_resp_func;
+ resp.radioset_rsp = cfctrl_resp_func;
+ resp.linksetup_rsp = cncfg_linkup_rsp;
+ resp.reject_rsp = cncfg_reject_rsp;
+ /* Initiate this layer */
+ this = cfglu_alloc(sizeof(cfcnfg_t));
+ memset(this, 0, sizeof(cfcnfg_t));
+ this->mux = cfmuxl_create();
+ this->ctrl = cfctrl_create();
+ this->last_phyid = 1;
+ cfctrl_set_respfuncs(this->ctrl, &resp);
+ cfmuxl_set_uplayer(this->mux, this->ctrl, 0);
+ layer_set_dn(this->ctrl, this->mux);
+ layer_set_up(this->ctrl, this);
+ return this;
+}
+
+static void cfctrl_resp_func(void)
+{
+ CFLOG_ENTER(("cfcnfg: cfctrl_resp_func\n"));
+ CFLOG_EXIT(("cfcnfg: cfctrl_resp_func\n"));
+}
+
+static void cfctrl_enum_resp(void)
+{
+ CFLOG_ENTER(("cfcnfg: enter cfctrl_enum_resp\n"));
+ CFLOG_EXIT(("cfcnfg: exit cfctrl_enum_resp\n"));
+}
+
+
+int cfcnfg_get_phyid(cfcnfg_t *cnfg, cfcnfg_phy_preference_t phy_pref)
+{
+ int i;
+
+ /* Try to match with specified preference */
+ for (i = 1; i < MAX_PHY_LAYERS; i++) {
+ if (cnfg->phy_layers[i].id == i &&
+ cnfg->phy_layers[i].pref == phy_pref &&
+ cnfg->phy_layers[i].frm_layer != NULL) {
+ cfglu_assert(cnfg->phy_layers != NULL);
+ cfglu_assert(cnfg->phy_layers[i].id == i);
+ return cnfg->phy_layers[i].frm_layer->id;
+ }
+ }
+ /* Otherwise just return something */
+ for (i = 1; i < MAX_PHY_LAYERS; i++) {
+ if (cnfg->phy_layers[i].id == i) {
+ cfglu_assert(cnfg->phy_layers != NULL);
+ cfglu_assert(cnfg->phy_layers[i].id == i);
+ return i;
+ }
+ }
+
+ return 0;
+}
+
+
+static struct cfcnfg_phyinfo *cfcnfg_get_phyinfo(cfcnfg_t *cnfg, uint8 phyid)
+{
+ int i;
+
+ /* Try to match with specified preference */
+ for (i = 0; i < MAX_PHY_LAYERS; i++)
+ if (cnfg->phy_layers[i].frm_layer != NULL &&
+ cnfg->phy_layers[i].id == phyid)
+ return &cnfg->phy_layers[i];
+
+ return 0;
+}
+
+int cfcnfg_get_named(cfcnfg_t *cnfg, char *name)
+{
+ int i;
+
+ /* Try to match with specified preference */
+ for (i = 0; i < MAX_PHY_LAYERS; i++) {
+ if (cnfg->phy_layers[i].frm_layer != NULL
+ && strcmp(cnfg->phy_layers[i].frm_layer->name,
+ name) == 0) {
+ return cnfg->phy_layers[i].frm_layer->id;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * NOTE: What happends destroy failure:
+ * 1a) No response - Too early
+ * This will not happend because enumerate has already
+ * completed
+ * 1b) No response - FATAL
+ * Not handled, but this should be a CAIF PROTOCOL ERROR
+ * Modem error, response is really expected - this
+ * case is not really handled..
+ * 2) O/E-bit indicate error
+ * Ignored - this link is destroyed anyway.
+ * 3) Not able to match on reques
+ * Not handled, but this should be a CAIF PROTOCOL ERROR
+ * 4) Link-Error - (no response)
+ * Not handled, but this should be a CAIF PROTOCOL ERROR
+ */
+
+
+bool cfcnfg_del_adapt_layer(struct _cfcnfg_t *cnfg, layer_t *adap_layer)
+{
+ uint8 channel_id = 0;
+ struct cfcnfg_phyinfo *phyinfo = NULL;
+ uint8 phyid = 0;
+ CFLOG_TRACE(("cfcnfg: enter del_adaptation_layer\n"));
+
+ cfglu_assert(adap_layer != NULL);
+ channel_id = adap_layer->id;
+ cfglu_assert(channel_id != 0);
+
+ if (adap_layer->dn == NULL) {
+ CFLOG_ERROR(("cfcnfg:adap_layer->dn is NULL\n"));
+ return CFGLU_EINVAL;
+ }
+
+ if (cnfg->mux != NULL)
+ phyid = cfsrvl_getphyid(adap_layer->dn);
+
+
+ phyinfo = cfcnfg_get_phyinfo(cnfg, phyid);
+
+ cfglu_assert(phyinfo != NULL);
+ cfglu_assert(phyinfo->id == phyid);
+ cfglu_assert(phyinfo->phy_layer->id == phyid);
+ cfglu_assert(phyinfo->frm_layer->id == phyid);
+
+ if (phyinfo != NULL && --phyinfo->phy_ref_count == 0 &&
+ phyinfo->phy_layer->modemcmd != NULL) {
+
+ phyinfo->phy_layer->modemcmd(phyinfo->phy_layer,
+ _CAIF_MODEMCMD_PHYIF_USELESS);
+ }
+
+
+ cfctrl_linkdown_req(cnfg->ctrl, channel_id, adap_layer);
+ return true;
+}
+
+static void cncfg_linkdestroy_rsp(layer_t *layer, uint8 linkid,
+ layer_t *client_layer)
+{
+ cfcnfg_t *cnfg = container_obj(layer);
+ layer_t *servl;
+
+ CFLOG_TRACE(("cfcnfg: enter linkdestroy_rsp\n"));
+
+ /* 1) Remove service from the MUX layer */
+ servl = cfmuxl_remove_uplayer(cnfg->mux, linkid);
+ /* invar: MUX guarantees not more payload sent "upwards" (receive) */
+
+ if (servl == NULL) {
+ CFLOG_ERROR(("cfcnfg: Error removing service_layer Linkid(%d)",
+ linkid));
+ return;
+ }
+ cfglu_assert(linkid == servl->id);
+
+ if (servl != client_layer && servl->up != client_layer) {
+ CFLOG_ERROR(("cfcnfg: Error removing service_layer "
+ "Linkid(%d) %p %p",
+ linkid, (void *) servl, (void *) client_layer));
+ return;
+ }
+
+ /* 2) SHUTDOWN must guarantee that no more packets are transmitted
+ from adap_layer when it returns. We assume it locks the layer for
+ every transmit and lock when receiving this SHUTDOWN */
+
+ if (servl->ctrlcmd == NULL) {
+ CFLOG_ERROR(("cfcnfg: Error servl->ctrlcmd == NULL"));
+ return;
+ }
+
+ servl->ctrlcmd(servl, CAIF_CTRLCMD_DEINIT_RSP, 0);
+
+ /* invar: Adaptation Layer guarantees not more payload sen
+ "down-wards" (transmit) */
+
+ /* 3) It is now safe to destroy the service layer (if any) */
+
+ /*
+ * FIXME: We need a ref-count in order to safely free
+ * the up layer.
+ */
+ if (client_layer != servl->up)
+ cfservl_destroy(servl);
+}
+
+/*
+ * NOTE: What happends linksetup failure:
+ * 1a) No response - Too early
+ * This will not happend because enumerate is secured
+ * before using interface)
+ * 1b) No response - FATAL
+ * Not handled, but this should be a CAIF PROTOCOL ERROR
+ * Modem error, response is really expected - this case is
+ * not really handled..
+ * 2) O/E-bit indicate error
+ * Handeled in cnfg_reject_rsp
+ * 3) Not able to match on reques
+ * Not handled, but this should be a CAIF PROTOCOL ERROR
+ * 4) Link-Error - (no response)
+ * Not handled, but this should be a CAIF PROTOCOL ERROR
+ */
+
+bool
+cfcnfg_add_adaptation_layer(cfcnfg_t *cnfg, cfctrl_link_param_t *param,
+ layer_t *adap_layer)
+{
+ layer_t *frml;
+ CFLOG_TRACE(("cfcnfg[%p]: enter add_adaptation_layer\n",
+ (void *) cnfg));
+
+ if (adap_layer == NULL) {
+ CFLOG_ERROR(("cfcnfg: adap_layer is zero"));
+ return CFGLU_EINVAL;
+ }
+ if (adap_layer->receive == NULL) {
+ CFLOG_ERROR(("cfcnfg-add: adap_layer->receive is NULL"));
+ return CFGLU_EINVAL;
+ }
+ if (adap_layer->ctrlcmd == NULL) {
+ CFLOG_ERROR(("cfcnfg-add: adap_layer->ctrlcmd == NULL"));
+ return CFGLU_EINVAL;
+ }
+
+ frml = cnfg->phy_layers[param->phyid].frm_layer;
+ if (frml == NULL) {
+ CFLOG_ERROR(("cfcnfg: Specified PHY type does not exist!"));
+ return false;
+ }
+ cfglu_assert(param->phyid == cnfg->phy_layers[param->phyid].id);
+ cfglu_assert(cnfg->phy_layers[param->phyid].frm_layer->id ==
+ param->phyid);
+ cfglu_assert(cnfg->phy_layers[param->phyid].phy_layer->id ==
+ param->phyid);
+ CFLOG_TRACE(("cfcnfg: send enum request\n"));
+ /* FIXME: ENUMERATE INITIALLY WHEN ACTIVATING PHYSICAL INTERFACE */
+
+ cfctrl_enum_req(cnfg->ctrl, param->phyid);
+
+ cfctrl_linkup_request(cnfg->ctrl, param, adap_layer);
+
+ return true;
+}
+
+static void cncfg_reject_rsp(layer_t *layer, uint8 linkid,
+ layer_t *adapt_layer)
+{
+ CFLOG_ENTER(("layer=%p linkid=%d adapt_layer=%p\n", (void *) layer,
+ linkid, (void *) adapt_layer));
+ if (adapt_layer != NULL && adapt_layer->ctrlcmd != NULL)
+ adapt_layer->ctrlcmd(adapt_layer,
+ CAIF_CTRLCMD_INIT_FAIL_RSP, 0);
+
+}
+
+static void
+cncfg_linkup_rsp(layer_t *layer, uint8 linkid, cfctrl_srv_t serv,
+ uint8 phyid, layer_t *adapt_layer)
+{
+ cfcnfg_t *cnfg = container_obj(layer);
+ layer_t *servicel = NULL;
+ struct cfcnfg_phyinfo *phyinfo;
+ CFLOG_ENTER(("cfcnfg[%p]: enter scfcnfg_linup_rsp\n", (void *) layer));
+ if (adapt_layer == NULL) {
+ CFLOG_ERROR(("cfcnfg: CAIF PROTOCOL ERROR "
+ "- LinkUp Request/Response did not match\n"));
+ return;
+ }
+
+ cfglu_assert(cnfg != NULL);
+ cfglu_assert(phyid != 0);
+ phyinfo = &cnfg->phy_layers[phyid];
+ cfglu_assert(phyinfo->id == phyid);
+ cfglu_assert(phyinfo->phy_layer->id == phyid);
+
+ if (phyinfo != NULL &&
+ phyinfo->phy_ref_count++ == 0 &&
+ phyinfo->phy_layer != NULL &&
+ phyinfo->phy_layer->modemcmd != NULL) {
+ cfglu_assert(phyinfo->phy_layer->id == phyid);
+ phyinfo->phy_layer->modemcmd(phyinfo->phy_layer,
+ _CAIF_MODEMCMD_PHYIF_USEFULL);
+
+ }
+ adapt_layer->id = linkid;
+
+ switch (serv) {
+ case CFCTRL_SRV_VEI:
+ servicel = cfvei_create(linkid, phyid);
+ CFLOG_TRACE(("cfcnfg: AT channel created\n"));
+ break;
+ case CFCTRL_SRV_DATAGRAM:
+ servicel = cfdgml_create(linkid, phyid);
+ CFLOG_TRACE(("cfcnfg: Datagram channel created\n"));
+ break;
+ case CFCTRL_SRV_RFM:
+ servicel = cfrfml_create(linkid, phyid);
+ CFLOG_TRACE(("cfcnfg: RFM channel created\n"));
+ break;
+ case CFCTRL_SRV_UTIL:
+ servicel = cfutill_create(linkid, phyid);
+ CFLOG_TRACE(("cfcnfg: Utility channel created\n"));
+ break;
+ case CFCTRL_SRV_VIDEO:
+ servicel = cfvidl_create(linkid, phyid);
+ CFLOG_TRACE(("cfcnfg: Video channel created\n"));
+ break;
+ case CFCTRL_SRV_DBG:
+ CFLOG_TRACE(("cfcnfg: Debug channel created\n"));
+ servicel = adapt_layer;
+ break;
+ default:
+ CFLOG_ERROR(("cfcnfg: ERROR "
+ "Link setup response - unknown channel type\n"));
+ return;
+ }
+ layer_set_dn(servicel, cnfg->mux);
+
+ CFLOG_TRACE(("cfcnfg: insert service layer in mux\n"));
+
+ cfmuxl_set_uplayer(cnfg->mux, servicel, linkid);
+ if (servicel != adapt_layer) {
+ CFLOG_TRACE(("cfcnfg: insert adapt-layer in service layer\n"));
+ layer_set_up(servicel, adapt_layer);
+ layer_set_dn(adapt_layer, servicel);
+ }
+ CFLOG_TRACE(("cfcnfg: successfull link setup - call flowtrl(init)\n"));
+ servicel->ctrlcmd(servicel, CAIF_CTRLCMD_INIT_RSP, 0);
+}
+
+
+
+
+
+caif_packet_funcs_t cfcnfg_get_packet_funcs()
+{
+ return cfpkt_get_packet_funcs();
+}
+
+
+void
+cfcnfg_add_phy_layer(cfcnfg_t *cnfg, cfcnfg_phy_type_t phy_type,
+ layer_t *phy_layer, uint16 *phyid,
+ cfcnfg_phy_preference_t pref)
+{
+ layer_t *frml;
+ layer_t *phy_driver = NULL;
+ int i;
+ bool DoFCS = false;
+ CFLOG_TRACE(("cfcnfg: enter add_phy_layer\n"));
+
+ if (cnfg->phy_layers[cnfg->last_phyid].frm_layer == NULL) {
+ *phyid = cnfg->last_phyid;
+
+ /* range: * 1..(MAX_PHY_LAYERS-1) */
+ cnfg->last_phyid =
+ (cnfg->last_phyid % (MAX_PHY_LAYERS - 1)) + 1;
+ } else {
+ *phyid = 0;
+ for (i = 1; i < MAX_PHY_LAYERS; i++) {
+ if (cnfg->phy_layers[i].frm_layer == NULL) {
+ *phyid = i;
+ break;
+ }
+ }
+ }
+ if (*phyid == 0) {
+ CFLOG_ERROR(("cfcnfg: No Available PHY ID\n"));
+ return;
+ }
+
+ switch (phy_type) {
+ case CFPHYTYPE_SERIAL:
+ CFLOG_TRACE(("cfcnfg: Starting Serial link\n"));
+ DoFCS = true;
+ phy_driver =
+ cfserl_create(CFPHYTYPE_SERIAL, *phyid, serial_use_stx);
+ break;
+ case CFPHYTYPE_SPI:
+ CFLOG_TRACE(("cfcnfg: Starting SPI link\n"));
+ phy_driver = cfspil_create(CFPHYTYPE_SPI, *phyid);
+ break;
+ case CFPHYTYPE_SHM:
+ CFLOG_TRACE(("cfcnfg: Starting SHM link\n"));
+ phy_driver = cfshml_create(CFPHYTYPE_SHM, *phyid);
+ break;
+ case CFPHYTYPE_MSL:
+ CFLOG_TRACE(("cfcnfg: Starting MSL link\n"));
+ phy_driver = cfmsll_create(CFPHYTYPE_MSL, *phyid);
+ break;
+ default:
+ CFLOG_ERROR(("cfcnfg: Bad phy_type specified: %d", phy_type));
+ return;
+ break;
+ }
+
+
+ phy_layer->id = *phyid;
+ phy_driver->id = *phyid;
+ cnfg->phy_layers[*phyid].pref = pref;
+ cnfg->phy_layers[*phyid].id = *phyid;
+ cnfg->phy_layers[*phyid].type = phy_type;
+ cnfg->phy_layers[*phyid].phy_layer = phy_layer;
+ cnfg->phy_layers[*phyid].phy_ref_count = 0;
+ phy_layer->type = phy_type;
+ frml = cffrml_create(*phyid, DoFCS);
+ cnfg->phy_layers[*phyid].frm_layer = frml;
+ cfmuxl_set_dnlayer(cnfg->mux, frml, *phyid);
+ layer_set_up(frml, cnfg->mux);
+ layer_set_dn(frml, phy_driver);
+ layer_set_up(phy_driver, frml);
+ layer_set_dn(phy_driver, phy_layer);
+ layer_set_up(phy_layer, phy_driver);
+ CFLOG_TRACE(("cfcnfg: phy1=%p phy2=%p transmit=0x%d\n",
+ (void *) phy_driver, (void *) phy_layer,
+ (int) phy_layer->transmit));
+}
+
+int cfcnfg_del_phy_layer(struct _cfcnfg_t *cnfg, layer_t *phy_layer)
+{
+ layer_t *frml, *cfphy;
+ uint16 phyid;
+ phyid = phy_layer->id;
+ cfglu_assert(phyid == cnfg->phy_layers[phyid].id);
+ cfglu_assert(phy_layer == cnfg->phy_layers[phyid].phy_layer);
+ cfglu_assert(phy_layer->id == phyid);
+ cfglu_assert(cnfg->phy_layers[phyid].frm_layer->id == phyid);
+
+ memset(&cnfg->phy_layers[phy_layer->id], 0,
+ sizeof(struct cfcnfg_phyinfo));
+ frml = cfmuxl_remove_dnlayer(cnfg->mux, phy_layer->id);
+ cfphy = frml->dn;
+
+ cffrml_set_uplayer(frml, NULL);
+ cffrml_set_dnlayer(frml, NULL);
+ cffrml_destroy(frml);
+
+ cfglu_assert(cfphy->dn == phy_layer);
+ layer_set_up(cfphy, NULL);
+ layer_set_dn(cfphy, NULL);
+ cfglu_free(cfphy);
+
+ layer_set_up(phy_layer, NULL);
+ return CFGLU_EOK;
+}
diff --git a/net/caif/generic/cfctrl.c b/net/caif/generic/cfctrl.c
new file mode 100644
index 0000000..c0517b4
--- /dev/null
+++ b/net/caif/generic/cfctrl.c
@@ -0,0 +1,641 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2009
+ *
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ *
+ */
+
+
+
+#include "caif_layer.h"
+#include "cfpkt.h"
+#include "cfglue.h"
+#include "cfctrl.h"
+
+#define container_obj(layr) cfglu_container_of(layr, struct cfctrl, serv.layer)
+#define UTILITY_NAME_LENGTH 16
+#define CFPKT_CTRL_PKT_LEN 20
+
+
+struct cfctrl {
+ cfsrvl_t serv;
+ cfctrl_rsp_t res;
+ int req_seq_no;
+ int rsp_seq_no;
+ struct cfctrl_request_info *first_req;
+};
+
+
+static int cfctrl_recv(struct cfctrl *cfctrl, cfpkt_t *pkt);
+static void cfctrl_ctrlcmd(layer_t *layr, caif_ctrlcmd_t ctrl, int phyid);
+
+layer_t *cfctrl_create()
+{
+ struct cfctrl *this =
+ (struct cfctrl *) cfglu_alloc(sizeof(struct cfctrl));
+ cfglu_assert(offsetof(struct cfctrl, serv.layer) == 0);
+ memset(this, 0, sizeof(*this));
+ this->req_seq_no = 1;
+ this->rsp_seq_no = 1;
+ this->serv.phid = 0xff;
+ this->serv.layer.id = 0;
+ this->serv.layer.receive = (receive_cb_t) cfctrl_recv;
+ sprintf(this->serv.layer.name, "ctrl");
+ this->serv.layer.ctrlcmd = cfctrl_ctrlcmd;
+ return &this->serv.layer;
+}
+
+
+bool param_eq(cfctrl_link_param_t *p1, cfctrl_link_param_t *p2)
+{
+ bool eq =
+ p1->linktype == p2->linktype &&
+ p1->priority == p2->priority &&
+ p1->phyid == p2->phyid &&
+ p1->endpoint == p2->endpoint && p1->chtype == p2->chtype;
+
+ if (!eq)
+ return false;
+
+ switch (p1->linktype) {
+ case CFCTRL_SRV_VEI:
+ return true;
+ case CFCTRL_SRV_DATAGRAM:
+ return p1->u.datagram.connid == p2->u.datagram.connid;
+ case CFCTRL_SRV_RFM:
+ return
+ p1->u.rfm.connid == p2->u.rfm.connid &&
+ strcmp(p1->u.rfm.volume, p2->u.rfm.volume) == 0;
+ case CFCTRL_SRV_UTIL:
+ return
+ p1->u.utility.fifosize_kb == p2->u.utility.fifosize_kb
+ && p1->u.utility.fifosize_bufs ==
+ p2->u.utility.fifosize_bufs
+ && strcmp(p1->u.utility.name, p2->u.utility.name) == 0
+ && p1->u.utility.paramlen == p2->u.utility.paramlen
+ && memcmp(p1->u.utility.params, p2->u.utility.params,
+ p1->u.utility.paramlen) == 0;
+
+ case CFCTRL_SRV_VIDEO:
+ return p1->u.video.connid == p2->u.video.connid;
+ case CFCTRL_SRV_DBG:
+ return true;
+ case CFCTRL_SRV_DECM:
+ return false;
+ default:
+ return false;
+ }
+ return false;
+}
+
+bool cfctrl_req_eq(struct cfctrl_request_info *r1,
+ struct cfctrl_request_info *r2)
+{
+ if (r1->cmd != r2->cmd)
+ return false;
+ if (r1->cmd == CFCTRL_CMD_LINK_SETUP)
+ return param_eq(&r1->param, &r2->param);
+ else
+ return r1->channel_id == r2->channel_id;
+}
+
+/* Insert request at the end */
+void cfctrl_insert_req(struct cfctrl *ctrl, struct cfctrl_request_info *req)
+{
+ struct cfctrl_request_info *p;
+ req->next = NULL;
+ req->sequence_no = ++ctrl->req_seq_no;
+ if (ctrl->first_req == NULL) {
+ ctrl->first_req = req;
+ return;
+ }
+ p = ctrl->first_req;
+ while (p->next != NULL)
+ p = p->next;
+ p->next = req;
+}
+
+void cfctrl_insert_req2(struct cfctrl *ctrl, cfctrl_cmd_t cmd, uint8 linkid,
+ layer_t *user_layer)
+{
+ struct cfctrl_request_info *req = cfglu_alloc(sizeof(*req));
+ req->client_layer = user_layer;
+ req->cmd = cmd;
+ req->channel_id = linkid;
+ cfctrl_insert_req(ctrl, req);
+}
+
+/* Compare and remove request */
+struct cfctrl_request_info *cfctrl_remove_req(struct cfctrl *ctrl,
+ struct cfctrl_request_info *req)
+{
+ struct cfctrl_request_info *p;
+ struct cfctrl_request_info *ret;
+ if (ctrl->first_req == NULL)
+ return NULL;
+
+ if (cfctrl_req_eq(req, ctrl->first_req)) {
+ ret = ctrl->first_req;
+ ctrl->rsp_seq_no = ctrl->first_req->sequence_no;
+ ctrl->first_req = ctrl->first_req->next;
+ return ret;
+ }
+
+ CFLOG_WARN(("cfctrl: Requests are not received in order/matching\n"));
+
+ p = ctrl->first_req;
+
+ while (p->next != NULL) {
+ if (cfctrl_req_eq(req, p->next)) {
+ ret = p->next;
+ ctrl->rsp_seq_no = p->next->sequence_no;
+ p = p->next;
+ return ret;
+ }
+ p = p->next;
+ }
+ return NULL;
+}
+
+/* Compare and remove old requests based on sequence no. */
+void cfctrl_prune_req(struct cfctrl *ctrl)
+{
+ struct cfctrl_request_info *p;
+ struct cfctrl_request_info *del;
+ if (ctrl->first_req == NULL)
+ return;
+
+ if (ctrl->first_req->sequence_no < ctrl->req_seq_no) {
+ del = ctrl->first_req;
+ ctrl->first_req = ctrl->first_req->next;
+ cfglu_free(del);
+ }
+ p = ctrl->first_req;
+ while (p->next != NULL) {
+ if (p->next->sequence_no < ctrl->rsp_seq_no) {
+ del = p->next;
+ p = p->next;
+ ctrl->rsp_seq_no = ctrl->first_req->sequence_no;
+ cfglu_free(del);
+ }
+ p = p->next;
+ }
+}
+
+
+void cfctrl_set_respfuncs(layer_t *layer, cfctrl_rsp_t *respfuncs)
+{
+ struct cfctrl *this = container_obj(layer);
+ this->res = *respfuncs;
+}
+
+void cfctrl_set_dnlayer(layer_t *this, layer_t *dn)
+{
+ this->dn = dn;
+}
+
+void cfctrl_set_uplayer(layer_t *this, layer_t *up)
+{
+ this->up = up;
+}
+
+
+static transmt_info init_info(struct cfctrl *cfctrl)
+{
+ transmt_info info;
+ info.hdr_len = 0;
+ info.prio = 0;
+ info.channel_id = cfctrl->serv.layer.id;
+ info.phid = cfctrl->serv.phid;
+ return info;
+}
+
+void cfctrl_enum_req(layer_t *layer, uint8 physlinkid)
+{
+ struct cfctrl *cfctrl = container_obj(layer);
+ transmt_info info = init_info(cfctrl);
+ int ret;
+ cfpkt_t *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
+ cfglu_assert(offsetof(struct cfctrl, serv.layer) == 0);
+ info.phid = physlinkid;
+ cfctrl->serv.phid = physlinkid;
+ cfpkt_addbdy(pkt, CFCTRL_CMD_ENUM);
+ cfpkt_addbdy(pkt, physlinkid);
+ ret =
+ cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, &info, pkt);
+ if (ret < 0) {
+ CFLOG_ERROR(("Could not transmit enum message\n"));
+ cfpkt_destroy(pkt);
+ }
+}
+
+
+void cfctrl_linkup_request(layer_t *layer, cfctrl_link_param_t *param,
+ layer_t *user_layer)
+{
+
+ struct cfctrl *cfctrl = container_obj(layer);
+ uint32 tmp32;
+ uint16 tmp16;
+ uint8 tmp8;
+ int ret;
+ char utility_name[16];
+ struct cfctrl_request_info *req;
+ transmt_info info = init_info(cfctrl);
+ cfpkt_t *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
+ CFLOG_TRACE(("cfctrl: enter linkup_request\n"));
+
+ cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_SETUP);
+ cfpkt_addbdy(pkt, (param->chtype << 4) + param->linktype);
+ cfpkt_addbdy(pkt, (param->priority << 3) + param->phyid);
+ cfpkt_addbdy(pkt, param->endpoint & 0x03);
+ CFLOG_TRACE2(("channel config: chtype:%d linktype:%d "
+ "phyid:%d prio:%d endpoint:%d\n",
+ param->chtype, param->linktype,
+ param->phyid, param->priority, param->endpoint));
+
+ switch (param->linktype) {
+ case CFCTRL_SRV_VEI:
+ break;
+
+ case CFCTRL_SRV_VIDEO:
+ cfpkt_addbdy(pkt, (uint8) param->u.video.connid);
+ break;
+
+ case CFCTRL_SRV_DBG:
+ break;
+
+ case CFCTRL_SRV_DATAGRAM:
+ tmp32 = cfglu_cpu_to_le32(param->u.datagram.connid);
+ cfpkt_add_body(pkt, &tmp32, 4);
+ break;
+
+ case CFCTRL_SRV_RFM:
+ /* construct a frame Convert DatagramConnectionID to network
+ format long and copy it out.. */
+ tmp32 = cfglu_cpu_to_le32(param->u.rfm.connid);
+ cfpkt_add_body(pkt, &tmp32, 4);
+ /* Add Volume name, including zero termination */
+ cfpkt_add_body(pkt, param->u.rfm.volume,
+ strlen(param->u.rfm.volume) + 1);
+ break;
+
+ case CFCTRL_SRV_UTIL:
+ tmp16 = cfglu_cpu_to_le16(param->u.utility.fifosize_kb);
+ cfpkt_add_body(pkt, &tmp16, 2);
+ tmp16 = cfglu_cpu_to_le16(param->u.utility.fifosize_bufs);
+ cfpkt_add_body(pkt, &tmp16, 2);
+ memset(utility_name, 0, sizeof(utility_name));
+ strncpy(utility_name, param->u.utility.name,
+ UTILITY_NAME_LENGTH - 1);
+ cfpkt_add_body(pkt, utility_name, UTILITY_NAME_LENGTH);
+ tmp8 = param->u.utility.paramlen;
+ cfpkt_add_body(pkt, &tmp8, 1);
+ cfpkt_add_body(pkt, param->u.utility.params,
+ param->u.utility.paramlen);
+ CFLOG_TRACE2(("util config: kb:%d bufs:%d name:%s paramlen:%d"
+ "param:0x%02x,%02x,%02x,%02x,%02x\n",
+ param->u.utility.fifosize_kb,
+ param->u.utility.fifosize_bufs,
+ utility_name,
+ param->u.utility.paramlen,
+ param->u.utility.params[0],
+ param->u.utility.params[1],
+ param->u.utility.params[2],
+ param->u.utility.params[3],
+ param->u.utility.params[4]));
+ break;
+
+ default:
+ CFLOG_ERROR(("CAIF: Request setup of invalid link type = %d\n",
+ param->linktype));
+ }
+ req = cfglu_alloc(sizeof(*req));
+ memset(req, 0, sizeof(*req));
+ req->client_layer = user_layer;
+ req->cmd = CFCTRL_CMD_LINK_SETUP;
+ req->param = *param;
+ cfctrl_insert_req(cfctrl, req);
+ ret =
+ cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, &info, pkt);
+ if (ret < 0) {
+ CFLOG_ERROR(("cfctl: Could not transmit linksetup request\n"));
+ cfpkt_destroy(pkt);
+ }
+}
+
+void cfctrl_linkdown_req(layer_t *layer, uint8 channelid, layer_t *client)
+{
+ int ret;
+ struct cfctrl *cfctrl = container_obj(layer);
+ cfpkt_t *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
+ struct cfctrl_request_info *req = cfglu_alloc(sizeof(*req));
+ transmt_info info = init_info(cfctrl);
+ cfctrl_insert_req2(cfctrl, CFCTRL_CMD_LINK_DESTROY, channelid, client);
+ cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_DESTROY);
+ cfpkt_addbdy(pkt, channelid);
+ ret =
+ cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, &info, pkt);
+ if (ret < 0) {
+ CFLOG_ERROR(("cfctl: Could not transmit link-down request\n"));
+ cfpkt_destroy(pkt);
+ }
+}
+
+void cfctrl_sleep_req(layer_t *layer)
+{
+ int ret;
+ struct cfctrl *cfctrl = container_obj(layer);
+ cfpkt_t *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
+ transmt_info info = init_info(cfctrl);
+ cfpkt_addbdy(pkt, CFCTRL_CMD_SLEEP);
+ ret =
+ cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, &info, pkt);
+ if (ret < 0)
+ cfpkt_destroy(pkt);
+
+}
+
+void cfctrl_wake_req(layer_t *layer)
+{
+ int ret;
+ struct cfctrl *cfctrl = container_obj(layer);
+ cfpkt_t *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
+ transmt_info info = init_info(cfctrl);
+ cfpkt_addbdy(pkt, CFCTRL_CMD_WAKE);
+ ret =
+ cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, &info, pkt);
+ if (ret < 0)
+ cfpkt_destroy(pkt);
+}
+
+void cfctrl_getstartreason_req(layer_t *layer)
+{
+ int ret;
+ struct cfctrl *cfctrl = container_obj(layer);
+ cfpkt_t *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
+ transmt_info info = init_info(cfctrl);
+ cfpkt_addbdy(pkt, CFCTRL_CMD_START_REASON);
+ ret =
+ cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, &info, pkt);
+ if (ret < 0)
+ cfpkt_destroy(pkt);
+}
+
+
+void cfctrl_setmode_req(layer_t *layer, uint8 mode)
+{
+ int ret;
+ struct cfctrl *cfctrl = container_obj(layer);
+ cfpkt_t *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
+ transmt_info info = init_info(cfctrl);
+ cfpkt_addbdy(pkt, CFCTRL_CMD_RADIO_SET);
+ cfpkt_addbdy(pkt, mode);
+ ret =
+ cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, &info, pkt);
+ if (ret < 0)
+ cfpkt_destroy(pkt);
+}
+
+
+
+static int cfctrl_recv(struct cfctrl *cfctrl, cfpkt_t *pkt)
+{
+ uint8 cmdrsp;
+ uint8 cmd;
+ int ret = -1;
+ uint16 tmp16;
+ uint8 len;
+ uint8 param[255];
+ uint8 linkid;
+ struct cfctrl_request_info rsp, *req;
+
+ CFLOG_TRACE(("cfctrl: enter cfctrl_recv\n"));
+
+ (void) cfpkt_extr_head(pkt, &cmdrsp, 1);
+ cmd = cmdrsp & CFCTRL_CMD_MASK;
+ if (cmd != CFCTRL_CMD_LINK_ERR
+ && CFCTRL_RSP_BIT != (CFCTRL_RSP_BIT & cmdrsp)) {
+ CFLOG_ERROR(("CAIF Protocol error: Response bit not set\n"));
+ goto error;
+ }
+
+ switch (cmd) {
+ case CFCTRL_CMD_LINK_SETUP:
+ {
+ cfctrl_srv_t serv;
+ cfctrl_srv_t servtype;
+ uint8 endpoint;
+ uint8 physlinkid;
+ uint8 prio;
+ uint8 tmp;
+ uint32 tmp32;
+ uint8 *cp;
+ int i;
+ cfctrl_link_param_t linkparam;
+ memset(&linkparam, 0, sizeof(linkparam));
+
+ cfpkt_extr_head(pkt, &tmp, 1);
+
+ serv = tmp & CFCTRL_SRV_MASK;
+ linkparam.linktype = serv;
+
+ servtype = tmp >> 4;
+ linkparam.chtype = servtype;
+
+ cfpkt_extr_head(pkt, &tmp, 1);
+ physlinkid = tmp & 0x07;
+ prio = tmp >> 3;
+
+ linkparam.priority = prio;
+ linkparam.phyid = physlinkid;
+ cfpkt_extr_head(pkt, &endpoint, 1);
+ linkparam.endpoint = endpoint & 0x03;
+
+ switch (serv) {
+ case CFCTRL_SRV_VEI:
+ case CFCTRL_SRV_DBG:
+ /* Link ID */
+ cfpkt_extr_head(pkt, &linkid, 1);
+ break;
+ case CFCTRL_SRV_VIDEO:
+ cfpkt_extr_head(pkt, &tmp, 1);
+ linkparam.u.video.connid = tmp;
+ /* Link ID */
+ cfpkt_extr_head(pkt, &linkid, 1);
+ break;
+
+ case CFCTRL_SRV_DATAGRAM:
+ cfpkt_extr_head(pkt, &tmp32, 4);
+ linkparam.u.datagram.connid =
+ cfglu_le32_to_cpu(tmp32);
+ /* Link ID */
+ cfpkt_extr_head(pkt, &linkid, 1);
+ break;
+
+ case CFCTRL_SRV_RFM:
+ /* construct a frame Conver
+ DatagramConnectionID to network format long
+ and copy it out.. */
+
+ cfpkt_extr_head(pkt, &tmp32, 4);
+ linkparam.u.rfm.connid =
+ cfglu_le32_to_cpu(tmp32);
+ cp = (uint8 *) linkparam.u.rfm.volume;
+ for (cfpkt_extr_head(pkt, &tmp, 1);
+ cfpkt_more(pkt) && tmp != '\0';
+ cfpkt_extr_head(pkt, &tmp, 1))
+ *cp++ = tmp;
+ *cp = '\0';
+
+ /* Link ID */
+ cfpkt_extr_head(pkt, &linkid, 1);
+
+ break;
+ case CFCTRL_SRV_UTIL:
+
+ /* construct a frame Conver
+ DatagramConnectionID to network format long
+ and copy it out.. */
+ /* Fifosize KB */
+ cfpkt_extr_head(pkt, &tmp16, 2);
+ linkparam.u.utility.fifosize_kb =
+ cfglu_le16_to_cpu(tmp16);
+ /* Fifosize bufs */
+ cfpkt_extr_head(pkt, &tmp16, 2);
+ linkparam.u.utility.fifosize_bufs =
+ cfglu_le16_to_cpu(tmp16);
+
+ /* name */
+ cp = (uint8 *) linkparam.u.utility.name;
+ cfglu_assert(sizeof(linkparam.u.utility.name)
+ >= UTILITY_NAME_LENGTH);
+ for (i = 0;
+ i < UTILITY_NAME_LENGTH
+ && cfpkt_more(pkt); i++) {
+ cfpkt_extr_head(pkt, &tmp, 1);
+ *cp++ = tmp;
+ }
+ /* Length */
+ cfpkt_extr_head(pkt, &len, 1);
+ linkparam.u.utility.paramlen = len;
+ /* Param Data */
+ cp = linkparam.u.utility.params;
+ while (cfpkt_more(pkt) && len--) {
+ cfpkt_extr_head(pkt, &tmp, 1);
+ *cp++ = tmp;
+ }
+
+ /* Link ID */
+ cfpkt_extr_head(pkt, &linkid, 1);
+
+ /* Length */
+ cfpkt_extr_head(pkt, &len, 1);
+
+ /* Param Data */
+ cfpkt_extr_head(pkt, ¶m, len);
+
+ break;
+ default:
+ CFLOG_ERROR(("cfctrl_rec:Request setup "
+ "- invalid link type (%d)",
+ serv));
+ goto error;
+ }
+
+ if (cfpkt_erroneous(pkt)) {
+ CFLOG_ERROR(("cfctrl: Packet is erroneous!"));
+ goto error;
+ }
+ CFLOG_TRACE(("cfctrl: success parsing linksetup_rsp"));
+ rsp.cmd = cmd;
+ rsp.param = linkparam;
+ req = cfctrl_remove_req(cfctrl, &rsp);
+
+ if (CFCTRL_ERR_BIT == (CFCTRL_ERR_BIT & cmdrsp)) {
+ CFLOG_ERROR(("CaifChannel:Invalid O/E bit "
+ "on CAIF control channel"));
+ cfctrl->res.reject_rsp(cfctrl->serv.layer.up,
+ 0,
+ req ? req->client_layer
+ : NULL);
+ } else {
+ cfctrl->res.linksetup_rsp(cfctrl->serv.
+ layer.up, linkid,
+ serv, physlinkid,
+ req ? req->
+ client_layer : NULL);
+ }
+
+ if (req != NULL)
+ cfglu_free(req);
+ }
+ break;
+
+ case CFCTRL_CMD_LINK_DESTROY:
+ cfpkt_extr_head(pkt, &linkid, 1);
+ rsp.cmd = cmd;
+ rsp.channel_id = linkid;
+ req = cfctrl_remove_req(cfctrl, &rsp);
+ cfctrl->res.linkdestroy_rsp(cfctrl->serv.layer.up, linkid,
+ req ? req->client_layer : NULL);
+ if (req != NULL)
+ cfglu_free(req);
+ break;
+
+ case CFCTRL_CMD_LINK_ERR:
+ CFLOG_ERROR(("cfctrl: Frame Error Indication received \n"));
+ cfctrl->res.linkerror_ind();
+ break;
+
+ case CFCTRL_CMD_ENUM:
+ cfctrl->res.enum_rsp();
+ break;
+
+ case CFCTRL_CMD_SLEEP:
+ cfctrl->res.sleep_rsp();
+ break;
+
+ case CFCTRL_CMD_WAKE:
+ cfctrl->res.wake_rsp();
+ break;
+
+ case CFCTRL_CMD_LINK_RECONF:
+ cfctrl->res.restart_rsp();
+ break;
+
+ case CFCTRL_CMD_RADIO_SET:
+ cfctrl->res.radioset_rsp();
+ break;
+
+ default:
+ CFLOG_ERROR(("CAIF: Unrecognized Control Frame\n"));
+ goto error;
+ break;
+ }
+ ret = 0;
+error:
+ cfpkt_destroy(pkt);
+ return ret;
+}
+
+static void cfctrl_ctrlcmd(layer_t *layr, caif_ctrlcmd_t ctrl, int phyid)
+{
+ struct cfctrl *this = container_obj(layr);
+ CFLOG_ENTER(("\n"));
+ switch (ctrl) {
+ case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND:
+ case CAIF_CTRLCMD_FLOW_OFF_IND:
+ if (this->first_req != NULL) {
+ CFLOG_WARN(("cfctrl:"
+ "Received flow off in control layer"));
+ }
+ break;
+
+ default:
+ break;
+ }
+ CFLOG_EXIT(("\n"));
+}
diff --git a/net/caif/generic/cfdgml.c b/net/caif/generic/cfdgml.c
new file mode 100644
index 0000000..4f44a24
--- /dev/null
+++ b/net/caif/generic/cfdgml.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2009
+ *
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ *
+ */
+
+
+
+
+
+
+
+
+#include "caif_layer.h"
+#include "cfglue.h"
+#include "cfsrvl.h"
+#include "cfpkt.h"
+#define container_obj(layr) ((cfsrvl_t *) layr)
+
+#define DGM_CMD_BIT 0x80
+#define DGM_FLOW_OFF 0x81
+#define DGM_FLOW_ON 0x80
+#define DGM_CTRL_PKT_SIZE 1
+
+static int cfdgml_receive(layer_t *layr, cfpkt_t *pkt);
+static int cfdgml_transmit(layer_t *layr, transmt_info *dummy, cfpkt_t *pkt);
+
+layer_t *cfdgml_create(uint8 channel_id, uint8 phyid)
+{
+ cfsrvl_t *dgm = cfglu_alloc(sizeof(cfsrvl_t));
+ cfglu_assert(offsetof(cfsrvl_t, layer) == 0);
+ CFLOG_ENTER(("\n"));
+ memset(dgm, 0, sizeof(cfsrvl_t));
+ cfsrvl_init(dgm, channel_id, phyid);
+ dgm->layer.receive = cfdgml_receive;
+ dgm->layer.transmit = cfdgml_transmit;
+ sprintf(dgm->layer.name, "dgm%d", channel_id);
+ CFLOG_EXIT(("\n"));
+ return &dgm->layer;
+}
+
+static int cfdgml_receive(layer_t *layr, cfpkt_t *pkt)
+{
+ uint8 cmd = -1;
+ uint8 dgmhdr[3];
+ int ret;
+ cfglu_assert(layr->up != NULL);
+ cfglu_assert(layr->receive != NULL);
+ cfglu_assert(layr->ctrlcmd != NULL);
+ CFLOG_ENTER(("\n"));
+
+ if (!cfpkt_extr_head(pkt, &cmd, 1)) {
+ CFLOG_ERROR(("cfdgml: Packet is erroneous!\n"));
+ cfpkt_destroy(pkt);
+ CFLOG_EXIT(("\n"));
+ return CFGLU_EPROTO;
+ }
+
+ if ((cmd & DGM_CMD_BIT) == 0) {
+ if (!cfpkt_extr_head(pkt, &dgmhdr, 3)) {
+ CFLOG_ERROR(("cfdgml: Packet is erroneous!\n"));
+ cfpkt_destroy(pkt);
+ return CFGLU_EPROTO;
+ }
+ ret = layr->up->receive(layr->up, pkt);
+ CFLOG_EXIT(("\n"));
+ return ret;
+ }
+
+ switch (cmd) {
+ case DGM_FLOW_OFF: /* FLOW OFF */
+ layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_OFF_IND, 0);
+ cfpkt_destroy(pkt);
+ CFLOG_EXIT(("\n"));
+ return 0;
+ case DGM_FLOW_ON: /* FLOW ON */
+ layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_ON_IND, 0);
+ cfpkt_destroy(pkt);
+ CFLOG_EXIT(("\n"));
+ return 0;
+ default:
+ cfpkt_destroy(pkt);
+ CFLOG_ERROR(("cfdgml: Unknown datagram control %d (0x%x)\n",
+ cmd, cmd));
+ CFLOG_EXIT(("\n"));
+ return CFGLU_EPROTO;
+ }
+ CFLOG_EXIT(("\n"));
+}
+
+static int cfdgml_transmit(layer_t *layr, transmt_info *dummy, cfpkt_t *pkt)
+{
+ uint32 zero = 0;
+ transmt_info info;
+ cfsrvl_t *service = container_obj(layr);
+ int ret;
+ CFLOG_ENTER(("\n"));
+ if (!cfsrvl_ready(service, &ret))
+ return ret;
+
+ cfpkt_add_head(pkt, &zero, 4);
+
+ /* Add info for MUX-layer to route the packet out */
+ info.channel_id = service->layer.id;
+ info.phid = service->phid;
+ /* For optimizing alignment we add up the size of CAIF header before
+ payload */
+ info.hdr_len = 4;
+ ret = layr->dn->transmit(layr->dn, &info, pkt);
+ if (ret < 0) {
+ uint32 tmp32;
+ cfpkt_extr_head(pkt, &tmp32, 4);
+ }
+ CFLOG_EXIT(("\n"));
+ return ret;
+}
diff --git a/net/caif/generic/cffrml.c b/net/caif/generic/cffrml.c
new file mode 100644
index 0000000..ee28121
--- /dev/null
+++ b/net/caif/generic/cffrml.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2009
+ *
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ *
+ * Caif Framing Layer.
+ */
+
+#define container_obj(layr) cfglu_container_of(layr, cffrml_t, layer)
+
+#include "caif_layer.h"
+#include "cfpkt.h"
+#include "cfglue.h"
+#include "fcs.h"
+#include "cffrml.h"
+
+struct _cffrml_t {
+ layer_t layer;
+ bool dofcs; /* !< FCS active */
+};
+
+
+static int cffrml_receive(layer_t *layr, cfpkt_t *pkt);
+static int cffrml_transmit(layer_t *layr, transmt_info *info, cfpkt_t *pkt);
+static void cffrml_ctrlcmd(layer_t *layr, caif_ctrlcmd_t ctrl, int phyid);
+
+uint32 cffrml_rcv_error;
+uint32 cffrml_rcv_checsum_error;
+layer_t *cffrml_create(uint16 phyid, bool use_fcs)
+{
+ cffrml_t *this = cfglu_alloc(sizeof(cffrml_t));
+ cfglu_assert(offsetof(cffrml_t, layer) == 0);
+
+ memset(this, 0, sizeof(layer_t));
+ this->layer.receive = cffrml_receive;
+ this->layer.transmit = cffrml_transmit;
+ this->layer.ctrlcmd = cffrml_ctrlcmd;
+ sprintf(this->layer.name, "frm%d", phyid);
+ this->dofcs = use_fcs;
+ this->layer.id = phyid;
+ CFLOG_TRACE(("cffrml: FCS=%d\n", use_fcs));
+ return (layer_t *) this;
+}
+
+void cffrml_set_uplayer(layer_t *this, layer_t *up)
+{
+ this->up = up;
+}
+
+void cffrml_set_dnlayer(layer_t *this, layer_t *dn)
+{
+ this->dn = dn;
+}
+
+static uint16 cffrml_checksum(uint16 chks, void *buf, uint16 len)
+{
+ /* FIXME: FCS should be moved to glue in order to use OS-Specific
+ solutions */
+ return fcs16(chks, buf, len);
+}
+
+static int cffrml_receive(layer_t *layr, cfpkt_t *pkt)
+{
+ uint16 tmp;
+ uint16 len;
+ uint16 hdrchks;
+ uint16 pktchks;
+ cffrml_t *this;
+ this = container_obj(layr);
+
+
+ (void) cfpkt_extr_head(pkt, &tmp, 2);
+ len = cfglu_le16_to_cpu(tmp);
+
+ /* Subtract for FCS on length if FCS is not used. */
+ if (!this->dofcs)
+ len -= 2;
+
+ if (cfpkt_setlen(pkt, len) < 0) {
+ ++cffrml_rcv_error;
+ CFLOG_ERROR(("cffrml: Framing length error (%d)\n", len));
+ cfpkt_destroy(pkt);
+ return CFGLU_EPKT;
+ }
+ /* Don't do extract if fcs is false, rather to setlen - then we don'
+ get cach-miss */
+ if (this->dofcs) {
+ (void) cfpkt_extr_trail(pkt, &tmp, 2);
+ hdrchks = cfglu_le16_to_cpu(tmp);
+ pktchks = cfpkt_iterate(pkt, cffrml_checksum, 0xffff);
+ if (pktchks != hdrchks) {
+ cfpkt_add_trail(pkt, &tmp, 2);
+ ++cffrml_rcv_error;
+ ++cffrml_rcv_checsum_error;
+ CFLOG_ERROR(("cffrml: Frame checksum error "
+ "(0x%x != 0x%x)\n", hdrchks, pktchks));
+ return CFGLU_EFCS;
+ }
+ }
+ if (cfpkt_erroneous(pkt)) {
+ ++cffrml_rcv_error;
+ CFLOG_ERROR(("cffrml: Packet is erroneous!\n"));
+ cfpkt_destroy(pkt);
+ return CFGLU_EPKT;
+ }
+ return layr->up->receive(layr->up, pkt);
+}
+
+static int cffrml_transmit(layer_t *layr, transmt_info *info, cfpkt_t *pkt)
+{
+ int tmp;
+ uint16 chks;
+ uint16 len;
+ int ret;
+ cffrml_t *this = container_obj(layr);
+ if (this->dofcs) {
+ chks = cfpkt_iterate(pkt, cffrml_checksum, 0xffff);
+ tmp = cfglu_cpu_to_le16(chks);
+ cfpkt_add_trail(pkt, &tmp, 2);
+ } else {
+ cfpkt_pad_trail(pkt, 2);
+ }
+
+ len = cfpkt_getlen(pkt);
+ tmp = cfglu_cpu_to_le16(len);
+ cfpkt_add_head(pkt, &tmp, 2);
+ info->hdr_len += 2;
+ if (cfpkt_erroneous(pkt)) {
+ CFLOG_ERROR(("cffrml: Packet is erroneous!\n"));
+ return CFGLU_EPROTO;
+ }
+ ret = layr->dn->transmit(layr->dn, info, pkt);
+ if (ret < 0) {
+ /* Remove header on faulty packet */
+ cfpkt_extr_head(pkt, &tmp, 2);
+ }
+ return ret;
+}
+
+static void cffrml_ctrlcmd(layer_t *layr, caif_ctrlcmd_t ctrl, int phyid)
+{
+ if (layr->up->ctrlcmd)
+ layr->up->ctrlcmd(layr->up, ctrl, layr->id);
+}
diff --git a/net/caif/generic/cflist.c b/net/caif/generic/cflist.c
new file mode 100644
index 0000000..1eb36e3
--- /dev/null
+++ b/net/caif/generic/cflist.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2009
+ *
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ *
+ */
+
+
+
+
+
+#include "cfpkt.h"
+#include "cfglue.h"
+#include "cflst.h"
+
+void cflst_init(layer_t **lst)
+{
+ *lst = NULL;
+}
+
+
+layer_t *cflst_remove(layer_t **lst, layer_t *elem)
+{
+ layer_t *tmp;
+ if (*lst == NULL)
+ return NULL;
+
+ tmp = (*lst);
+ (*lst) = (*lst)->next;
+ return tmp;
+}
+
+/** Adds an element from the Queue */
+
+void cflst_insert(layer_t **lst, layer_t *node)
+{
+ layer_t *tmp;
+ node->next = NULL;
+ if ((*lst) == NULL) {
+ (*lst) = node;
+ return;
+ }
+ tmp = *lst;
+ while (tmp->next != NULL)
+ tmp = tmp->next;
+ tmp->next = node;
+}
+
+
+
+bool cflst_put(layer_t **lst, uint8 id, layer_t *node)
+{
+ if (cflst_get(lst, id) != NULL) {
+ CFLOG_ERROR(("CAIF: cflst_put duplicate key\n"));
+ return false;
+ }
+ node->id = id;
+ cflst_insert(lst, node);
+ return true;
+}
+
+layer_t *cflst_get(layer_t * *lst, uint8 id)
+{
+ layer_t *node;
+ for (node = (*lst); node != NULL; node = node->next) {
+ if (id == node->id)
+ return node;
+ }
+ return NULL;
+}
+
+layer_t *cflst_del(layer_t * *lst, uint8 id)
+{
+ layer_t *iter;
+ layer_t *node = NULL;
+
+ if ((*lst) == NULL)
+ return NULL;
+
+ if ((*lst)->id == id) {
+ node = (*lst);
+ (*lst) = (*lst)->next;
+ node->next = NULL;
+
+ return node;
+ }
+
+ for (iter = (*lst); iter->next != NULL; iter = iter->next) {
+ if (id == iter->next->id) {
+ node = iter->next;
+ iter->next = iter->next->next;
+ node->next = NULL;
+ return node;
+ }
+ }
+ return NULL;
+}
diff --git a/net/caif/generic/cfloopcfg.c b/net/caif/generic/cfloopcfg.c
new file mode 100644
index 0000000..25ffd69
--- /dev/null
+++ b/net/caif/generic/cfloopcfg.c
@@ -0,0 +1,93 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+
+
+
+
+
+
+#include "caif_layer.h"
+#include "cfpkt.h"
+#include "cfglue.h"
+#include "cflst.h"
+#include "cfctrl.h"
+#include "cfglue.h"
+#include "cfmuxl.h"
+#include "cffrml.h"
+#include "cfspil.h"
+#include "cfserl.h"
+#include "cfcnfg.h"
+#include "cfshml.h"
+#include "cfmsll.h"
+struct _cfloopcfg_t {
+ layer_t *loop;
+
+};
+typedef struct _cfloopcfg_t cfloopcfg_t;
+
+cfloopcfg_t *cfloopcfg_create(void)
+{
+ extern layer_t *cflooplayer_create(void);
+ cfloopcfg_t *this;
+ this = cfglu_alloc(sizeof(cfloopcfg_t));
+ memset(this, 0, sizeof(cfloopcfg_t));
+ this->loop = cflooplayer_create();
+ return this;
+}
+
+void
+cfloopcfg_add_phy_layer(cfloopcfg_t *cnfg, cfcnfg_phy_type_t phy_type,
+ layer_t *phy_layer)
+{
+ if (phy_type == CFPHYTYPE_SERIAL) {
+ layer_t *frml = cffrml_create(1, true);
+ layer_t *cfser;
+ cfser = cfserl_create(phy_type, 0, serial_use_stx);
+ layer_set_dn(cnfg->loop, frml);
+ layer_set_up(frml, cnfg->loop);
+ layer_set_dn(frml, cfser);
+ layer_set_up(cfser, frml);
+ layer_set_dn(cfser, phy_layer);
+ layer_set_up(phy_layer, cfser);
+ }
+ if (phy_type == CFPHYTYPE_SPI) {
+ layer_t *frml = cffrml_create(1, false);
+ layer_t *cfspi;
+ cfspi = cfspil_create(phy_type, 0);
+ layer_set_dn(cnfg->loop, frml);
+ layer_set_up(frml, cnfg->loop);
+ layer_set_dn(frml, cfspi);
+ layer_set_up(cfspi, frml);
+ layer_set_dn(cfspi, phy_layer);
+ layer_set_up(phy_layer, cfspi);
+ }
+ if (phy_type == CFPHYTYPE_SHM) {
+ layer_t *frml = cffrml_create(1, false);
+ layer_t *cfspi;
+ cfspi = cfshml_create(phy_type, 0);
+ layer_set_dn(cnfg->loop, frml);
+ layer_set_up(frml, cnfg->loop);
+ layer_set_dn(frml, cfspi);
+ layer_set_up(cfspi, frml);
+ layer_set_dn(cfspi, phy_layer);
+ layer_set_up(phy_layer, cfspi);
+ }
+ if (phy_type == CFPHYTYPE_MSL) {
+ layer_t *frml = cffrml_create(1, false);
+ layer_t *cfspi;
+ cfspi = cfmsll_create(phy_type, 0);
+ layer_set_dn(cnfg->loop, frml);
+ layer_set_up(frml, cnfg->loop);
+ layer_set_dn(frml, cfspi);
+ layer_set_up(cfspi, frml);
+ layer_set_dn(cfspi, phy_layer);
+ layer_set_up(phy_layer, cfspi);
+ }
+}
diff --git a/net/caif/generic/cflooplayer.c b/net/caif/generic/cflooplayer.c
new file mode 100644
index 0000000..c6ea22c
--- /dev/null
+++ b/net/caif/generic/cflooplayer.c
@@ -0,0 +1,116 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+#include "caif_layer.h"
+#include "cfglue.h"
+#include "cfpkt.h"
+#include "cfglue.h"
+#include "cflst.h"
+#include "cfglue.h"
+#include "cfsrvl.h"
+#include "cffrml.h"
+#include "cfctrl.h"
+
+static int cflooplayer_receive(layer_t *layr, cfpkt_t *pkt);
+static uint8 linkid;
+static int linkused[256];
+static cfglu_lock_t linkid_lock;
+
+layer_t *cflooplayer_create(void)
+{
+ layer_t *this = cfglu_alloc(sizeof(layer_t));
+ memset(this, 0, sizeof(layer_t));
+ this->receive = cflooplayer_receive;
+ cfglu_init_lock(linkid_lock);
+ sprintf(this->name, "loop1");
+ return this;
+}
+
+
+static int cflooplayer_receive(layer_t *layr, cfpkt_t *inpkt)
+{
+ uint8 id;
+ transmt_info info;
+ uint8 *buf;
+ uint16 pktlen;
+ cfpkt_t *pkt;
+ int ret;
+ caif_packet_funcs_t f = cfpkt_get_packet_funcs();
+ memset(&info, 0, sizeof(info));
+ pktlen = cfpkt_getlen(inpkt);
+ ret = f.cfpkt_raw_extract(inpkt, (void **) &buf, pktlen);
+ cfglu_assert(ret > 0);
+ pkt = f.cfpkt_create_recv_pkt(buf, pktlen);
+ cfpkt_destroy(inpkt);
+ (void) cfpkt_extr_head(pkt, &id, 1);
+ if (id != 0) {
+ /* This is not a Control Packet, just loop it */
+ cfpkt_add_head(pkt, &id, 1);
+ } else {
+ /* This is a Control Packet */
+ uint8 tmp;
+ uint8 cmdrsp;
+ uint8 cmd;
+ uint8 linktype = 0;
+
+ (void) cfpkt_extr_head(pkt, &cmdrsp, 1);
+ cmd = cmdrsp & CFCTRL_CMD_MASK;
+
+ switch (cmd) {
+ case CFCTRL_CMD_LINK_SETUP:
+ {
+ cfglu_assert(!cfpkt_erroneous(pkt));
+ cfglu_assert(cfpkt_more(pkt));
+ cfpkt_peek_head(pkt, &linktype, 1);
+ cfglu_assert(!cfpkt_erroneous(pkt));
+ cmdrsp |= CFCTRL_RSP_BIT;
+ cfpkt_add_head(pkt, &cmdrsp, 1);
+ cfpkt_add_head(pkt, &id, 1);
+ cfglu_lock(linkid_lock);
+ for (linkid = 0x41; linkid < 255; linkid++) {
+ if (!linkused[linkid]) {
+ linkused[linkid] = 1;
+ break;
+ }
+ }
+ if (linkid == 255) {
+ CFLOG_WARN(("cflooplayer_receive:"
+ " no free link id's\n"));
+ }
+ CFLOG_WARN(("cflooplayer_receive: setup "
+ "linkid = %d\n", linkid));
+ cfpkt_add_trail(pkt, &linkid, 1);
+ cfglu_unlock(linkid_lock);
+
+
+
+ if (linktype == 0x06) {
+ tmp = 0x01;
+ cfpkt_add_trail(pkt, &tmp, 1);
+ cfpkt_add_trail(pkt, &tmp, 1);
+ }
+ break;
+ }
+ case CFCTRL_CMD_LINK_DESTROY:
+ cfglu_lock(linkid_lock);
+ (void) cfpkt_peek_head(pkt, &linkid, 1);
+ CFLOG_WARN(("cflooplayer_receive: destroy "
+ "linkid = %d\n", linkid));
+ linkused[linkid] = 0;
+ cfglu_unlock(linkid_lock);
+ /* fallthrough */
+ default:
+ cmdrsp |= CFCTRL_RSP_BIT;
+ cfpkt_add_head(pkt, &cmdrsp, 1);
+ cfpkt_add_head(pkt, &id, 1);
+ break;
+ }
+ }
+ return layr->dn->transmit(layr->dn, &info, pkt);
+}
diff --git a/net/caif/generic/cfmsll.c b/net/caif/generic/cfmsll.c
new file mode 100644
index 0000000..3e781db
--- /dev/null
+++ b/net/caif/generic/cfmsll.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2009
+ *
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ *
+ */
+
+
+#include "caif_layer.h"
+#include "cfpkt.h"
+#include "cfglue.h"
+#include "cfmsll.h"
+
+static int cfmsll_receive(layer_t *layr, cfpkt_t *pkt);
+static int cfmsll_transmit(layer_t *layr, transmt_info *info, cfpkt_t *pkt);
+
+layer_t *cfmsll_create(int type, int instance)
+{
+ layer_t *this = cfglu_alloc(sizeof(layer_t));
+ memset(this, 0, sizeof(layer_t));
+ this->receive = cfmsll_receive;
+ this->transmit = cfmsll_transmit;
+ this->type = type;
+ sprintf(this->name, "msl%d", instance);
+ return this;
+}
+
+void cfmsll_set_uplayer(layer_t *this, layer_t *up)
+{
+ this->up = up;
+}
+
+void cfmsll_set_dnlayer(layer_t *this, layer_t *dn)
+{
+ this->dn = dn;
+
+}
+
+static int cfmsll_receive(layer_t *layr, cfpkt_t *pkt)
+{
+ /* Send the first part of packet upwards */
+ int ret = layr->up->receive(layr->up, pkt);
+ /* FCS Error don't delete the packet */
+ if (ret == CFGLU_EFCS)
+ cfpkt_destroy(pkt);
+ return ret;
+
+}
+
+static int cfmsll_transmit(layer_t *layr, transmt_info *info, cfpkt_t *pkt)
+{
+ return layr->dn->transmit(layr->dn, info, pkt);
+}
diff --git a/net/caif/generic/cfmuxl.c b/net/caif/generic/cfmuxl.c
new file mode 100644
index 0000000..f09857b
--- /dev/null
+++ b/net/caif/generic/cfmuxl.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2009
+ *
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ *
+ */
+
+#include "cfpkt.h"
+#include "cfglue.h"
+#include "cflst.h"
+#include "cfglue.h"
+#include "cfmuxl.h"
+#include "cfsrvl.h"
+#include "cffrml.h"
+
+#define container_obj(layr) cfglu_container_of(layr, struct cfmuxl_t, layer)
+
+
+#define CAIF_CTRL_CHANNEL 0
+#define UP_CACHE_SIZE 8
+#define DN_CACHE_SIZE 8
+
+
+struct cfmuxl_t {
+ layer_t layer;
+ layer_t *up_cache[UP_CACHE_SIZE];
+ layer_t *dn_cache[DN_CACHE_SIZE];
+ /*
+ * transmit_lock is a read-write lock.
+ * readlock is set when packets are transmitted.
+ * writelock is set when inserting or removing down layers.
+ */
+ cfglu_rwlock_t transmit_lock;
+
+ /*
+ * receive_lock is a read-write lock.
+ * readlock is set when packets are received.
+ * writelock is set when inserting or removing up-wards layers.
+ */
+ cfglu_rwlock_t receive_lock;
+
+};
+
+static int cfmuxl_receive(layer_t *layr, cfpkt_t *pkt);
+static int cfmuxl_transmit(layer_t *layr, transmt_info *info, cfpkt_t *pkt);
+static void cfmuxl_ctrlcmd(layer_t *layr, caif_ctrlcmd_t ctrl, int phyid);
+static layer_t *get_up(struct cfmuxl_t *muxl, int id);
+
+layer_t *cfmuxl_create()
+{
+ struct cfmuxl_t *this = cfglu_alloc(sizeof(struct cfmuxl_t));
+ memset(this, 0, sizeof(*this));
+ this->layer.receive = cfmuxl_receive;
+ this->layer.transmit = cfmuxl_transmit;
+ this->layer.ctrlcmd = cfmuxl_ctrlcmd;
+ cfglu_init_rwlock(this->transmit_lock);
+ cfglu_init_rwlock(this->receive_lock);
+ sprintf(this->layer.name, "mux");
+ return &this->layer;
+}
+
+bool cfmuxl_set_uplayer(layer_t *layr, layer_t *up, uint8 linkid)
+{
+ bool ok;
+ struct cfmuxl_t *muxl = container_obj(layr);
+ CFLOG_ENTER(("layr:%p up:%p linkid:%d\n", (void *) layr, (void *) up,
+ linkid));
+ cfglu_write_lock(muxl->receive_lock);
+ ok = cflst_put(&muxl->layer.up, linkid, up);
+ cfglu_write_unlock(muxl->receive_lock);
+ CFLOG_EXIT(("ok=%d\n", ok));
+ return ok;
+}
+
+bool cfmuxl_is_phy_inuse(layer_t *layr, uint8 phyid)
+{
+ layer_t *p;
+ struct cfmuxl_t *muxl = container_obj(layr);
+ bool match = false;
+ cfglu_read_lock(muxl->receive_lock);
+ CFLOG_ENTER(("\n"));
+
+ for (p = layr->up; p != NULL; p = p->next) {
+ if (cfsrvl_phyid_match(p, phyid)) {
+ match = true;
+ break;
+ }
+ }
+ cfglu_read_unlock(muxl->receive_lock);
+ CFLOG_EXIT(("\n"));
+ return match;
+}
+
+uint8 cfmuxl_get_phyid(layer_t *layr, uint8 channel_id)
+{
+ layer_t *up;
+ struct cfmuxl_t *muxl = container_obj(layr);
+ cfglu_read_lock(muxl->receive_lock);
+ CFLOG_ENTER(("\n"));
+ up = get_up(muxl, channel_id);
+ if (up != NULL)
+ return cfsrvl_getphyid(up);
+ else
+ return 0;
+}
+
+bool cfmuxl_set_dnlayer(layer_t *layr, layer_t *dn, uint8 phyid)
+{
+ bool ok;
+ struct cfmuxl_t *muxl = (struct cfmuxl_t *) layr;
+ CFLOG_ENTER(("\n"));
+ cfglu_write_lock(muxl->transmit_lock);
+ ok = cflst_put(&muxl->layer.dn, phyid, dn);
+ cfglu_write_unlock(muxl->transmit_lock);
+ CFLOG_EXIT(("\n"));
+ return ok;
+}
+
+layer_t *cfmuxl_remove_dnlayer(layer_t *layr, uint8 phyid)
+{
+ struct cfmuxl_t *muxl = container_obj(layr);
+ layer_t *dn;
+ CFLOG_ENTER(("\n"));
+ cfglu_write_lock(muxl->transmit_lock);
+ memset(muxl->dn_cache, 0, sizeof(muxl->dn_cache));
+ dn = cflst_del(&muxl->layer.dn, phyid);
+ cfglu_assert(dn != NULL);
+ cfglu_write_unlock(muxl->transmit_lock);
+ CFLOG_EXIT(("\n"));
+ return dn;
+}
+
+
+/* Invariant: lock is taken */
+static layer_t *get_up(struct cfmuxl_t *muxl, int id)
+{
+ layer_t *up;
+ int idx = id % UP_CACHE_SIZE;
+ CFLOG_ENTER(("id:%d\n", id));
+ up = muxl->up_cache[idx];
+ if (up == NULL || up->id != id) {
+ up = cflst_get(&muxl->layer.up, id);
+ muxl->up_cache[idx] = up;
+ }
+ CFLOG_EXIT(("id: %d up:%p", id, (void *) up));
+ return up;
+
+}
+
+/* Invariant: lock is taken */
+static layer_t *get_dn(struct cfmuxl_t *muxl, int id)
+{
+ layer_t *dn;
+ int idx = id % DN_CACHE_SIZE;
+ CFLOG_ENTER(("\n"));
+ dn = muxl->dn_cache[idx];
+ if (dn == NULL || dn->id != id) {
+ dn = cflst_get(&muxl->layer.dn, id);
+ muxl->dn_cache[idx] = dn;
+ }
+ CFLOG_EXIT(("\n"));
+ return dn;
+}
+
+
+
+
+
+layer_t *cfmuxl_remove_uplayer(layer_t *layr, uint8 id)
+{
+ layer_t *up;
+ struct cfmuxl_t *muxl = container_obj(layr);
+ CFLOG_ENTER(("\n"));
+ cfglu_write_lock(muxl->receive_lock);
+ memset(muxl->up_cache, 0, sizeof(muxl->up_cache));
+ up = cflst_del(&muxl->layer.up, id);
+ cfglu_assert(up != NULL);
+ cfglu_write_unlock(muxl->receive_lock);
+ CFLOG_EXIT(("\n"));
+ return up;
+}
+
+
+static int cfmuxl_receive(layer_t *layr, cfpkt_t *pkt)
+{
+ int ret;
+ struct cfmuxl_t *muxl = container_obj(layr);
+ uint8 id;
+ layer_t *up;
+ CFLOG_ENTER(("\n"));
+ if (!cfpkt_extr_head(pkt, &id, 1)) {
+ CFLOG_ERROR(("cfmuxl: erroneous Caif Packet\n"));
+ cfpkt_destroy(pkt);
+ CFLOG_EXIT(("error"));
+ return CFGLU_EPKT;
+ }
+ cfglu_read_lock(muxl->receive_lock);
+ up = get_up(muxl, id);
+ if (up == NULL) {
+ CFLOG_WARN(("CAIF: Received data on unknown link ID = %d "
+ "(0x%x) up == NULL", id, id));
+ cfpkt_destroy(pkt);
+ cfglu_read_unlock(muxl->receive_lock);
+ /* Don't return ERROR since modem miss-behaves and sends ou
+ flow before linksetup rsp. */
+ CFLOG_EXIT(("error - unknown channel"));
+ return /* CFGLU_EPROT; */ CFGLU_EOK;
+ }
+
+ cfglu_read_unlock(muxl->receive_lock);
+
+ /* FIXME: We need a ref-count in order to safely free
+ * the up layer
+ */
+ ret = up->receive(up, pkt);
+
+ CFLOG_EXIT(("\n"));
+
+ return ret;
+}
+
+static int cfmuxl_transmit(layer_t *layr, transmt_info *info, cfpkt_t *pkt)
+{
+ int ret;
+ struct cfmuxl_t *muxl = container_obj(layr);
+ uint8 linkid;
+ layer_t *dn;
+ CFLOG_ENTER(("\n"));
+ cfglu_read_lock(muxl->transmit_lock);
+
+ dn = get_dn(muxl, info->phid);
+ if (dn == NULL) {
+ CFLOG_WARN(("CAIF: Send data on unknown phy ID = %d (0x%x)\n",
+ info->phid, info->phid));
+ cfglu_read_unlock(muxl->transmit_lock);
+ CFLOG_EXIT(("error"));
+ return CFGLU_ENOTCONN;
+ }
+ info->hdr_len += 1;
+ linkid = info->channel_id;
+ cfpkt_add_head(pkt, &linkid, 1);
+
+ ret = dn->transmit(dn, info, pkt);
+ if (ret < 0) {
+ /** Remove MUX protocol header upon error */
+ cfpkt_extr_head(pkt, &linkid, 1);
+ }
+ cfglu_read_unlock(muxl->transmit_lock);
+ CFLOG_EXIT(("\n"));
+ return ret;
+}
+
+static void cfmuxl_ctrlcmd(layer_t *layr, caif_ctrlcmd_t ctrl, int phyid)
+{
+ layer_t *p;
+ struct cfmuxl_t *muxl = container_obj(layr);
+ for (p = muxl->layer.up; p != NULL; p = p->next) {
+ if (cfsrvl_phyid_match(p, phyid))
+ p->ctrlcmd(p, ctrl, phyid);
+ }
+}
diff --git a/net/caif/generic/cfpkt_skbuff.c b/net/caif/generic/cfpkt_skbuff.c
new file mode 100644
index 0000000..0f89907
--- /dev/null
+++ b/net/caif/generic/cfpkt_skbuff.c
@@ -0,0 +1,590 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+
+
+
+
+
+#ifndef CAIF_KERNEL
+#include <string.h>
+#include "skbuff.h"
+#else /* */
+#include <linux/string.h>
+#include <linux/skbuff.h>
+#include <linux/hardirq.h>
+#endif /* CAIF_KERNEL */
+
+#include "cfglue.h"
+#include "cfpkt.h"
+
+/* Maximum size of CAIF header */
+#define PKT_PREFIX CAIF_NEEDED_HEADROOM
+
+/* Maximum size of CAIF trailer */
+#define PKT_POSTFIX CAIF_NEEDED_TAILROOM
+
+#define PKT_LEN_WHEN_EXTENDING 128
+
+#define CFPKT_PRIV_DATA(pkt) ((struct cfpkt_priv_data_t *) (&pkt->skb.cb[0]))
+
+#define PKT_ERROR(pkt, errmsg) do { \
+ CFPKT_PRIV_DATA(pkt)->erronous = true; \
+ skb_reset_tail_pointer(&pkt->skb); \
+ CFLOG_WARN((errmsg)); \
+ } while (0)
+
+#ifdef CFKPKT_PACKET_DUMP
+char pkt_debug_buffer[256];
+#define pkt_dump_debug(pkt) do { \
+ cfpkt_log_pkt((pkt), pkt_debug_buffer, 250); \
+ printk(KERN_DEBUG "%d: nonlinear: %s, fp=%s %s", \
+ __LINE__, \
+ skb_is_nonlinear(&(pkt)->skb) ? "true" : "false", \
+ CFPKT_PRIV_DATA((pkt))->fastpath ? "true" : "false", \
+ pkt_debug_buffer); \
+ } while (0)
+#else
+#define pkt_dump_debug(pkt)
+#endif
+
+struct _cfpktq_t {
+ struct sk_buff_head queue;
+};
+
+/* _cfpkt_t is (memory-wise) an sk_buff, but _cfpkt_t is forward declared in
+ cfpkt.h, so we do it this way (instead if typedef): */
+struct _cfpkt_t {
+ struct sk_buff skb;
+};
+
+
+/* Private data inside SKB */
+struct cfpkt_priv_data_t {
+ bool erronous;
+ /*!<Indicate fastpath, buffer has large enough head and tail room
+ so boundary checks are skipped. */
+ bool fastpath;
+};
+
+cfglu_atomic_t cfpkt_packet_count;
+
+cfpkt_t *cfpkt_fromnative(caif_direction_t dir, void *nativepkt)
+{
+ cfpkt_t *pkt = (cfpkt_t *) nativepkt;
+
+ /* In Linux, "native" is always sk_buff: */
+ struct sk_buff *skb = (struct sk_buff *) nativepkt;
+
+ CFPKT_PRIV_DATA(pkt)->erronous = false;
+
+ /* Set Fastpath if we have enough head room */
+ if (likely(dir == CAIF_DIR_OUT && !skb_is_nonlinear(skb)
+ && skb_headroom(skb) >= PKT_PREFIX
+ && skb_tailroom(skb) >= PKT_POSTFIX))
+ CFPKT_PRIV_DATA(pkt)->fastpath = true;
+ else if (likely(dir == CAIF_DIR_IN && !skb_is_nonlinear(skb)
+ && skb_headlen(skb) > PKT_PREFIX + PKT_POSTFIX))
+ CFPKT_PRIV_DATA(pkt)->fastpath = true;
+ else
+ CFPKT_PRIV_DATA(pkt)->fastpath = false;
+
+ CFLOG_TRACE3(("cfpkt_fromnative: fastpath=%s",
+ CFPKT_PRIV_DATA(pkt)->fastpath ? "true" : "false"));
+ cfglu_atomic_inc(cfpkt_packet_count);
+
+ pkt_dump_debug(pkt);
+ return pkt;
+}
+
+
+inline void *cfpkt_tonative(cfpkt_t *pkt)
+{
+ /* In Linux, "native" is always sk_buff: */
+ pkt_dump_debug(pkt);
+ return (void *) pkt;
+}
+
+
+cfpkt_t *cfpkt_create_pfx(uint16 len, uint16 pfx)
+{
+ struct sk_buff *skb;
+
+ if (likely(in_interrupt()))
+ skb = alloc_skb(len + pfx, GFP_ATOMIC);
+ else
+ skb = alloc_skb(len + pfx, GFP_KERNEL);
+
+ if (unlikely(skb == NULL))
+ return NULL;
+
+ skb_reserve(skb, pfx);
+ cfglu_atomic_inc(cfpkt_packet_count);
+ return (cfpkt_t *) skb;
+}
+
+inline cfpkt_t *cfpkt_create(uint16 len)
+{
+ cfpkt_t *r = cfpkt_create_pfx(len + PKT_POSTFIX, PKT_PREFIX);
+ CFPKT_PRIV_DATA(r)->fastpath = true;
+
+ pkt_dump_debug(r);
+ return r;
+}
+
+void cfpkt_destroy(cfpkt_t *pkt)
+{
+ struct sk_buff *skb = (struct sk_buff *) pkt;
+ cfglu_atomic_dec(cfpkt_packet_count);
+ cfglu_assert(cfglu_atomic_read(cfpkt_packet_count) >= 0);
+ kfree_skb(skb);
+}
+
+inline bool cfpkt_more(cfpkt_t *pkt)
+{
+ struct sk_buff *skb = (struct sk_buff *) pkt;
+ return skb->len > 0;
+}
+
+bool cfpkt_peek_head(cfpkt_t *pkt, void *data, uint16 len)
+{
+ struct sk_buff *skb = (struct sk_buff *) pkt;
+ pkt_dump_debug(pkt);
+ if (likely(CFPKT_PRIV_DATA(pkt)->fastpath)) {
+ memcpy(data, skb->data, len);
+ pkt_dump_debug(pkt);
+ return true;
+ }
+ return cfpkt_extr_head(pkt, data, len) &&
+ cfpkt_add_head(pkt, data, len);
+}
+
+bool cfpkt_extr_head(cfpkt_t *pkt, void *data, uint16 len)
+{
+ struct sk_buff *skb = (struct sk_buff *) pkt;
+ uint8 *from;
+ pkt_dump_debug(pkt);
+
+ if (unlikely(len > skb->len)) {
+ PKT_ERROR(pkt, "cfpkt_extr_head read beyond end of packet\n");
+ CFPKT_PRIV_DATA(pkt)->erronous = true;
+ return false;
+ }
+
+ if (unlikely(!(CFPKT_PRIV_DATA(pkt)->fastpath) &&
+ (len > skb_headlen(skb)))) {
+ if (unlikely(skb_linearize(skb) != 0)) {
+ CFPKT_PRIV_DATA(pkt)->erronous = true;
+ return false;
+ }
+ }
+
+ from = skb_pull(skb, len);
+ from -= len;
+ memcpy(data, from, len);
+ pkt_dump_debug(pkt);
+ return true;
+}
+
+bool cfpkt_extr_trail(cfpkt_t *pkt, void *dta, uint16 len)
+{
+ struct sk_buff *skb = (struct sk_buff *) pkt;
+ uint8 *data = dta;
+ uint8 *from;
+ pkt_dump_debug(pkt);
+ if (unlikely(skb_linearize(skb) != 0)) {
+ CFPKT_PRIV_DATA(pkt)->erronous = true;
+ return false;
+ }
+ pkt_dump_debug(pkt);
+ if (unlikely(skb->data + len > skb_tail_pointer(skb))) {
+ PKT_ERROR(pkt, "cfpkt_extr_trail read beyond end of packet\n");
+ CFPKT_PRIV_DATA(pkt)->erronous = true;
+ return false;
+ }
+ from = skb_tail_pointer(skb) - len;
+ skb_trim(skb, skb->len - len);
+ pkt_dump_debug(pkt);
+ memcpy(data, from, len);
+ return true;
+}
+
+bool cfpkt_pad_trail(cfpkt_t *pkt, uint16 len)
+{
+ return cfpkt_add_body(pkt, NULL, len);
+}
+
+bool cfpkt_add_body(cfpkt_t *pkt, const void *data, uint16 len)
+{
+ struct sk_buff *skb = (struct sk_buff *) pkt;
+ struct sk_buff *lastskb;
+ uint8 *to;
+ uint16 addlen = 0;
+
+ pkt_dump_debug(pkt);
+
+ lastskb = skb;
+
+ /* Do we need to add space at the tail? */
+ if (unlikely(skb_tailroom(skb) < len)) {
+ if (likely(len < PKT_LEN_WHEN_EXTENDING))
+ addlen = PKT_LEN_WHEN_EXTENDING;
+ else
+ addlen = len;
+ }
+
+ /* Do we need to change the skb before writing to the tail? */
+ if (unlikely((addlen > 0) || skb_cloned(skb) || skb_shared(skb))) {
+
+ /* Make sure data is writable */
+ if (unlikely(skb_cow_data(skb, addlen, &lastskb) < 0)) {
+ CFPKT_PRIV_DATA(pkt)->erronous = true;
+ return false;
+ }
+
+ /* Is the skb non-linear after skb_cow_data()? If so, we are
+ going to add data to the last skb, so we need to adjust
+ lengths of the top skb. */
+ if (lastskb != skb) {
+ skb->len += len;
+ skb->data_len += len;
+ }
+ }
+
+ /* All set to put the last skb and optionally write data there. */
+ to = skb_put(lastskb, len);
+ if (likely(data))
+ memcpy(to, data, len);
+ pkt_dump_debug(pkt);
+ return true;
+}
+inline bool cfpkt_addbdy(cfpkt_t *pkt, uint8 data)
+{
+ return cfpkt_add_body(pkt, &data, 1);
+}
+
+bool cfpkt_add_head(cfpkt_t *pkt, const void *data2, uint16 len)
+{
+ struct sk_buff *skb = (struct sk_buff *) pkt;
+ struct sk_buff *lastskb;
+ uint8 *to;
+ const uint8 *data = data2;
+ pkt_dump_debug(pkt);
+ if (unlikely(skb_headroom(skb) < len)) {
+ CFPKT_PRIV_DATA(pkt)->erronous = true;
+ return false;
+ }
+
+ /* Make sure data is writable */
+ if (unlikely(skb_cow_data(skb, 0, &lastskb) < 0)) {
+ CFPKT_PRIV_DATA(pkt)->erronous = true;
+ return false;
+ }
+
+ to = skb_push(skb, len);
+ memcpy(to, data, len);
+ pkt_dump_debug(pkt);
+ return true;
+}
+
+inline bool cfpkt_add_trail(cfpkt_t *pkt, const void *data, uint16 len)
+{
+ return cfpkt_add_body(pkt, data, len);
+}
+
+inline uint16 cfpkt_getlen(cfpkt_t *pkt)
+{
+ struct sk_buff *skb = (struct sk_buff *) pkt;
+ return skb->len;
+}
+
+inline uint16 cfpkt_iterate(cfpkt_t *pkt, iterfunc_t func, uint16 data)
+{
+
+ /* Don't care about the performance hit of linearizing,
+ * Checksum should not be used on high-speed interfaces anyway.*/
+ if (unlikely(skb_linearize(&pkt->skb) != 0)) {
+ CFPKT_PRIV_DATA(pkt)->erronous = true;
+ return 0;
+ }
+ return func(data, pkt->skb.data, cfpkt_getlen(pkt));
+}
+
+int cfpkt_setlen(cfpkt_t *pkt, uint16 len)
+{
+ struct sk_buff *skb = (struct sk_buff *) pkt;
+
+ pkt_dump_debug(pkt);
+
+ if (likely(len <= skb->len)) {
+ if (unlikely(skb->data_len))
+ ___pskb_trim(skb, len);
+ else
+ skb_trim(skb, len);
+
+ pkt_dump_debug(pkt);
+ return cfpkt_getlen(pkt);
+ }
+
+ /* Need to expand skb */
+ if (unlikely(!cfpkt_pad_trail(pkt, len - skb->len)))
+ CFPKT_PRIV_DATA(pkt)->erronous = true;
+
+ pkt_dump_debug(pkt);
+ return cfpkt_getlen(pkt);
+}
+
+void
+cfpkt_extract(cfpkt_t *cfpkt, void *buf, unsigned int buflen,
+ unsigned int *actual_len)
+{
+ uint16 pklen;
+
+ pkt_dump_debug(cfpkt);
+ pklen = cfpkt_getlen(cfpkt);
+ if (likely(buflen < pklen))
+ pklen = buflen;
+ *actual_len = pklen;
+ cfpkt_extr_head(cfpkt, buf, pklen);
+ pkt_dump_debug(cfpkt);
+}
+
+cfpkt_t *cfpkt_create_uplink(const unsigned char *data, unsigned int len)
+{
+ cfpkt_t *pkt = cfpkt_create_pfx(len + PKT_POSTFIX, PKT_PREFIX);
+ CFPKT_PRIV_DATA(pkt)->fastpath = true;
+ if (unlikely(data != NULL))
+ cfpkt_add_body(pkt, data, len);
+ return pkt;
+}
+
+cfpkt_t *cfpkt_append(cfpkt_t *dstpkt, cfpkt_t *addpkt, uint16 expectlen)
+{
+ struct sk_buff *dst = (struct sk_buff *) dstpkt;
+ struct sk_buff *add = (struct sk_buff *) addpkt;
+ struct sk_buff *lastskb;
+ CFPKT_PRIV_DATA(addpkt)->fastpath = false;
+ CFPKT_PRIV_DATA(dstpkt)->fastpath = false;
+ pkt_dump_debug(dstpkt);
+ pkt_dump_debug(addpkt);
+
+ printk("cfpkt_append() called\n");
+
+ /* Could we (instead of calling skb_cow_data()) check for
+ cloned || shared and if true allocate a new skb
+ with an empty head and set skb_shinfo(skb)->frag_list = add ?
+ If this is safe, would it be more efficient?
+ */
+
+ /* Make sure destination skb is writable */
+ if (unlikely(skb_cow_data(dst, 0, &lastskb) < 0)) {
+ CFPKT_PRIV_DATA(dstpkt)->erronous = true;
+ return dstpkt;
+ }
+
+ /* First get the frag_list if any */
+ if (likely(skb_shinfo(dst)->frag_list == NULL))
+ skb_shinfo(dst)->frag_list = add;
+ else {
+ lastskb = skb_shinfo(dst)->frag_list;
+
+ /* Go to the very right in the fragment list */
+ while (lastskb->next)
+ lastskb = lastskb->next;
+
+ lastskb->next = add;
+ }
+
+ /* Update size */
+ dst->data_len += add->len;
+ dst->len += add->len;
+ dst->truesize += add->truesize;
+ pkt_dump_debug(dstpkt);
+ cfglu_atomic_dec(cfpkt_packet_count);
+ return dstpkt;
+}
+
+cfpkt_t *cfpkt_split(cfpkt_t *pkt, uint16 pos)
+{
+ struct sk_buff *skb = (struct sk_buff *) pkt;
+ struct sk_buff *skb2;
+ CFPKT_PRIV_DATA(pkt)->fastpath = false;
+ if (unlikely(pos > skb->len)) {
+ PKT_ERROR(pkt, "cfpkt_split: split beyond end of packet\n");
+ return NULL;
+ }
+ pkt_dump_debug(pkt);
+
+ /* Make sure skb is writable */
+ if (unlikely(skb_cow_data(skb, 0, &skb2) < 0)) {
+ CFPKT_PRIV_DATA(pkt)->erronous = true;
+ return NULL;
+ }
+
+ skb2 = (struct sk_buff *) cfpkt_create(skb->len - pos);
+
+ if (unlikely(skb2 == NULL))
+ return NULL;
+
+ CFPKT_PRIV_DATA(pkt)->fastpath = false;
+ CFPKT_PRIV_DATA(pkt)->erronous = false;
+ pkt_dump_debug((cfpkt_t *) skb2);
+
+ skb_split(skb, skb2, pos);
+
+ pkt_dump_debug((cfpkt_t *) skb2);
+ cfglu_atomic_inc(cfpkt_packet_count);
+ return (cfpkt_t *) skb2;
+}
+
+char *cfpkt_log_pkt(cfpkt_t *cfpkt, char *buf, int buflen)
+{
+ struct sk_buff *skb = (struct sk_buff *) cfpkt;
+ char *p = buf;
+ int i;
+
+ if (skb_linearize(skb) != 0)
+ return NULL;
+
+ sprintf(buf, " pkt:%p len:%d {%d,%d} data: [",
+ skb,
+ skb->tail - skb->data,
+ skb->data - skb->head, skb->tail - skb->head);
+ p = buf + strlen(buf);
+
+ for (i = 0; i < skb->tail - skb->data; i++) {
+ if (p > buf + buflen - 10) {
+ sprintf(p, "...");
+ p = buf + strlen(buf);
+ break;
+ }
+ sprintf(p, "%02x,", skb->data[i]);
+ p = buf + strlen(buf);
+ }
+ sprintf(p, "]\n");
+ return buf;
+}
+
+
+int cfpkt_raw_append(cfpkt_t *cfpkt, void **buf, unsigned int buflen)
+{
+ struct sk_buff *skb = (struct sk_buff *) cfpkt;
+ struct sk_buff *lastskb;
+
+ cfglu_assert(buf != NULL);
+
+ /* Make sure skb is writable */
+ if (unlikely(skb_cow_data(skb, 0, &lastskb) < 0)) {
+ CFPKT_PRIV_DATA(cfpkt)->erronous = true;
+ return 0;
+ }
+
+ if (unlikely(skb_linearize(skb) != 0)) {
+ CFPKT_PRIV_DATA(cfpkt)->erronous = true;
+ return 0;
+ }
+
+ if (unlikely(skb_tailroom(skb) < buflen)) {
+ CFPKT_PRIV_DATA(cfpkt)->erronous = true;
+ return 0;
+ }
+
+ *buf = skb_put(skb, buflen);
+ return 1;
+}
+
+int cfpkt_raw_extract(cfpkt_t *cfpkt, void **buf, unsigned int buflen)
+{
+ struct sk_buff *skb = (struct sk_buff *) cfpkt;
+
+ cfglu_assert(buf != NULL);
+
+ if (unlikely(buflen > skb->len)) {
+ CFPKT_PRIV_DATA(cfpkt)->erronous = true;
+ return 0;
+ }
+
+ if (unlikely(buflen > skb_headlen(skb))) {
+ if (unlikely(skb_linearize(skb) != 0)) {
+ CFPKT_PRIV_DATA(cfpkt)->erronous = true;
+ return 0;
+ }
+ }
+
+ *buf = skb->data;
+ skb_pull(skb, buflen);
+
+ return 1;
+}
+
+
+
+
+inline bool cfpkt_erroneous(cfpkt_t *pkt)
+{
+ return CFPKT_PRIV_DATA(pkt)->erronous;
+}
+
+cfpkt_t *cfpkt_create_pkt(caif_direction_t dir,
+ const unsigned char *data, unsigned int len)
+{
+ cfpkt_t *pkt;
+ if (dir == CAIF_DIR_OUT)
+ pkt = cfpkt_create_pfx(len + PKT_POSTFIX, PKT_PREFIX);
+
+ else
+ pkt = cfpkt_create_pfx(len, 0);
+
+ if (unlikely(data))
+ cfpkt_add_body(pkt, data, len);
+ CFPKT_PRIV_DATA(pkt)->fastpath = true;
+ CFPKT_PRIV_DATA(pkt)->erronous = false;
+ return pkt;
+}
+
+caif_packet_funcs_t cfpkt_get_packet_funcs()
+{
+ caif_packet_funcs_t f;
+ f.cfpkt_fromnative = cfpkt_fromnative;
+ f.cfpkt_tonative = cfpkt_tonative;
+ f.cfpkt_destroy = cfpkt_destroy;
+ f.cfpkt_extract = cfpkt_extract;
+ f.cfpkt_create_xmit_pkt = cfpkt_create_uplink;
+ f.cfpkt_create_recv_pkt = cfpkt_create_uplink;
+ f.cfpkt_dequeue = cfpkt_dequeue;
+ f.cfpkt_qpeek = cfpkt_qpeek;
+ f.cfpkt_queue = cfpkt_queue;
+ f.cfpktq_create = cfpkt_queuecreate;
+ f.cfpkt_raw_extract = cfpkt_raw_extract;
+ f.cfpkt_raw_append = cfpkt_raw_append;
+ f.cfpkt_getlen = cfpkt_getlen;
+ return f;
+}
+
+cfpktq_t *cfpkt_queuecreate()
+{
+ struct sk_buff_head *q = cfglu_alloc(sizeof(struct sk_buff_head));
+ skb_queue_head_init(q);
+ return (cfpktq_t *) q;
+}
+
+void cfpkt_queue(cfpktq_t *pktq, cfpkt_t *pkt, unsigned short prio)
+{
+ skb_queue_tail((struct sk_buff_head *) pktq, (struct sk_buff *) pkt);
+}
+
+cfpkt_t *cfpkt_qpeek(cfpktq_t *pktq)
+{
+ return (cfpkt_t *) skb_peek((struct sk_buff_head *) pktq);
+}
+
+cfpkt_t *cfpkt_dequeue(cfpktq_t *pktq)
+{
+ return (cfpkt_t *) skb_dequeue((struct sk_buff_head *) pktq);
+}
diff --git a/net/caif/generic/cfrfml.c b/net/caif/generic/cfrfml.c
new file mode 100644
index 0000000..25206ca
--- /dev/null
+++ b/net/caif/generic/cfrfml.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2009
+ *
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ *
+ */
+
+
+
+#include "caif_layer.h"
+#include "cfglue.h"
+#include "cfsrvl.h"
+#include "cfpkt.h"
+#define container_obj(layr) cfglu_container_of(layr, cfsrvl_t, layer)
+
+#define RFM_SEGMENTATION_BIT 0x01
+
+#define RFM_PAYLOAD 0x00
+#define RFM_CMD_BIT 0x80
+#define RFM_FLOW_OFF 0x81
+#define RFM_FLOW_ON 0x80
+#define RFM_SET_PIN 0x82
+#define RFM_CTRL_PKT_SIZE 1
+
+static int cfrfml_receive(layer_t *layr, cfpkt_t *pkt);
+static int cfrfml_transmit(layer_t *layr, transmt_info *dummy, cfpkt_t *pkt);
+
+layer_t *cfrfml_create(uint8 channel_id, uint8 phyid)
+{
+ cfsrvl_t *rfm = cfglu_alloc(sizeof(cfsrvl_t));
+ cfglu_assert(offsetof(cfsrvl_t, layer) == 0);
+
+ memset(rfm, 0, sizeof(cfsrvl_t));
+ cfsrvl_init(rfm, channel_id, phyid);
+ rfm->layer.receive = cfrfml_receive;
+ rfm->layer.transmit = cfrfml_transmit;
+ sprintf(rfm->layer.name, "rfm%d", channel_id);
+ return &rfm->layer;
+}
+
+void cffrml_destroy(layer_t *layer)
+{
+ cfglu_free(layer);
+}
+
+
+static int cfrfml_receive(layer_t *layr, cfpkt_t *pkt)
+{
+ uint8 tmp;
+ bool segmented;
+ int ret;
+ CFLOG_ENTER(("\n"));
+ cfglu_assert(layr->up != NULL);
+ cfglu_assert(layr->receive != NULL);
+
+
+ /* RFM is taking care of Segmentation, stripping of Segmentation bit */
+ if (!cfpkt_extr_head(pkt, &tmp, 1)) {
+ CFLOG_ERROR(("cfrfml: Packet is erroneous!\n"));
+ cfpkt_destroy(pkt);
+ return CFGLU_EPROTO;
+ }
+ segmented = tmp & RFM_SEGMENTATION_BIT;
+ cfglu_assert(!segmented);
+
+ ret = layr->up->receive(layr->up, pkt);
+ CFLOG_EXIT(("\n"));
+ return ret;
+}
+
+static int cfrfml_transmit(layer_t *layr, transmt_info *dummy, cfpkt_t *pkt)
+{
+ uint8 tmp = 0;
+ transmt_info info;
+ int ret;
+ cfsrvl_t *service = container_obj(layr);
+
+ cfglu_assert(layr->dn != NULL);
+ cfglu_assert(layr->dn->transmit != NULL);
+
+ if (!cfsrvl_ready(service, &ret))
+ return ret;
+
+ if (!cfpkt_getlen(pkt) > CAIF_MAX_PAYLOAD_SIZE) {
+ CFLOG_ERROR(("packet too large size=%d\n", cfpkt_getlen(pkt)));
+ return CFGLU_EOVERFLOW;
+ }
+
+
+ if (!cfpkt_getlen(pkt) > CAIF_MAX_PAYLOAD_SIZE) {
+ CFLOG_ERROR(("packert too large size=%d\n",
+ cfpkt_getlen(pkt)));
+ return CFGLU_EOVERFLOW;
+ }
+ if (!cfpkt_add_head(pkt, &tmp, 1)) {
+ CFLOG_ERROR(("cffrml: Packet is erroneous!\n"));
+ return CFGLU_EPKT;
+ }
+
+ /* Add info for MUX-layer to route the packet out */
+ info.channel_id = service->layer.id;
+ info.phid = service->phid;
+ /* For optimizing alignment we add up the size of CAIF header before
+ payload */
+ info.hdr_len = 1;
+ ret = layr->dn->transmit(layr->dn, &info, pkt);
+ if (ret < 0)
+ cfpkt_extr_head(pkt, &tmp, 1);
+ return ret;
+}
diff --git a/net/caif/generic/cfserl.c b/net/caif/generic/cfserl.c
new file mode 100644
index 0000000..f0f30ba
--- /dev/null
+++ b/net/caif/generic/cfserl.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2009
+ *
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ *
+ */
+
+
+
+
+#include "caif_layer.h"
+#include "cfpkt.h"
+#include "cfglue.h"
+
+#include "fcs.h"
+#define container_obj(layr) ((struct cfserl *) layr)
+
+
+#define CFSERL_STX 0x02
+#define CAIF_MINIUM_PACKET_SIZE 4
+struct cfserl {
+ layer_t layer;
+ cfpkt_t *incomplete_frm;
+ cfglu_lock_t sync;
+ bool usestx;
+};
+#define STXLEN(layr) (layr->usestx ? 1 : 0)
+static int cfserl_receive(layer_t *layr, cfpkt_t *pkt);
+static int cfserl_transmit(layer_t *layr, transmt_info *info,
+ cfpkt_t *pkt);
+static void cfserl_ctrlcmd(layer_t *layr, caif_ctrlcmd_t ctrl, int phyid);
+
+struct cfserl *cfserl_create(int type, int instance, bool use_stx)
+{
+ struct cfserl *this = cfglu_alloc(sizeof(struct cfserl));
+ cfglu_assert(offsetof(struct cfserl, layer) == 0);
+
+ memset(this, 0, sizeof(struct cfserl));
+ this->layer.receive = cfserl_receive;
+ this->layer.transmit = cfserl_transmit;
+ this->layer.ctrlcmd = cfserl_ctrlcmd;
+ this->layer.type = type;
+ this->usestx = use_stx;
+ cfglu_init_lock(this->sync);
+ sprintf(this->layer.name, "ser1");
+ return this;
+}
+
+void cfserl_set_uplayer(struct cfserl *this, layer_t *up)
+{
+ this->layer.up = up;
+}
+
+void cfserl_set_dnlayer(struct cfserl *this, layer_t *dn)
+{
+ this->layer.dn = dn;
+
+}
+
+static int cfserl_receive(layer_t *l, cfpkt_t *newpkt)
+{
+ struct cfserl *layr = container_obj(l);
+ uint16 pkt_len;
+ cfpkt_t *pkt = NULL;
+ cfpkt_t *tail_pkt = NULL;
+ uint8 tmp8;
+ uint16 tmp;
+ uint8 stx = CFSERL_STX;
+ int ret;
+ uint16 expectlen = 0;
+
+
+
+#ifdef CAIF_DEBUG_ON
+ static char buf[10000];
+ uint16 newpktlen = cfpkt_getlen(newpkt);
+ CFLOG_ENTER(("\n"));
+ if (caif_dbg_level > CFLOG_LEVEL_TRACE3) {
+ cfpkt_log_pkt(newpkt, buf, sizeof(buf));
+ CFLOG_TRACE3(("serl: RECEIVE PACKET %s\n", buf));
+ }
+#endif
+ cfglu_lock(layr->sync);
+
+
+ if (layr->incomplete_frm != NULL) {
+
+ layr->incomplete_frm =
+ cfpkt_append(layr->incomplete_frm, newpkt, expectlen);
+ CFLOG_TRACE2(("serl: Appending %d bytes, new packet holds %d "
+ "bytes with expected frame-length=%d\n",
+ newpktlen,
+ cfpkt_getlen(layr->incomplete_frm),
+ expectlen));
+ pkt = layr->incomplete_frm;
+ } else {
+ pkt = newpkt;
+ }
+
+ layr->incomplete_frm = NULL;
+
+
+ do {
+#ifdef CAIF_DEBUG_ON
+ if (caif_dbg_level > CFLOG_LEVEL_TRACE3) {
+ cfpkt_log_pkt(pkt, buf, sizeof(buf));
+ CFLOG_TRACE3(("serl: PACKET before STX search: %s\n",
+ buf));
+ }
+#endif
+
+ /*
+ * Seach for STX at start of pkt if STX is used
+ */
+ if (layr->usestx) {
+ CFLOG_TRACE2(("serl: Start looking for STX at "
+ "start of frame\n"));
+ (void) cfpkt_extr_head(pkt, &tmp8, 1);
+ if (tmp8 != CFSERL_STX) {
+ CFLOG_TRACE(("serl: STX Error! STX not at "
+ "start of packe\n"));
+ while (cfpkt_more(pkt)
+ && tmp8 != CFSERL_STX) {
+ (void) cfpkt_extr_head(pkt, &tmp8, 1);
+ }
+ if (!cfpkt_more(pkt)) {
+ CFLOG_TRACE(("serl: Destroying packet "
+ "when no STX found\n"));
+ cfpkt_destroy(pkt);
+ layr->incomplete_frm = NULL;
+ cfglu_unlock(layr->sync);
+ CFLOG_EXIT(("\n"));
+ return CFGLU_EPROTO;
+ }
+ }
+ }
+
+ pkt_len = cfpkt_getlen(pkt);
+
+ /*
+ * pkt_len is the accumulated length of the packet data
+ * we have received so far
+ * Exit if frame don't hold length.
+ */
+
+ if (pkt_len < 2) {
+ if (layr->usestx)
+ cfpkt_add_head(pkt, &stx, 1);
+ layr->incomplete_frm = pkt;
+ cfglu_unlock(layr->sync);
+#ifdef CAIF_DEBUG_ON
+ if (caif_dbg_level > CFLOG_LEVEL_TRACE3) {
+ cfpkt_log_pkt(pkt, buf, sizeof(buf));
+ CFLOG_TRACE3(("serl: PACKET before exit "
+ "(too short): %s\n", buf));
+ }
+#endif
+
+ CFLOG_EXIT(("\n"));
+ return 0;
+ }
+
+ /*
+ * Find length of frame
+ * expectlen is the length we need for a full frame.
+ */
+ (void) cfpkt_peek_head(pkt, &tmp, 2);
+ expectlen = cfglu_le16_to_cpu(tmp) + 2;
+ CFLOG_TRACE2(("serl: Processing a packet of %d bytes with "
+ "expected length=%d\n", pkt_len, expectlen));
+
+#ifdef CAIF_DEBUG_ON
+ if (caif_dbg_level > CFLOG_LEVEL_TRACE3) {
+ cfpkt_log_pkt(pkt, buf, sizeof(buf));
+ CFLOG_TRACE3(("serl: PACKET after STX Seach: %s\n",
+ buf));
+ }
+#endif
+
+
+
+ /*
+ * Frame error handling
+ */
+ if (expectlen < CAIF_MINIUM_PACKET_SIZE
+ || expectlen > CAIF_MAX_FRAMESIZE) {
+ if (!layr->usestx) {
+ CFLOG_ERROR(("cfserl: packet has bad expectlen "
+ "%d\n", expectlen));
+ CFLOG_ERROR(("serl: Packet is erroneous throw "
+ "it away (expectlen=%d, len=%d)",
+ expectlen, pkt_len));
+ if (pkt != NULL)
+ cfpkt_destroy(pkt);
+ layr->incomplete_frm = NULL;
+ expectlen = 0;
+ cfglu_unlock(layr->sync);
+ CFLOG_EXIT(("\n"));
+ return CFGLU_EPROTO;
+ }
+ continue;
+ }
+
+
+ if (pkt_len < expectlen) {
+ /* Too little received data */
+ CFLOG_TRACE2(("serl: Holding incomplete packet with "
+ "current length=%d and expected "
+ "length=%d", pkt_len, expectlen));
+ if (layr->usestx)
+ cfpkt_add_head(pkt, &stx, 1);
+
+#ifdef CAIF_DEBUG_ON
+ if (caif_dbg_level > CFLOG_LEVEL_TRACE3) {
+ cfpkt_log_pkt(pkt, buf, sizeof(buf));
+ CFLOG_TRACE3(("serl: incomplete_frame: %s\n",
+ buf));
+ }
+#endif
+ layr->incomplete_frm = pkt;
+ cfglu_unlock(layr->sync);
+ return 0;
+ }
+
+ /* Enough data for at least one frame */
+ /* Split the frame, if too long */
+ if (pkt_len > expectlen) {
+ CFLOG_TRACE2(("serl: Splitting too long packet of "
+ "length = %d and frame size =%d\n",
+ pkt_len, expectlen));
+ tail_pkt = cfpkt_split(pkt, expectlen);
+ } else {
+ tail_pkt = NULL;
+ }
+
+#ifdef CAIF_DEBUG_ON
+ if (caif_dbg_level > CFLOG_LEVEL_TRACE3) {
+ cfpkt_log_pkt(pkt, buf, sizeof(buf));
+ CFLOG_TRACE3(("serl: PACKET sent up: %s\n", buf));
+ }
+#endif
+
+ /* Send the first part of packet upwards */
+ ret = layr->layer.up->receive(layr->layer.up, pkt);
+
+ if (ret == CFGLU_EFCS) {
+ CFLOG_ERROR(("cfserl: upper layer return error: %d\n",
+ ret));
+
+ if (layr->usestx) {
+ CFLOG_WARN(("cfserl: Layer above return fcs "
+ "error, Search for next STX\n"));
+ if (tail_pkt != NULL)
+ pkt = cfpkt_append(pkt, tail_pkt, 0);
+
+ /* Start seach for next STX if frame failed */
+ continue;
+ } else {
+ cfpkt_destroy(pkt);
+ pkt = NULL;
+ }
+ }
+
+ pkt = tail_pkt;
+
+ } while (pkt != NULL);
+
+
+
+ cfglu_unlock(layr->sync);
+ return 0;
+
+}
+
+static int cfserl_transmit(layer_t *layer, transmt_info *info,
+ cfpkt_t *newpkt)
+{
+ struct cfserl *layr = container_obj(layer);
+ int ret;
+ uint8 tmp8 = CFSERL_STX;
+ CFLOG_ENTER(("\n"));
+ if (layr->usestx)
+ cfpkt_add_head(newpkt, &tmp8, 1);
+ ret = layer->dn->transmit(layer->dn, info, newpkt);
+ if (ret < 0)
+ cfpkt_extr_head(newpkt, &tmp8, 1);
+
+ CFLOG_EXIT(("\n"));
+ return ret;
+}
+
+static void cfserl_ctrlcmd(layer_t *layr, caif_ctrlcmd_t ctrl, int phyid)
+{
+ layr->up->ctrlcmd(layr->up, ctrl, phyid);
+}
diff --git a/net/caif/generic/cfshml.c b/net/caif/generic/cfshml.c
new file mode 100644
index 0000000..f00a960
--- /dev/null
+++ b/net/caif/generic/cfshml.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2009
+ *
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ *
+ */
+
+
+
+
+
+#include "caif_layer.h"
+#include "cfpkt.h"
+#include "cfglue.h"
+#include "fcs.h"
+
+
+static int cfshml_receive(layer_t *layr, cfpkt_t *pkt);
+static int cfshml_transmit(layer_t *layr, transmt_info *info, cfpkt_t *pkt);
+static void cfshml_ctrlcmd(layer_t *layr, caif_ctrlcmd_t ctrl, int phyid);
+
+layer_t *cfshml_create(int type, int instance)
+{
+ layer_t *this = cfglu_alloc(sizeof(layer_t));
+
+ memset(this, 0, sizeof(layer_t));
+ this->receive = cfshml_receive;
+ this->transmit = cfshml_transmit;
+ this->ctrlcmd = cfshml_ctrlcmd;
+ this->type = type;
+ sprintf(this->name, "shm1");
+ return this;
+}
+
+void cfshml_set_uplayer(layer_t *this, layer_t *up)
+{
+ this->up = up;
+}
+
+void cfshml_set_dnlayer(layer_t *this, layer_t *dn)
+{
+ this->dn = dn;
+ this->dn->type = this->type;
+}
+
+static int cfshml_receive(layer_t *layr, cfpkt_t *pkt)
+{
+ int ret;
+ /* Send the first part of packet upwards */
+ ret = layr->up->receive(layr->up, pkt);
+ /* FCS Error don't delete the packet */
+ if (ret == CFGLU_EFCS)
+ cfpkt_destroy(pkt);
+ return ret;
+}
+
+static int cfshml_transmit(layer_t *layr, transmt_info *info, cfpkt_t *pkt)
+{
+ return layr->dn->transmit(layr->dn, info, pkt);
+}
+
+static void cfshml_ctrlcmd(layer_t *layr, caif_ctrlcmd_t ctrl, int phyid)
+{
+ layr->up->ctrlcmd(layr->up, ctrl, phyid);
+}
diff --git a/net/caif/generic/cfspil.c b/net/caif/generic/cfspil.c
new file mode 100644
index 0000000..f782146
--- /dev/null
+++ b/net/caif/generic/cfspil.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2009
+ *
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ *
+ */
+
+
+
+/** \page CFSPIL - CAIF SPI Layer:
+
+ * \note: A special protocol is defined for SPI PHYSICAL layer:
+ * -# CAIF packet is sent by \ref transmit_cb_t "transmit function"
+ * -# SPI PHY layer will send \ref ctrlcmd_cb_t "flowcontrol"
+ * \ref CAIF_CTRLCMD_FLOW_OFF_IND "off" upwards in CAIF stack.
+ * -# CAIF will start appending CAIF Packets upto maximum size.
+ * -# SPI PHY layer will send \ref ctrlcmd_cb_t "flowcontrol"
+ * \ref CAIF_CTRLCMD_FLOW_ON_IND "on" upwards in CAIF stack.
+ * -# Appended CAIF packets are sent by \ref transmit_cb_t
+ * "transmit" function.
+
+ */
+
+#include "cfspil.h"
+#include "caif_layer.h"
+#include "cfpkt.h"
+#include "cfglue.h"
+#include "fcs.h"
+#define CAIF_MAX_SPI_FRAME 4096
+struct _cfspil_t {
+ layer_t layer; /* The Layer Structure must always be present,
+ first in struct */
+ uint16 headalignment; /* Current head alignment: 0 for byte
+ alignment, 1 for 16bits, 2 for 32bits */
+ uint16 tailalignment; /* Current tail alignment: 0 for byte
+ alignment, 1 for 16bits, 2 for 32bits */
+ cfpkt_t *xmitpkt; /* Pending packet to be sent */
+ cfglu_lock_t sync;
+ cfpktq_t *queue;
+
+};
+
+/* Prototype declarations */
+static int cfspil_receive(cfspil_t *layr, cfpkt_t *pkt);
+static int cfspil_transmit(cfspil_t *layr, transmt_info *info, cfpkt_t *pkt);
+
+layer_t *cfspil_create(int type, int instance)
+{
+ cfspil_t *this = cfglu_alloc(sizeof(cfspil_t));
+ cfglu_assert(offsetof(cfspil_t, layer) == 0);
+ memset(this, 0, sizeof(cfspil_t));
+ cfglu_init_lock(this->sync);
+ this->layer.receive = (receive_cb_t) cfspil_receive;
+ this->layer.transmit = (transmit_cb_t) cfspil_transmit;
+ this->headalignment = 1;
+ this->tailalignment = 1;
+ this->xmitpkt = NULL; /* Pending packet to be sent */
+ this->layer.type = type;
+ sprintf(this->layer.name, "spi1");
+ this->queue = cfpkt_queuecreate();
+ return &this->layer;
+}
+
+static int cfspil_receive(cfspil_t *layr, cfpkt_t *pkt)
+{
+ uint8 startpad;
+ uint8 pad[16];
+ uint16 len, tmplen;
+ uint16 pktlen;
+ uint16 endpad;
+ int ret;
+ cfpkt_t *nextpkt = NULL;
+
+ while (pkt && cfpkt_more(pkt)) {
+
+ /* Read and handle start offset */
+ (void) cfpkt_extr_head(pkt, &startpad, 1);
+ if (startpad > 3) {
+ CFLOG_ERROR(("cfspil: Received start padding = %d,"
+ " expected 3 as max\n", startpad));
+ goto error;
+ }
+
+ cfglu_assert(startpad < 16);
+
+ if (startpad)
+ (void) cfpkt_extr_head(pkt, &pad, startpad);
+
+ pktlen = cfpkt_getlen(pkt);
+
+ /* Read packet length */
+ (void) cfpkt_peek_head(pkt, &tmplen, 2);
+ len = cfglu_le16_to_cpu(tmplen) + 2;
+ if (cfpkt_erroneous(pkt) || len < 6
+ || len > CAIF_MAX_SPI_FRAME || pktlen < len) {
+ CFLOG_ERROR(("cfspil: Packet is erroneous throw it "
+ " away (len=%d)\n", len));
+ cfpkt_destroy(pkt);
+ return CFGLU_EPROTO;
+ }
+
+
+ /* Compute tail offset i.e. number of bytes to add to ge
+ alignment */
+ endpad = (len + startpad + 1) & layr->tailalignment;
+
+ /* CFLOG_TRACE(("cfspil: recv pkt:0x%x len:%d startpad:%d
+ endpad:%d\n", pkt, len, startpad, endpad)); */
+
+ nextpkt = NULL;
+ if (pktlen - len > 5)
+ nextpkt = cfpkt_split(pkt, len);
+
+ if (cfpkt_erroneous(pkt)) {
+ CFLOG_ERROR(("cfspil: Packet is erroneous!\n"));
+ goto error;
+ }
+
+ if (endpad && nextpkt)
+ (void) cfpkt_extr_head(nextpkt, &pad, endpad);
+
+
+ ret = layr->layer.up->receive(layr->layer.up, pkt);
+ /* FCS Error don't delete the packet */
+ if (ret == CFGLU_EFCS)
+ cfpkt_destroy(pkt);
+
+ pkt = nextpkt;
+ nextpkt = NULL;
+ }
+ return 0;
+error:
+ cfpkt_destroy(pkt);
+ if (nextpkt)
+ cfpkt_destroy(nextpkt);
+ return CFGLU_EPROTO;
+}
+
+static cfpkt_t *cfspil_built_xmit_pkt(cfspil_t *layr)
+{
+ uint16 totallen;
+ cfpkt_t *pkt, *xmitpkt;
+
+
+ cfglu_lock(layr->sync);
+ xmitpkt = cfpkt_dequeue(layr->queue);
+
+ /* CFLOG_TRACE(("cfspil_xmitlen - 1: lyr:0x%x,
+ xmitpkt:0x%x\n", layr, xmitpkt)); */
+
+ if (xmitpkt == NULL) {
+ cfglu_unlock(layr->sync);
+ return NULL;
+ }
+
+ totallen = cfpkt_getlen(xmitpkt);
+ /* CFLOG_TRACE(("cfspil_xmitlen -2 : xmitpkt:0x%x
+ len:%d\n", xmitpkt, totallen)); */
+
+ while (xmitpkt != NULL && !cfpkt_erroneous(xmitpkt)) {
+ int len;
+ pkt = cfpkt_qpeek(layr->queue);
+
+ len = pkt == NULL ? 0 : cfpkt_getlen(pkt);
+ if (pkt != NULL && totallen + len < CAIF_MAX_SPI_FRAME) {
+ pkt = cfpkt_dequeue(layr->queue);
+ /* CFLOG_TRACE(("cfspil_xmitlen - 3: pkt:0x%x
+ len:%d\n", pkt, len)); */
+ totallen += len;
+ xmitpkt = cfpkt_append(xmitpkt, pkt, 0);
+ /* CFLOG_TRACE(("cfspil_xmitlen - 4: pkt:0x%x
+ len:%d\n", xmitpkt, totallen)); */
+ } else {
+ cfglu_unlock(layr->sync);
+ /* CFLOG_TRACE(("cfspil_xmitlen - 5: pkt:0x%x
+ len:%d\n", xmitpkt, totallen)); */
+
+ return xmitpkt;
+ }
+ }
+
+ cfglu_unlock(layr->sync);
+
+ /* error handling */
+ if (xmitpkt != NULL)
+ cfpkt_destroy(xmitpkt);
+
+ return NULL;
+}
+
+int cfspil_xmitlen(cfspil_t *layr)
+{
+ if (layr->xmitpkt == NULL)
+ layr->xmitpkt = cfspil_built_xmit_pkt(layr);
+
+ /* CFLOG_TRACE(("cfspil_xmitlen: lyr:0x%x\n", layr)); */
+
+ if (layr->xmitpkt != NULL)
+ return cfpkt_getlen(layr->xmitpkt);
+ return 0;
+}
+
+cfpkt_t *cfspil_getxmitpkt(cfspil_t *layr)
+{
+ cfpkt_t *ret = layr->xmitpkt;
+ layr->xmitpkt = NULL;
+ return ret;
+}
+
+static int cfspil_transmit(cfspil_t *layr, transmt_info *info, cfpkt_t *pkt)
+{
+ uint32 pad = -1;
+ uint8 startpad;
+ /* uint16 endpad; */
+ uint16 len;
+ /* CFLOG_TRACE(("cfspil_transmit: lyr:0x%x, pkt:0x%x
+ pktlen:%d\n", layr, pkt, cfpkt_getlen(pkt))); */
+ startpad = (info->hdr_len + 1) & layr->headalignment;
+ if (startpad)
+ cfpkt_add_head(pkt, &pad, startpad);
+ cfpkt_add_head(pkt, &startpad, 1);
+ len = cfpkt_getlen(pkt);
+
+ /* This should be a compile time option along with all other
+ * gory SPI details. */
+
+ /* endpad = len & layr->tailalignment; if (endpad)
+ * cfpkt_pad_trail(pkt, endpad); */
+
+ if (cfpkt_erroneous(pkt)) {
+ CFLOG_ERROR(("cfspil: Packet is erroneous!\n"));
+ return CFGLU_EPROTO;
+ }
+
+ cfglu_lock(layr->sync);
+ cfpkt_queue(layr->queue, pkt, info->prio);
+ cfglu_unlock(layr->sync);
+
+ /* Inidicate a transmit request, but SPI-PHY pull the packet */
+ layr->layer.dn->transmit(layr->layer.dn, NULL, NULL);
+
+ return 0;
+}
diff --git a/net/caif/generic/cfsrvl.c b/net/caif/generic/cfsrvl.c
new file mode 100644
index 0000000..3095401
--- /dev/null
+++ b/net/caif/generic/cfsrvl.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2009
+ *
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ *
+ */
+
+#include "caif_layer.h"
+#include "cfglue.h"
+#include "cfsrvl.h"
+#include "cfpkt.h"
+
+#define SRVL_CTRL_PKT_SIZE 1
+#define SRVL_FLOW_OFF 0x81
+#define SRVL_FLOW_ON 0x80
+#define SRVL_SET_PIN 0x82
+#define SRVL_CTRL_PKT_SIZE 1
+
+#define container_obj(layr) cfglu_container_of(layr, cfsrvl_t, layer)
+
+static void cfservl_ctrlcmd(layer_t *layr, caif_ctrlcmd_t ctrl, int phyid)
+{
+ cfsrvl_t *service = container_obj(layr);
+ cfglu_assert(layr->up != NULL);
+ cfglu_assert(layr->up->ctrlcmd != NULL);
+ CFLOG_ENTER(("\n"));
+ switch (ctrl) {
+ case CAIF_CTRLCMD_INIT_RSP:
+ CFLOG_TRACE(("cfsrvl: ctrlcmd SEND_FLOW_INIT\n"));
+ service->open = true;
+ layr->up->ctrlcmd(layr->up, ctrl, phyid);
+ break;
+ case CAIF_CTRLCMD_DEINIT_RSP:
+ case CAIF_CTRLCMD_INIT_FAIL_RSP:
+ CFLOG_TRACE(("cfsrvl: ctrlcmd DEINIT_RSP / INIT_FAIL_RSP\n"));
+ service->open = false;
+ layr->up->ctrlcmd(layr->up, ctrl, phyid);
+ break;
+ case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND:
+ if (phyid != service->phid)
+ break;
+ if (service->modem_flow_on)
+ layr->up->ctrlcmd(layr->up,
+ CAIF_CTRLCMD_FLOW_OFF_IND, phyid);
+ service->phy_flow_on = false;
+ break;
+ case _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND:
+ if (phyid != service->phid)
+ return;
+ if (service->modem_flow_on) {
+ layr->up->ctrlcmd(layr->up,
+ CAIF_CTRLCMD_FLOW_ON_IND,
+ phyid);
+ }
+ service->phy_flow_on = true;
+ break;
+ case CAIF_CTRLCMD_FLOW_OFF_IND:
+ if (service->phy_flow_on) {
+ layr->up->ctrlcmd(layr->up,
+ CAIF_CTRLCMD_FLOW_OFF_IND, phyid);
+ }
+ service->modem_flow_on = false;
+ break;
+ case CAIF_CTRLCMD_FLOW_ON_IND:
+ if (service->phy_flow_on) {
+ layr->up->ctrlcmd(layr->up,
+ CAIF_CTRLCMD_FLOW_ON_IND, phyid);
+ }
+ service->modem_flow_on = true;
+ break;
+ case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND:
+ layr->up->ctrlcmd(layr->up, ctrl, phyid);
+ break;
+ default:
+ CFLOG_WARN(("cfsrvl: Unexpected ctrl in cfsrvl (%d)\n", ctrl));
+
+ /* We have both modem and phy flow on, send flow on */
+ layr->up->ctrlcmd(layr->up, ctrl, phyid);
+ service->phy_flow_on = true;
+ break;
+ }
+ CFLOG_EXIT(("\n"));
+}
+
+static int cfservl_modemcmd(layer_t *layr, caif_modemcmd_t ctrl)
+{
+ cfsrvl_t *service = container_obj(layr);
+ cfglu_assert(layr != NULL);
+ cfglu_assert(layr->dn != NULL);
+ cfglu_assert(layr->dn->transmit != NULL);
+ switch (ctrl) {
+ case CAIF_MODEMCMD_FLOW_ON_REQ:
+ {
+ cfpkt_t *pkt;
+ transmt_info info;
+ uint8 flow_on = SRVL_FLOW_ON;
+ memset(&info, 0, sizeof(info));
+ info.channel_id = service->layer.id;
+ info.phid = service->phid;
+ info.hdr_len = 1;
+ CFLOG_TRACE(("cfsrvl: ctrlcmd SEND_FLOW_ON\n"));
+ pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE);
+ if (!cfpkt_add_head(pkt, &flow_on, 1)) {
+ CFLOG_ERROR(("cfsrvl: Packet is erroneous!\n"));
+ cfpkt_destroy(pkt);
+ return CFGLU_EPROTO;
+ }
+ return layr->dn->transmit(layr->dn, &info, pkt);
+ }
+ case CAIF_MODEMCMD_FLOW_OFF_REQ:
+ {
+ cfpkt_t *pkt;
+ transmt_info info;
+ uint8 flow_off = SRVL_FLOW_OFF;
+ memset(&info, 0, sizeof(info));
+ info.channel_id = service->layer.id;
+ info.phid = service->phid;
+ info.hdr_len = 1;
+ CFLOG_TRACE(("cfsrvl: ctrlcmd SEND_FLOW_OFF\n"));
+ pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE);
+ if (!cfpkt_add_head(pkt, &flow_off, 1)) {
+ CFLOG_ERROR(("cfsrvl: Packet is erroneous!\n"));
+ cfpkt_destroy(pkt);
+ return CFGLU_EPROTO;
+ }
+ return layr->dn->transmit(layr->dn, &info, pkt);
+ }
+ default:
+ break;
+ }
+ return CFGLU_EINVAL;
+}
+
+void cfservl_destroy(layer_t *layer)
+{
+ cfglu_free(layer);
+}
+
+void cfsrvl_init(cfsrvl_t *service, uint8 channel_id, uint8 phyid)
+{
+ cfsrvl_t *srvl = cfglu_alloc(sizeof(cfsrvl_t));
+ cfglu_assert(offsetof(cfsrvl_t, layer) == 0);
+ memset(srvl, 0, sizeof(cfsrvl_t));
+ service->open = false;
+ service->modem_flow_on = true;
+ service->phy_flow_on = true;
+ service->layer.id = channel_id;
+ service->phid = phyid;
+ service->layer.ctrlcmd = cfservl_ctrlcmd;
+ service->layer.modemcmd = cfservl_modemcmd;
+}
+
+bool cfsrvl_ready(cfsrvl_t *service, int *err)
+{
+ if (service->open && service->modem_flow_on && service->phy_flow_on)
+ return true;
+ if (!service->open) {
+ *err = CFGLU_ENOTCONN;
+ return false;
+ }
+ cfglu_assert(!(service->modem_flow_on && service->phy_flow_on));
+ *err = CFGLU_ERETRY;
+ return false;
+}
+uint8 cfsrvl_getphyid(layer_t *layer)
+{
+ cfsrvl_t *servl = container_obj(layer);
+ return servl->phid;
+}
+
+bool cfsrvl_phyid_match(layer_t *layer, int phyid)
+{
+ cfsrvl_t *servl = container_obj(layer);
+ return servl->phid == phyid;
+}
diff --git a/net/caif/generic/cfutill.c b/net/caif/generic/cfutill.c
new file mode 100644
index 0000000..1182da3
--- /dev/null
+++ b/net/caif/generic/cfutill.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2009
+ *
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ *
+ */
+
+
+
+#include "caif_layer.h"
+#include "cfglue.h"
+#include "cfsrvl.h"
+#include "cfpkt.h"
+
+#define container_obj(layr) ((cfsrvl_t *) layr)
+#define UTIL_PAYLOAD 0x00
+#define UTIL_CMD_BIT 0x80
+#define UTIL_REMOTE_SHUTDOWN 0x82
+#define UTIL_FLOW_OFF 0x81
+#define UTIL_FLOW_ON 0x80
+#define UTIL_CTRL_PKT_SIZE 1
+static int cfutill_receive(layer_t *layr, cfpkt_t *pkt);
+static int cfutill_transmit(layer_t *layr, transmt_info *dummy,
+ cfpkt_t *pkt);
+
+layer_t *cfutill_create(uint8 channel_id, uint8 phyid)
+{
+ cfsrvl_t *util = cfglu_alloc(sizeof(cfsrvl_t));
+ cfglu_assert(offsetof(cfsrvl_t, layer) == 0);
+
+ memset(util, 0, sizeof(cfsrvl_t));
+ cfsrvl_init(util, channel_id, phyid);
+ util->layer.receive = cfutill_receive;
+ util->layer.transmit = cfutill_transmit;
+ sprintf(util->layer.name, "util1");
+ return &util->layer;
+}
+
+
+
+static int cfutill_receive(layer_t *layr, cfpkt_t *pkt)
+{
+ uint8 cmd = -1;
+ cfsrvl_t *service = container_obj(layr);
+ cfglu_assert(layr != NULL);
+ cfglu_assert(layr->up != NULL);
+ cfglu_assert(layr->up->receive != NULL);
+ cfglu_assert(layr->up->ctrlcmd != NULL);
+ if (!cfpkt_extr_head(pkt, &cmd, 1)) {
+ CFLOG_ERROR(("cfutill: Packet is erroneous!\n"));
+ cfpkt_destroy(pkt);
+ return CFGLU_EPROTO;
+ }
+
+ switch (cmd) {
+ case UTIL_PAYLOAD:
+ return layr->up->receive(layr->up, pkt);
+
+ case UTIL_FLOW_OFF:
+ layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_OFF_IND, 0);
+ cfpkt_destroy(pkt);
+ return 0;
+ case UTIL_FLOW_ON:
+ layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_ON_IND, 0);
+ cfpkt_destroy(pkt);
+ return 0;
+ case UTIL_REMOTE_SHUTDOWN: /* Remote Shutdown Request */
+ CFLOG_ERROR(("cfutill: REMOTE SHUTDOWN REQUEST RECEIVED\n"));
+ layr->ctrlcmd(layr, CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND, 0);
+ service->open = false;
+ cfpkt_destroy(pkt);
+ return 0;
+ default:
+ cfpkt_destroy(pkt);
+ CFLOG_ERROR(("cfmuxl: Unknown datagram control %d (0x%x!\n",
+ cmd, cmd));
+ return CFGLU_EPROTO;
+ }
+}
+
+static int cfutill_transmit(layer_t *layr, transmt_info *dummy, cfpkt_t *pkt)
+{
+ uint8 zero = 0;
+ transmt_info info;
+ int ret;
+ cfsrvl_t *service = container_obj(layr);
+ cfglu_assert(layr != NULL);
+ cfglu_assert(layr->dn != NULL);
+ cfglu_assert(layr->dn->transmit != NULL);
+ if (!cfsrvl_ready(service, &ret))
+ return ret;
+
+
+ if (cfpkt_getlen(pkt) > CAIF_MAX_PAYLOAD_SIZE) {
+ CFLOG_ERROR(("packet too large size=%d\n", cfpkt_getlen(pkt)));
+ return CFGLU_EOVERFLOW;
+ }
+
+ cfpkt_add_head(pkt, &zero, 1);
+ memset(&info, 0, sizeof(info));
+ /* Add info for MUX-layer to route the packet out */
+ info.channel_id = service->layer.id;
+ info.phid = service->phid;
+ /* For optimizing alignment we add up the size of CAIF header before
+ payload */
+ info.hdr_len = 1;
+ ret = layr->dn->transmit(layr->dn, &info, pkt);
+ if (ret < 0) {
+ uint32 tmp32;
+ cfpkt_extr_head(pkt, &tmp32, 4);
+ }
+ return ret;
+}
diff --git a/net/caif/generic/cfveil.c b/net/caif/generic/cfveil.c
new file mode 100644
index 0000000..06a4d21
--- /dev/null
+++ b/net/caif/generic/cfveil.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2009
+ *
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ *
+ */
+
+
+
+#include "caif_layer.h"
+#include "cfglue.h"
+#include "cfsrvl.h"
+#include "cfpkt.h"
+
+
+#define VEI_PAYLOAD 0x00
+#define VEI_CMD_BIT 0x80
+#define VEI_FLOW_OFF 0x81
+#define VEI_FLOW_ON 0x80
+#define VEI_SET_PIN 0x82
+#define VEI_CTRL_PKT_SIZE 1
+#define container_obj(layr) cfglu_container_of(layr, cfsrvl_t, layer)
+
+static int cfvei_receive(layer_t *layr, cfpkt_t *pkt);
+static int cfvei_transmit(layer_t *layr, transmt_info *dummy, cfpkt_t *pkt);
+
+layer_t *cfvei_create(uint8 channel_id, uint8 phyid)
+{
+ cfsrvl_t *vei = cfglu_alloc(sizeof(cfsrvl_t));
+ cfglu_assert(offsetof(cfsrvl_t, layer) == 0);
+ memset(vei, 0, sizeof(cfsrvl_t));
+ cfsrvl_init(vei, channel_id, phyid);
+ vei->layer.receive = cfvei_receive;
+ vei->layer.transmit = cfvei_transmit;
+ sprintf(vei->layer.name, "vei%d", channel_id);
+ CFLOG_TRACE(("cfvei: Created %p\n", (void *) vei));
+ return &vei->layer;
+}
+
+
+
+static int cfvei_receive(layer_t *layr, cfpkt_t *pkt)
+{
+ uint8 cmd;
+ int ret;
+ CFLOG_ENTER(("\n"));
+ cfglu_assert(layr->up != NULL);
+ cfglu_assert(layr->receive != NULL);
+ cfglu_assert(layr->ctrlcmd != NULL);
+
+
+ if (!cfpkt_extr_head(pkt, &cmd, 1)) {
+ CFLOG_ERROR(("cfvei: Packet is erroneous!\n"));
+ cfpkt_destroy(pkt);
+ CFLOG_ENTER(("\n"));
+ return CFGLU_EPROTO;
+ }
+ switch (cmd) {
+ case VEI_PAYLOAD:
+ ret = layr->up->receive(layr->up, pkt);
+ CFLOG_EXIT(("\n"));
+ return ret;
+ case VEI_FLOW_OFF:
+ layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_OFF_IND, 0);
+ cfpkt_destroy(pkt);
+ CFLOG_EXIT(("\n"));
+ return CFGLU_EOK;
+ case VEI_FLOW_ON:
+ layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_ON_IND, 0);
+ cfpkt_destroy(pkt);
+ CFLOG_EXIT(("\n"));
+ return CFGLU_EOK;
+ case VEI_SET_PIN: /* SET RS232 PIN */
+ cfpkt_destroy(pkt);
+ CFLOG_EXIT(("\n"));
+ return CFGLU_EOK;
+ default: /* SET RS232 PIN */
+ CFLOG_ERROR(("cfvei: Unknown VEI Control packet %d (0x%x)!\n",
+ cmd, cmd));
+ cfpkt_destroy(pkt);
+ CFLOG_EXIT(("error"));
+ return CFGLU_EPROTO;
+ }
+}
+
+static int cfvei_transmit(layer_t *layr, transmt_info *dummy, cfpkt_t *pkt)
+{
+ uint8 tmp = 0;
+ transmt_info info;
+ int ret;
+ cfsrvl_t *service = container_obj(layr);
+ if (!cfsrvl_ready(service, &ret))
+ return ret;
+
+ cfglu_assert(layr->dn != NULL);
+ cfglu_assert(layr->dn->transmit != NULL);
+ if (!cfpkt_getlen(pkt) > CAIF_MAX_PAYLOAD_SIZE) {
+ CFLOG_ERROR(("packert too large size=%d\n",
+ cfpkt_getlen(pkt)));
+ return CFGLU_EOVERFLOW;
+ }
+
+ if (!cfpkt_add_head(pkt, &tmp, 1)) {
+ CFLOG_ERROR(("cfvei: Packet is erroneous!\n"));
+ return CFGLU_EPKT;
+ }
+
+ /* Add info for MUX-layer to route the packet out */
+ info.channel_id = service->layer.id;
+ info.phid = service->phid;
+ info.hdr_len = 1;
+ ret = layr->dn->transmit(layr->dn, &info, pkt);
+ if (ret < 0)
+ cfpkt_extr_head(pkt, &tmp, 1);
+ return ret;
+}
diff --git a/net/caif/generic/cfvidl.c b/net/caif/generic/cfvidl.c
new file mode 100644
index 0000000..bbf1a0b
--- /dev/null
+++ b/net/caif/generic/cfvidl.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2009
+ *
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ *
+ */
+
+
+
+
+
+
+
+#include "caif_layer.h"
+#include "cfglue.h"
+#include "cfsrvl.h"
+#include "cfpkt.h"
+#define container_obj(layr) ((cfsrvl_t *) layr)
+
+
+static int cfvidl_receive(layer_t *layr, cfpkt_t *pkt);
+static int cfvidl_transmit(layer_t *layr, transmt_info *dummy, cfpkt_t *pkt);
+
+layer_t *cfvidl_create(uint8 channel_id, uint8 phyid)
+{
+ cfsrvl_t *vid = cfglu_alloc(sizeof(cfsrvl_t));
+ cfglu_assert(offsetof(cfsrvl_t, layer) == 0);
+
+ memset(vid, 0, sizeof(cfsrvl_t));
+ cfsrvl_init(vid, channel_id, phyid);
+ vid->layer.receive = cfvidl_receive;
+ vid->layer.transmit = cfvidl_transmit;
+ sprintf(vid->layer.name, "vid1");
+ return &vid->layer;
+}
+
+static int cfvidl_receive(layer_t *layr, cfpkt_t *pkt)
+{
+ uint32 videoheader;
+ if (!cfpkt_extr_head(pkt, &videoheader, 4)) {
+ CFLOG_ERROR(("cfvidl: Packet is erroneous!\n"));
+ cfpkt_destroy(pkt);
+ return CFGLU_EPROTO;
+ }
+ return layr->up->receive(layr->up, pkt);
+}
+
+static int cfvidl_transmit(layer_t *layr, transmt_info *dummy, cfpkt_t *pkt)
+{
+ cfsrvl_t *service = container_obj(layr);
+ uint32 videoheader = 0;
+ int ret;
+ transmt_info info;
+ if (!cfsrvl_ready(service, &ret))
+ return ret;
+ cfpkt_add_head(pkt, &videoheader, 4);
+ memset(&info, 0, sizeof(info));
+ /* Add info for MUX-layer to route the packet out */
+ info.channel_id = service->layer.id;
+ info.phid = service->phid;
+
+ ret = layr->dn->transmit(layr->dn, &info, pkt);
+ if (ret < 0)
+ cfpkt_extr_head(pkt, &videoheader, 4);
+ return ret;
+}
diff --git a/net/caif/generic/fcs.c b/net/caif/generic/fcs.c
new file mode 100644
index 0000000..9cbf13b
--- /dev/null
+++ b/net/caif/generic/fcs.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2009
+ *
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ *
+ */
+
+
+
+
+/* NOTE: Move this to glue and use OS specific function if exists */
+
+#include "cfglue.h"
+
+static uint16 fcstab[256] = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+
+uint16 fcs16(uint16 fcs, uint8 *cp, uint16 len)
+{
+ while (len--)
+ fcs = (fcs >> 8) ^ fcstab[(fcs ^ *cp++) & 0xff];
+ return fcs;
+}
--
1.6.0.4
^ permalink raw reply related
* [PATCH 3/8] [RFC] CAIF Protocol Stack
From: sjur.brandeland @ 2009-09-23 17:31 UTC (permalink / raw)
To: netdev; +Cc: Kim.xx.Lilliestierna, sjur.brandeland
From: Kim Lilliestierna <Kim.xx.Lilliestierna@ericsson.com>
Signed-off-by: sjur.brandeland@stericsson.com
---
include/net/caif/caif_actions.h | 81 +++++++++
include/net/caif/caif_chr.h | 52 ++++++
include/net/caif/caif_config_util.h | 27 +++
include/net/caif/caif_kernel.h | 324 +++++++++++++++++++++++++++++++++++
include/net/caif/caif_log.h | 83 +++++++++
5 files changed, 567 insertions(+), 0 deletions(-)
create mode 100644 include/net/caif/caif_actions.h
create mode 100644 include/net/caif/caif_chr.h
create mode 100644 include/net/caif/caif_config_util.h
create mode 100644 include/net/caif/caif_kernel.h
create mode 100644 include/net/caif/caif_log.h
diff --git a/include/net/caif/caif_actions.h b/include/net/caif/caif_actions.h
new file mode 100644
index 0000000..fe474f7
--- /dev/null
+++ b/include/net/caif/caif_actions.h
@@ -0,0 +1,81 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Daniel Martensson / Daniel.Martensson@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+#ifndef CAIF_ACTION_H_
+#define CAIF_ACTION_H_
+#include "caif_config.h"
+#include "caif_ioctl.h"
+
+#define DEVICE_NAME_LEN 16
+
+/**
+ * Types of Physical HW Interfaces towards modem defined in CAIF Stack,
+ * used when CAIF enumerates Interfaces and for Channel Configuration.
+ *
+ * For Client convenience to special types are defined:
+ * - \ref CAIF_PHY_LOW_LAT is the preferred low latency physical link.
+ * Typically used for "control" purposes.
+ * - \ref CAIF_PHY_HIGH_BW is the preferred high bandwidth physical link.
+ * Typically used for "payload" purposes.
+ * - \ref CAIF_PHY_UNCPECIFIED CAIF Stack implementation will assign link
+ * for you.
+ *
+ */
+enum caif_phy_type {
+ CAIF_PHY_UNSPECIFIED = 0x00, /*!< Default Physical Interface */
+ CAIF_PHY_SERIAL = 0x10, /*!< Serial Physical Interface */
+ CAIF_PHY_SPI = 0x20, /*!< SPI Physical Interface */
+ CAIF_PHY_MSL = 0x30, /*!< MSL Physical Interface */
+ CAIF_PHY_SHM = 0x40, /*!< Shared Memory Interface */
+ CAIF_PHY_LOOP = 0x70, /*!< Loop-back Interface Simulating ACC side
+ responses */
+ CAIF_PHY_RAW_LOOP = 0x80, /*!< Raw loop-back interface */
+};
+#define CAIF_PHY_MASK 0xf0
+
+
+
+
+
+
+union caif_action {
+ struct caif_device_name delete_channel;
+ struct caif_channel_create_action create_channel;
+};
+
+
+/** Query the names of the Enumerated CAIF Physical Interfaces. */
+#define CAIF_ACT_LIST_PHYIFS 1 /*struct caif_device_list_action) */
+
+/** Get the physical interface information, given the name. */
+#define CAIF_ACT_GET_PHYIF_INFO 2 /*struct caif_phyif_info_action) */
+
+/** Enumerate a physical interface. */
+#define CAIF_ACT_ACTIVATE_PHYIF 3 /*struct caif_phy_activate) */
+
+/** Removes (De-Enumerates) a physical interface. */
+#define CAIF_ACT_DEACT_DEVICE 4 /*struct caif_device_name) */
+
+/** Get the device names of configured devices */
+#define CAIF_ACT_LIST_DEVICE_NAMES 5 /*struct caif_device_list_action) */
+
+/** Get configuration and status information for a specified CAIF device */
+#define CAIF_ACT_GET_DEVICE_INFO 6 /*struct caif_device_info_action) */
+
+/** Create and Configure a new CAIF device. Note that the device is not
+ * implicitly connected. */
+
+#define CAIF_ACT_CREATE_DEVICE 7 /*struct caif_channel_create_action*/
+
+/** Remove a CAIF device. Requires the device to be previously disconnected. */
+#define CAIF_ACT_DELETE_DEVICE 8 /*struct caif_device_name) */
+
+
+
+#endif
diff --git a/include/net/caif/caif_chr.h b/include/net/caif/caif_chr.h
new file mode 100644
index 0000000..f257d74
--- /dev/null
+++ b/include/net/caif/caif_chr.h
@@ -0,0 +1,52 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Daniel Martensson / Daniel.Martensson@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+
+
+
+
+#ifndef CAIF_CHR_H_
+#define CAIF_CHR_H_
+
+#include "caif_layer.h"
+#include "cfcnfg.h"
+#include "cfspil.h"
+#include "caif_config.h"
+#include "caif_actions.h"
+struct caif_service_config;
+
+typedef enum _cf_chr_dev_type_t {
+ CFDEVTYPE_CHR,
+ CFDEVTYPE_TTY,
+ CFDEVTYPE_NET
+} cf_chr_dev_type_t;
+
+extern int serial_use_stx;
+
+int caifdev_phy_reg(layer_t *phyif, cfcnfg_phy_mgmt_t *mgmt);
+int caifdev_phy_instanciate(cfcnfg_phy_config_t *phy_config);
+int caifdev_phy_register(layer_t *phyif, cfcnfg_phy_type_t phy_type,
+ cfcnfg_phy_preference_t phy_pref);
+int caifdev_phy_unregister(layer_t *phyif);
+int caifdev_phy_loop_register(layer_t *phyif, cfcnfg_phy_type_t phy_type);
+int caifdev_phy_spi_xmitlen(cfspil_t *layr);
+cfpkt_t *caifdev_phy_spi_getxmitpkt(cfspil_t *layr);
+int caifdev_adapt_register(struct caif_channel_config *config,
+ layer_t *adap_layer);
+int caifdev_adapt_unregister(layer_t *adap_layer);
+
+int caif_register_chrdev(int (*chrdev_mgmt)
+ (int action, union caif_action *param));
+void caif_unregister_chrdev(void);
+
+int caif_register_netdev(int (*netdev_mgmt)
+ (int action, union caif_action *param));
+void caif_unregister_netdev(void);
+
+#endif /* CAIF_CHR_H_ */
diff --git a/include/net/caif/caif_config_util.h b/include/net/caif/caif_config_util.h
new file mode 100644
index 0000000..3506993
--- /dev/null
+++ b/include/net/caif/caif_config_util.h
@@ -0,0 +1,27 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Daniel Martensson / Daniel.Martensson@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+
+
+
+
+#ifndef CAIF_CONFIG_UTIL_H_
+#define CAIF_CONFIG_UTIL_H_
+
+#include "caif_config.h"
+#include "caif_ioctl.h"
+#include "cfcnfg.h"
+#include "cfctrl.h"
+#include "caif_actions.h"
+
+int channel_config_2_link_param(cfcnfg_t *cnfg,
+ struct caif_channel_config *s,
+ cfctrl_link_param_t *l);
+
+#endif
diff --git a/include/net/caif/caif_kernel.h b/include/net/caif/caif_kernel.h
new file mode 100644
index 0000000..ff1164a
--- /dev/null
+++ b/include/net/caif/caif_kernel.h
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2009
+ *
+ * CAIF Kernel Internal interface for configuring and accessing
+ * CAIF Channels.
+ *
+ * Author: Sjur Brendeland/ sjur.brandeland@stericsson.com
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#ifndef CAIF_KERNEL_H_
+#define CAIF_KERNEL_H_
+#include "caif_config.h"
+struct sk_buff;
+
+/*!\page caif_kernel.h
+ * This is the specification of the CAIF Kernel internal interface to
+ * CAIF Channels.
+ * This interface follows the pattern used in Linux Device Drivers with a
+ * struct \ref caif_device
+ * holding control data handling each device instance.
+ *
+ * The functional interface consist of a few basic functions:
+ * - \ref caif_add_device Configures and Connect the CAIF
+ * Channel to the remote end. Configuration is described in
+ * \ref caif_channel_config.
+ * - \ref caif_remove_device Disconnect and remove the Channel.
+ * - \ref caif_transmit Sends a CAIF message on the link.
+ * - \ref caif_device.receive_cb Receive Callback function for
+ * receiving packets.
+ * - \ref caif_device.control_cb Control information from the CAIF stack.
+ * - \ref caif_flow_control Send Flow Control mesasge to remote end.
+ *
+ *
+ * Details:
+ * \see { caif_kernel }
+ *
+ * \code
+ *
+#include "caif_kernel.h"
+
+
+static void my_receive(struct caif_device *dev, struct sk_buff *skb)
+{
+...
+}
+
+static void my_control(struct caif_device *dev, enum caif_control ctrl)
+{
+....
+}
+
+int kernel_caif_usage_exampe()
+{
+struct sk_buff *skb;
+char *message = "hello";
+
+
+// Connect the Channel
+struct caif_device caif_dev = {
+.caif_config = {
+.name = "MYDEV",
+.priority = CAIF_PRIO_NORMAL,
+.type = CAIF_CHTY_UTILITY,
+.phy_pref = CAIF_PHYPREF_LOW_LAT,
+.u.utility.name = "CAIF_PSOCK_TEST",
+.u.utility.params = {0x01},
+.u.utility.paramlen = 1,
+},
+
+.receive_cb = my_receive,
+.control_cb = my_control,
+
+};
+ret = caif_add_device(&caif_dev);
+if (ret)
+goto error;
+
+// Send a packet
+skb = caif_create_skb(message, strlen(message));
+ret = caif_transmit(&caif_dev, skb);
+if (ret)
+goto error;
+
+
+// Remove device
+ret = caif_remove_device(&caif_dev);
+if (ret)
+goto error;
+
+}
+
+* \endcode
+*
+* \section Linux Socket Buffer (SKB)
+ * When sending out packets on a connection (\ref caif_transmit)
+ * the CAIF stack will add CAIF protocol headers.
+ * This requires space in the SKB.
+ * Caif has defined \ref CAIF_SKB_HEAD_RESERVE for minimum
+ * required reserved head-space in the packet and
+ * \ref CAIF_SKB_TAIL_RESERVE for minimum reserved tail-space.
+ *
+ * \b NOTE The Linux kernel SKB operations panics if not
+ * enough space is available!
+ *
+ */
+
+ /*! \addtogroup caif_kernel
+ * @{
+ */
+
+struct caif_device;
+
+
+ /** Minimum required CAIF Socket Buffer head-space */
+#define CAIF_SKB_HEAD_RESERVE 32
+
+ /** Minimum required CAIF Socket Buffer tail-space */
+#define CAIF_SKB_TAIL_RESERVE 32
+
+ /** CAIF Control information (used in \ref caif_device.control_cb)
+ * used for receiving control information from Modem. */
+enum caif_control {
+ /** Modem has sent Flow-ON, Clients can start transmitting
+ * data using \ref caif_transmit. */
+ CAIF_CONTROL_FLOW_ON = 0,
+ /** Modem has sent Flow-OFF, Clients must stop transmitting
+ * data using \ref caif_transmit. */
+ CAIF_CONTROL_FLOW_OFF = 1,
+
+ /** Channel Creation is complete. This is an acknowledge to
+ * (\ref caif_add_device) from Modem.
+ * and the Channel is ready for transmit (Flow-state is ON). */
+ CAIF_CONTROL_DEV_INIT = 3,
+
+ /** Spontaneous close request from Modem, only applicable
+ * for Utility Link. The Client should respond by calling
+ * \ref caif_remove_device */
+ CAIF_CONTROL_REMOTE_SHUTDOWN = 4,
+
+ /** Channel disconnect is complete. This is an acknowledge to
+ * (\ref caif_remove_device) from Modem.
+ * \ref caif_transmit or \ref caif_flow_control must not be
+ * called after this. */
+ CAIF_CONTROL_DEV_DEINIT = 5,
+
+ /** Channel Creation has failed. This is an negative acknowledge
+ * to (\ref caif_add_device) from Modem. */
+ CAIF_CONTROL_DEV_INIT_FAILED = 6
+};
+
+
+/** Flow Control information (used in \ref caif_device.control_cb) used
+ * for controlling outgoing flow */
+enum caif_flowctrl {
+ /** Flow Control is ON, transmit function can start sending data */
+ CAIF_FLOWCTRL_ON = 0,
+ /** Flow Control is OFF, transmit function should stop sending data */
+ CAIF_FLOWCTRL_OFF = 1,
+};
+
+/** Transmits CAIF Packets on Channel.
+ * This function is non-blocking and safe to use in tasklet context.
+ * The CAIF Stack takes ownership of the Socket Buffer (SKB) after calling
+ * \ref caif_transmit.
+ * This means that the user cannot access the SKB afterwards, this applies
+ * even in error situations.
+ *
+ * @return 0 on success, < 0 upon error.
+ *
+ * @param[in] skb Socket Buffer holding data to be written.
+ * @param[in] dev Structure used when creating the channel
+ *
+ *
+ * Error codes:
+ * - \b ENOTCONN, The Channel is not connected.
+ * - \b EPROTO, Protocol error (or skb is faulty)
+ * - \b EIO IO Error (unspecified error)
+ */
+int caif_transmit(struct caif_device *dev, struct sk_buff *skb);
+
+
+/** Function for sending flow ON / OFF to remote end.
+ * This function is non-blocking and safe to use in tasklet context.
+ *
+ * @param[in] dev Reference to device data.
+ * @param[in] flow Flow Control information.
+
+ * @return 0 on success, < 0 upon error.
+ * Error codes:
+ * - \b ENOTCONN, The Channel is not connected.
+ * - \b EPROTO, Protocol error.
+ * - \b EIO IO Error (unspecified error).
+ */
+int caif_flow_control(struct caif_device *dev, enum caif_flowctrl flow);
+
+
+
+/** Handle for Kernel Internal CAIF Channels.
+ * All fields in this structure must be filled in by Client before calling
+ * \ref caif_add_device (except _caif_handle).
+ */
+struct caif_device {
+
+ /** Channel Configuration Parameter. Contains information about type
+ * and configuration of the channel.
+ * This must be set before calling \ref caif_add_device. */
+ struct caif_channel_config caif_config;
+
+
+ /** Callback Function for receiving CAIF Packets from Channel.
+ * This callback is called from softirq context (tasklet).
+ * The receiver <b> must </b> free the skb.
+ * <b> DO NOT BLOCK IN THIS FUNCTION! </b>
+ *
+ * If client has to do blocking operations
+ * it must start it's own work queue (or kernel thread).
+ *
+ * @param[in] dev Reference to device data.
+ * @param[in] skb Socket Buffer with received data.
+ */
+ void (*receive_cb) (struct caif_device *dev, struct sk_buff *skb);
+
+
+ /** Callback Function for notifying flow control from remote end see
+ * \ref caif_control.
+ * This callback is called from from softirq context (tasklet).
+ *
+ * <b> DO NOT BLOCK IN THIS FUNCTION! </b>
+ *
+ * Client must not call \ref caif_transmit from this function.
+ *
+ * If client has queued packets to send
+ * it must start its own thread to do \ref caif_transmit from.
+ *
+ * @param[in] dev Reference to device data.
+ * @param[in] ctrl CAIF Control info \ref caif_control.
+ * e.g. Flow control
+ * \ref CAIF_CONTROL_FLOW_ON or
+ * \ref CAIF_CONTROL_FLOW_OFF
+ *
+ */
+ void (*control_cb) (struct caif_device *dev, enum caif_control ctrl);
+
+ /** This is a CAIF private attribute, holding CAIF internal reference
+ * to the CAIF stack. Do not update this field */
+
+ void *_caif_handle;
+
+ /** This field may be filled in by Client for their own usage. */
+ void *user_data;
+};
+
+
+
+/** Add (Connects) a CAIF Channel.
+ * This function is non-blocking. The channel connect is reported in
+ * \ref caif_device.control_cb.
+ * The channel is not open until \ref caif_device.control_cb is called with
+ * \ref CAIF_CONTROL_DEV_INIT.
+ * If setting up the channel fails \ref caif_device.control_cb is called with
+ * \ref CAIF_CONTROL_DEV_INIT_FAILED.
+ *
+ * \ref caif_transmit, \ref caif_flow_control or \ref caif_remove_device must
+ * not be called before receiveing CAIF_CONTROL_DEV_INIT.
+ * @return 0 on success, < 0 on failure.
+ *
+ * Error codes:
+ * - \b -EINVAL Invalid Arguments
+ * - \b -ENODEV No PHY Device exists.
+ * - \b -EIO IO Error (unspecified error)
+ *
+ */
+int caif_add_device(struct caif_device *dev);
+
+
+
+/** Disconnect a CAIF Channel
+ * This function is non-blocking.
+ * The channel is not disconnected until \ref caif_device : control_cb is
+ * called with \ref CAIF_CONTROL_DEV_DEINIT.
+ * \ref caif_transmit or \ref caif_flow_control \b must not be called after
+ * receiving \ref CAIF_CONTROL_DEV_DEINIT.
+ * The Client is responsible for freeing the \ref caif_device structure after
+ * receiving \ref CAIF_CONTROL_DEV_DEINIT (if applicable).
+ * @return 0 on success.
+ *
+ * - \b EIO IO Error (unspecified error)
+ *
+ */
+int caif_remove_device(struct caif_device *caif_dev);
+
+
+
+/** Convenience function for allocating a socket buffer for usage with CAIF
+ * and copy user data into the socket buffer.
+ * @param[in] data User data to send with CAIF.
+ * @param[in] data_length of data to send.
+ * @return socket buffer .
+ *
+ */
+struct sk_buff *caif_create_skb(unsigned char *data, unsigned int data_length);
+
+
+/** Convenience function for extracting data from a socket buffer (SKB) and
+ * then destroy it.
+ * Copies data from the SKB frees the SKB.
+ * @param[in] skb SKB to extract data from. SKB will be freed after extracting
+ * data.
+ *
+ * @param[in] data User data buffer to extract packet data into.
+ * @param[in] max_length User data buffer length,
+ * @return number of bytes extracted; < 0 upon error.
+ *
+ */
+int caif_extract_and_destroy_skb(struct sk_buff *skb, unsigned char *data,
+ unsigned int max_length);
+
+
+
+/*! @} */
+
+#endif /* CAIF_KERNEL_H_ */
diff --git a/include/net/caif/caif_log.h b/include/net/caif/caif_log.h
new file mode 100644
index 0000000..cbcea05
--- /dev/null
+++ b/include/net/caif/caif_log.h
@@ -0,0 +1,83 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Daniel Martensson / Daniel.Martensson@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+
+
+
+
+#ifndef CAIF_LOG_H_
+#define CAIF_LOG_H_
+
+extern int caif_dbg_level;
+
+#define CAIFLOG_ON 1
+
+#define CAIFLOG_MIN_LEVEL 1
+#define CAIFLOG_LEVEL_ERROR 1
+#define CAIFLOG_LEVEL_WARNING 2
+#define CAIFLOG_LEVEL_TRACE 3
+#define CAIFLOG_LEVEL_TRACE2 4
+#define CAIFLOG_LEVEL_TRACE3 5
+#define CAIFLOG_LEVEL_FUNC 6
+#define CAIFLOG_MAX_LEVEL 6
+
+/** Fatal error condition, halt the kernel */
+#define CAIFLOG_FATAL(format, args...) do if ( \
+ caif_dbg_level > CAIFLOG_LEVEL_ERROR) \
+ printk(KERN_ERR "<%s:%d, FATAL> " format, __func__, __LINE__ , \
+ ## args);\
+ while (0)
+
+/** CAIF Error Logging. */
+#define CAIFLOG_ERROR(format, args...) do if (\
+caif_dbg_level > CAIFLOG_LEVEL_ERROR) \
+printk(KERN_ERR "<%s:%d, ERROR> " format, __func__, __LINE__ , ## args);\
+ while (0)
+
+/** CAIF Warning Logging. */
+#define CAIFLOG_WARN(format, args...) do if (\
+caif_dbg_level > CAIFLOG_LEVEL_WARNING) \
+printk(KERN_WARNING "<%s:%d, WARN> " format, __func__, __LINE__ , ## args);\
+ while (0)
+
+/** CAIF Trace Control Logging. Level 1 control trace (Channel setup etc) */
+#define CAIFLOG_TRACE(format, args...) do if (\
+caif_dbg_level > CAIFLOG_LEVEL_TRACE) \
+printk(KERN_WARNING "<%s:%d, TRACE> " format, __func__, __LINE__ , ## args); \
+ while (0)
+
+/** CAIF Trace Payload Logging. Level payload trace */
+#define CAIFLOG_TRACE2(format, args...) do if ( \
+caif_dbg_level > CAIFLOG_LEVEL_TRACE2) \
+printk(KERN_WARNING "<%s:%d, TRACE2> " format, __func__, __LINE__ , ## args);\
+ while (0)
+
+/** CAIF Trace Detailed Logging including packet dumps */
+#define CAIFLOG_TRACE3(format, args...) do if ( \
+caif_dbg_level > CAIFLOG_LEVEL_TRACE3) \
+printk(KERN_WARNING "<%s:%d, TRACE3> " format, __func__, __LINE__ , ## args); \
+ while (0)
+
+/** CAIF Trace Entering Function */
+#define CAIFLOG_ENTER(format, args...) do if (\
+caif_dbg_level > CAIFLOG_LEVEL_FUNC) \
+printk(KERN_WARNING "<%s:%d, ENTER> " format, __func__, __LINE__ , ## args); \
+ while (0)
+
+/** CAIF Trace Exiting Function */
+#define CAIFLOG_EXIT(format, args...) do if (\
+caif_dbg_level > CAIFLOG_LEVEL_FUNC) \
+printk(KERN_WARNING "<%s:%d, EXIT> " format, __func__, __LINE__ , ## args);\
+ while (0)
+
+#define IF_CAIF_TRACE(cmd) do if (\
+caif_dbg_level > CAIFLOG_LEVEL_TRACE) { cmd; } \
+ while (0)
+
+#endif /*CAIF_LOG_H_ */
--
1.6.0.4
^ permalink raw reply related
* [PATCH 2/8] [RFC] CAIF Protocol Stack
From: sjur.brandeland @ 2009-09-23 17:31 UTC (permalink / raw)
To: netdev; +Cc: Kim.xx.Lilliestierna, sjur.brandeland
From: Kim Lilliestierna <Kim.xx.Lilliestierna@ericsson.com>
Signed-off-by: sjur.brandeland@stericsson.com
---
include/net/caif/generic/caif_layer.h | 378 +++++++++++++++++++++++++++++++++
include/net/caif/generic/cfcnfg.h | 223 +++++++++++++++++++
include/net/caif/generic/cfctrl.h | 139 ++++++++++++
include/net/caif/generic/cffrml.h | 29 +++
include/net/caif/generic/cfglue.h | 206 ++++++++++++++++++
include/net/caif/generic/cfloopcfg.h | 28 +++
include/net/caif/generic/cflst.h | 27 +++
include/net/caif/generic/cfmsll.h | 22 ++
include/net/caif/generic/cfmuxl.h | 30 +++
include/net/caif/generic/cfpkt.h | 246 +++++++++++++++++++++
include/net/caif/generic/cfserl.h | 22 ++
include/net/caif/generic/cfshml.h | 21 ++
include/net/caif/generic/cfspil.h | 80 +++++++
include/net/caif/generic/cfsrvl.h | 48 ++++
include/net/caif/generic/fcs.h | 22 ++
15 files changed, 1521 insertions(+), 0 deletions(-)
create mode 100644 include/net/caif/generic/caif_layer.h
create mode 100644 include/net/caif/generic/cfcnfg.h
create mode 100644 include/net/caif/generic/cfctrl.h
create mode 100644 include/net/caif/generic/cffrml.h
create mode 100644 include/net/caif/generic/cfglue.h
create mode 100644 include/net/caif/generic/cfloopcfg.h
create mode 100644 include/net/caif/generic/cflst.h
create mode 100644 include/net/caif/generic/cfmsll.h
create mode 100644 include/net/caif/generic/cfmuxl.h
create mode 100644 include/net/caif/generic/cfpkt.h
create mode 100644 include/net/caif/generic/cfserl.h
create mode 100644 include/net/caif/generic/cfshml.h
create mode 100644 include/net/caif/generic/cfspil.h
create mode 100644 include/net/caif/generic/cfsrvl.h
create mode 100644 include/net/caif/generic/fcs.h
diff --git a/include/net/caif/generic/caif_layer.h b/include/net/caif/generic/caif_layer.h
new file mode 100644
index 0000000..76280e6
--- /dev/null
+++ b/include/net/caif/generic/caif_layer.h
@@ -0,0 +1,378 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Sjur Braendeland/sjur.brandeland@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+
+#ifndef CAIF_LAYER_H_
+#define CAIF_LAYER_H_
+
+#include "cfglue.h"
+/* Forward Declaration */
+struct _layer_t;
+struct _cfpkt_t;
+struct _cfpktq_t;
+struct _transmt_info;
+struct _caif_packet_funcs_t;
+typedef struct _cfpktq_t cfpktq_t;
+/* Type definitions*/
+typedef struct _transmt_info transmt_info;
+
+typedef struct _layer_t layer_t;
+typedef struct _caif_packet_funcs_t caif_packet_funcs_t;
+typedef struct _cfpkt_t cfpkt_t;
+
+#define CAIF_MAX_FRAMESIZE 4096
+#define CAIF_MAX_PAYLOAD_SIZE (4096 - 64)
+#define CAIF_NEEDED_HEADROOM (10)
+#define CAIF_NEEDED_TAILROOM (2)
+
+/*! \addtogroup GenCaifExternal
+ * Additional documentation for group `GenCaifExternal'
+ * @{
+ */
+
+
+/** CAIF Control Signaling.
+ * These commands are sent upwards in the CAIF stack. They are used for
+ * Signaling originating from Modem.
+ * These are either responses (*_RSP) or events (*_IND).
+
+ */
+typedef enum _caif_ctrlcmd_t {
+ /** Flow Control is OFF, transmit function should stop sending data */
+ CAIF_CTRLCMD_FLOW_OFF_IND = 0,
+ /** Flow Control is ON, transmit function can start sending data */
+ CAIF_CTRLCMD_FLOW_ON_IND = 1,
+ /** Remote end CCPU has decided to close down channel */
+ CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND = 5,
+ /** Called initially when the layer below has finished initialization */
+ CAIF_CTRLCMD_INIT_RSP = 3,
+ /** Called when de-initialization is complete */
+ CAIF_CTRLCMD_DEINIT_RSP = 4,
+ /** Called if initialization failes */
+ CAIF_CTRLCMD_INIT_FAIL_RSP = 6,
+ /** Note: Only used internally in GenCaif.
+ * Called if physical interface cannot send more packets */
+ _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND = 7,
+ /** Note: Only used internally in GenCaif.
+ * Called if physical interface is able to send packets again */
+ _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND = 8
+} caif_ctrlcmd_t;
+
+
+
+/** Modem Control Signaling.
+ * These are requests sent 'down-wards' in the stack.
+ * Flow ON, OFF can be indicated to the modem.
+ *
+ */
+typedef enum _caif_modemcmd_t {
+ /** Flow Control is ON, transmit function can start sending data */
+ CAIF_MODEMCMD_FLOW_ON_REQ = 0,
+ /** Flow Control is OFF, transmit function should stop sending data */
+ CAIF_MODEMCMD_FLOW_OFF_REQ = 1,
+ /** Notify physical layer that it is in use */
+ _CAIF_MODEMCMD_PHYIF_USEFULL = 3,
+ /** Notify physical layer that it is no longer in use */
+ _CAIF_MODEMCMD_PHYIF_USELESS = 4
+} caif_modemcmd_t;
+
+/** CAIF Packet Direction.
+ * Indicate if a packet is to be sent \b out or to be received \b in.
+ *
+ *
+ */
+typedef enum _caif_direction_t {
+ CAIF_DIR_IN = 0, /*!< Incoming packet received. */
+ CAIF_DIR_OUT = 1 /*!< Outgoing packet to be transmitted. */
+} caif_direction_t;
+
+/*!
+ * Receive Function.
+ * Contract: Each layer must implement a receive function passing the Caif
+ * Packets upwards in the stack.
+ * Packet handling rules:
+ * -# The CAIF Packet (cfpkt) cannot be accessed after passing i
+ * to the next layer using up->receive().
+ * -# If parsing of the packet fail, the packet must be destroyed
+ * and -1 returned from the function.
+ * -# If parsing succeeds (and above layers return ok) function
+ * must return value > 0.
+ *
+ * @param[in] layr Pointer to the current layer the receive function is
+ * implemented for (this pointer).
+ * @param[in] cfpkt Pointer to CaifPacket to be handled.
+ * @return result < 0 indicates an error, 0 or positive value indicate success.
+ */
+typedef int (*receive_cb_t) (layer_t *layr, struct _cfpkt_t *cfpkt);
+
+/*!
+ * Transmit Function.
+ * Contract: Each layer must implement a transmit function passing the Caif
+ * Packet downwards in the stack.
+ * Packet handling rules:
+ * -# The CAIF Packet (cfpkt) ownership is passed to the transmit
+ * function. This means that the the packet cannot be access
+ * after passing it to the below layer using dn->transmit().
+ *
+ *
+ * -# However if transmit failes, the ownership is returned to
+ * caller. The caller of "dn->transmit()" must destroy or
+ * resend packet.
+ *
+ * -# Return less than zero means error, greater than zero means OK.
+ *
+ * @param[in] layr Pointer to the current layer the receive function is
+ * implemented for (this pointer).
+ * @param[in] cfpkt Pointer to CaifPacket to be handled.
+ * @param[in] info Info about physical layer (filled in by MUX layer) and
+ CAIF header size (each layer adds to hdr_len).
+ * @return result < 0 indicates an error, 0 or positive value
+ * indicate success.
+ */
+typedef int (*transmit_cb_t) (layer_t *layr, transmt_info *info,
+ struct _cfpkt_t *cfpkt);
+
+/*!
+ * Control Function used to signal upwards in the CAIF stack.
+ * Used for signaling responses (CAIF_CTRLCMD_*_RSP)
+ * and asynchronous events from the modem (CAIF_CTRLCMD_*_IND)
+ *
+ * @param[in] layr Pointer to the current layer the receive function is
+ * implemented for (this pointer).
+ * @param[in] ctrl Control Command.
+ */
+typedef void
+ (*ctrlcmd_cb_t) (layer_t *layr, caif_ctrlcmd_t ctrl, int phyid);
+
+/*!
+ * Control Function used for controlling the modem. Used to signal down-wards
+ * in the CAIF stack.
+ * @returns 0 on success, < 0 upon failure.
+ * @param[in] layr Pointer to the current layer the receive function is
+ * implemented for (this pointer).
+ * @param[in] ctrl Control Command.
+ */
+typedef int (*modemcmd_cb_t) (layer_t *layr, caif_modemcmd_t ctrl);
+
+/*!
+ * This function is used by the CAIF-Manager for initiating the adaptation
+ * layer.
+ * @param in caifstack The instance of layer below the adaptation layer in
+ * the CAIF stack (service layer)
+ * @param in pktfuncs The packet functions needed to create a packet is
+ * passed in this structure
+ * @param out adap_layer This layer is allocated and returned from the
+ * adaptaion layer, and is inserted
+ * in the CAIF stack as the topmost layer.
+ *
+ */
+typedef void
+ (*init_adaptation_layer) (layer_t *caifstack,
+ caif_packet_funcs_t *pktfuncs,
+ layer_t **adap_layer);
+
+
+/** This structure defines the generic layered structure in CAIF.
+ * It is inspired by the "Protocol Layer Design Pattern" (Streams).
+ *
+ * It defines a generic layering structure, used by all CAIF Layers and the
+ * layers interfacing CAIF.
+ *
+ * In order to integrate with CAIF an adaptation layer on top of the CAIF stack
+ * and PHY layer below the CAIF stack
+ * must be implemented. These layer must follow the design principles below.
+ *
+ * Principles for layering of protocol layers:
+ * -# All layers must use this structure. If embedding it, then place this
+ * structure first in the layer specific structure.
+ * -# Each layer should not depend on any others layer private data.
+ * -# In order to send data upwards do
+ * \code layer->up->receive(layer->up, packet); \endcode
+ * \see {receive_cb_t }
+ * -# In order to send data downwards do
+ * \code layer->dn->transmit(layer->dn, info, packet); \endcode
+ * \see {transmit_cb_t }
+ *
+ *
+ *
+ */
+struct _layer_t {
+
+ struct _layer_t *up; /*!< Pointer to the layer above */
+ struct _layer_t *dn; /*!< Pointer to the layer below */
+ receive_cb_t receive; /*!< Pointer to the receive function for this
+ * layer,used by the layer below to pass data
+ * upwards in the CAIF stack.*/
+ transmit_cb_t transmit; /*!< Pointer to the transmit function used for
+ * the layer above to pass packet to be
+ * sent out on the stack.*/
+ ctrlcmd_cb_t ctrlcmd; /*!< Pointer to function used by the CAIF stack
+ * to signal to the layer above.*/
+ modemcmd_cb_t modemcmd; /*!< Pointer to function used by the CAIF stack
+ * to signal to the layer below. */
+ struct _layer_t *next; /*!< Pointer to chain of layers, up/dn will
+ * then point at the first element of a
+ * which then should be iterated through
+ * the next pointer.*/
+ unsigned short prio; /*!< Priority of this layer */
+ unsigned int id; /*!< The identity of this layer. */
+ unsigned int type; /*<! The type of this layer */
+ char name[9]; /*!< Name of the layer */
+};
+
+/** Set the up pointer for a specified layer.
+ * @param layr Layer where up pointer shall be set.
+ * @param above Layer above.
+ */
+#define layer_set_up(layr, above) ((layr)->up = (struct _layer_t *)(above))
+
+/** Set the dn pointer for a specified layer.
+ * @param layr Layer where down pointer shall be set.
+ * @param below Layer below.
+ */
+#define layer_set_dn(layr, below) ((layr)->dn = (struct _layer_t *)(below))
+
+/**
+ * Transmit info, passed down-wards in protocol layers.
+ */
+struct _transmt_info {
+ /** Channel ID of the logical CAIF connection.
+ * Is used by Service Layer to indicate to mux the PHY-layer
+ * (Physical-ID) to send packet over.
+ */
+ unsigned short channel_id;
+
+ /** Physical ID of the logical physical connection.
+ * Used by Service Layers to identify their physical id to Caif MUX (CFMUXL)
+ * so that the MUX can add the
+ * correct physical Id to the packet.
+ */
+
+ unsigned short phid;
+ /** Header Length, used to align pay load on 32bit boundary.
+ * Used by SPI Layer (CFSPIL) to align start of pay-load data (IP header
+ * start) to 16 or 32 bits boundary.
+ * All layers add the number of header bytes they are using, then SPI
+ * layer adds padding to get correct alignment.
+ */
+
+ unsigned short hdr_len;
+ /** Packet priority. */
+ unsigned char prio;
+};
+
+/** Packet functions needed by Adaptation layer and PHY layer are exported in
+ * this structure
+ *
+ */
+struct _caif_packet_funcs_t {
+
+#ifdef CAIF_USE_SKB
+ /** Used map from a "native" packet e.g. Linux Socket Buffer to a CAIF packet.
+ * @param dir - Direction telling if this is an packet to be sent or received
+ * @param nativepkt - The native packet to be transformed to a CAIF packe
+ * @returns the mapped CAIF Packet CFPKT.
+ */
+ cfpkt_t *
+ (*cfpkt_fromnative)(caif_direction_t dir, void *nativepkt);
+
+ /** Used map from a CAIF packet to a "native" packet e.g. Linux Socket Buffer.
+ * @param pkt - The CAIF packet to be transformed to a "native" packet.
+ * @returns The native packet transformed from a CAIF packet.
+ */
+ void *(*cfpkt_tonative)(cfpkt_t *pkt);
+#endif
+ /** Used by "app" layer to create an outgoing CAIF packet to be sent ou
+ * of the CAIF Stack.
+ * @param data - Packet data to copy into the packet. If NULL copying will
+ * not take place.
+ * @param len - Length of data to copy into the packe
+ * @returns a new CAIF Packet CFPKT.
+ * @deprecated Use \b cfpkt_create_pkt (above) instead.
+ */
+ cfpkt_t *
+ (*cfpkt_create_recv_pkt)(const unsigned char *data,
+ unsigned int len);
+
+ /** Used by PHY layer to create an incoming CAIF packet to be processed by
+ * the CAIF Stack.
+ * @param data - Packet data to copy into the packet. If NULL copying
+ * will not take place.
+ * @param len - Length of data to copy into the packe
+ * @returns a new CAIF Packet CFPKT.
+ * @deprecated Use \b cfpkt_create_pkt (above) instead.
+ */
+ cfpkt_t *
+ (*cfpkt_create_xmit_pkt)(const unsigned char *data,
+ unsigned int len);
+
+ /** Used to extract data from a CAIF packe
+ * @param cfpkt Packet to extract data from.
+ * @param buf Buffer to hold the data to be extracted from the
+ * CAIF packet.
+ * @param buflen Length of the buffer (maximum length of the data to
+ * copy into the buffer).
+ * @param actual_len Amount of bytes copied from the packet into the buffer.
+ */
+ void
+ (*cfpkt_extract)(cfpkt_t *cfpkt, void *buf, unsigned int buflen,
+ unsigned int *actual_len);
+
+ /** Releases a CAIF Packe
+ * @param cfpkt Packet to destroy.
+ */
+ void
+ (*cfpkt_destroy)(cfpkt_t *cfpkt);
+
+
+
+/** Append by giving user access to packet buffer
+ * @param pkt Packet to append to
+ * @param buf Buffer inside pkt that user shall copy data into
+ * @param buflen Length of buffer and number of bytes added to packe
+ * @return < 0 on error
+ */
+
+ int (*cfpkt_raw_append)(cfpkt_t *cfpkt, void **buf,
+ unsigned int buflen);
+
+/** Extract by giving user access to packet buffer
+ * @param pkt Packet to extract from
+ * @param buf Buffer inside pkt that user shall copy data from
+ * @param buflen Length of buffer and number of bytes removed from packe
+ * @return < 0 on error
+ */
+ int (*cfpkt_raw_extract)(cfpkt_t *cfpkt, void **buf,
+ unsigned int buflen);
+
+
+ /** Creates a packet queue */
+ cfpktq_t *(*cfpktq_create)(void);
+
+ /** Inserts a packet into the packet queue, packets are ordered by priority.
+ * If the same priority is used packets are ordered as a FIFO.
+ */
+ void (*cfpkt_queue)(cfpktq_t *pktq, cfpkt_t *pkt,
+ unsigned short prio);
+
+
+ /** Peek into the first packet in the queue */
+ cfpkt_t *(*cfpkt_qpeek)(cfpktq_t *pktq);
+
+ /** Dequeue a packet from the queue */
+ cfpkt_t *(*cfpkt_dequeue)(cfpktq_t *pktq);
+
+ /** Get length of a packet */
+ uint16(*cfpkt_getlen)(cfpkt_t *pkt);
+
+};
+
+/*! @} */
+
+#endif /* CAIF_LAYER_H_ */
diff --git a/include/net/caif/generic/cfcnfg.h b/include/net/caif/generic/cfcnfg.h
new file mode 100644
index 0000000..853eff4
--- /dev/null
+++ b/include/net/caif/generic/cfcnfg.h
@@ -0,0 +1,223 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+
+
+
+
+
+
+
+#ifndef CFCNFG_H_
+#define CFCNFG_H_
+#include "caif_layer.h"
+#include "cfctrl.h"
+struct _cfctrl_t;
+struct _cfcnfg_t;
+
+/** CAIF Configuration Layer (CFCNFG)*/
+typedef struct _cfcnfg_t cfcnfg_t;
+
+/*! \addtogroup GenCaifExternal
+ * Additional documentation for group `GenCaifExternal'
+ * @{
+ */
+
+
+/** Types of Physical Layers defined in CAIF Stack */
+typedef enum _cfcnfg_phy_type_t {
+ CFPHYTYPE_UNKNOWN = 0,
+ CFPHYTYPE_SERIAL = 1, /*!< Serial Physical Interface */
+ CFPHYTYPE_SPI = 2, /*!< SPI Physical Interface */
+ CFPHYTYPE_MSL = 3, /*!< MSL Physical Interface */
+ CFPHYTYPE_SHM = 4, /*!< Shared Memory Physical Interface */
+ CFPHYTYPE_LOOP = 5, /*!< Loopback Physical Interface */
+ _CFPHYTYPE_MAX = 6
+} cfcnfg_phy_type_t;
+
+/** Physical Preference - HW Abstraction */
+typedef enum _cfcnfg_phy_preference_t {
+ /** Default Physical Interface */
+ CFPHYPREF_UNSPECIFIED = 0xa0,
+ /** Default Physical Interface for Low Latency Traffic */
+ CFPHYPREF_LOW_LAT = 0xd0,
+ /** Default Physical Interface for High Bandwidth Traffic */
+ CFPHYPREF_HIGH_BW = 0xe0,
+ /** \b TEST \b ONLY Loop-back Interface Simulating Acc side responses */
+ CFPHYPREF_LOOP = 0x70,
+ /** \b TEST \b ONLY Raw loopback interface */
+ CFPHYPREF_RAW_LOOP = 0x80
+} cfcnfg_phy_preference_t;
+
+/** Types of CAIF Links defined in CAIF Stack
+ * @obsolete
+ */
+typedef enum _cfcnfg_link_type_t {
+ _CF_DECM = 0, /*!< DECM link - NOT SUPPORTED */
+ CF_VEI = CFCTRL_SRV_VEI, /*!< VEI link - AT */
+ CF_VIDEO = CFCTRL_SRV_VIDEO, /*!< Video Link */
+ CF_DEBUG = CFCTRL_SRV_DBG, /*!< Debug Link */
+ CF_DATAGRAM = CFCTRL_SRV_DATAGRAM, /*!< Datagram link */
+ CF_RFM = CFCTRL_SRV_RFM, /*!< RFM link */
+ CF_UTILITY = CFCTRL_SRV_UTIL /*!< Utility link */
+} cfcnfg_link_type_t;
+
+
+/** Configuration parameters for a physical layer (e.g. Serial) */
+typedef struct cfcnfg_phy_param_t {
+ int foo;
+} cfcnfg_phy_param_t;
+
+/** Configuration information used to setup the CAIF Physical Interface */
+typedef struct cfcnfg_phy_config {
+ /** CAIF Physical Type */
+ cfcnfg_phy_type_t phy_type;
+ /** Instance Number, e.g. Uart Number */
+ uint8 phy_sub_instance;
+ /** Preference LowLatency/HighBandwithd */
+ cfcnfg_phy_preference_t pref;
+ /** Device Name */
+ char name[20];
+ /** Cheksum is used for Interface */
+ bool checksum;
+ /** Configuration param specific for the PHY Type */
+ cfcnfg_phy_param_t param;
+ /** Pointer to layer above */
+ layer_t *up;
+} cfcnfg_phy_config_t;
+
+/** Registration information used to setup the CAIF Physical Interface */
+typedef struct cfcnfg_phy_mgmt {
+ /** Registation of type */
+ cfcnfg_phy_type_t type;
+ /** Creates an instance of the physical layer (e.g. Serial)
+ * and configures it */
+ layer_t *(*create_phy) (cfcnfg_phy_config_t *config);
+ /** Delete an instance of the physical layer (e.g. Serial) */
+ int (*delete_phy) (layer_t *l);
+} cfcnfg_phy_mgmt_t;
+
+
+
+
+/**
+ * This variable is used as a global flag in order to set if STX is used on
+ * serial communication.
+ * NOTE: This is not a fully future proof solution.
+ */
+
+extern int serial_use_stx;
+
+
+/**
+ * Create the CAIF Configuration Object.
+ * \image html CreateCaifConfig.jpg "Create Caif Configuration Object."
+ * @returns the created instance of a CFCNFG object.
+ */
+cfcnfg_t *cfcnfg_create(void);
+
+/**
+ * Adds a physical layer to the CAIF stack.
+ * \image html AddPhyCaifConfig.jpg "Add a PHY layer to CAIF Stack."
+ * @param cnfg Pointer to the Caif Configuration Class, created by
+ * fcnfg_create().
+ * @param phy_type Specifies the type of physical interface e.g.
+ * CFPHYTYPE_SERIAL.
+ * @param phy_layer Specify the physical layer, the transmit function
+ * MUST be set in the structure.
+ * @param phyid [out] The assigned physical ID for this layer,
+ * used in \ref cfcnfg_add_adapt_layer to specify
+ * PHY for the link.
+ */
+
+void
+cfcnfg_add_phy_layer(cfcnfg_t *cnfg, cfcnfg_phy_type_t phy_type,
+ layer_t *phy_layer, uint16 *phyid,
+ cfcnfg_phy_preference_t pref);
+
+/**
+ * Deletes a Adaptation Layer from the CAIF Stack.
+ *
+ * @param cnfg Pointer to the CAIF Configuration Class, created by
+ * cfcnfg_create().
+ * @param adap_layer Adaptation layer to be removed.
+ * @return true on success, false upon failure.
+ */
+
+bool cfcnfg_del_adapt_layer(struct _cfcnfg_t *cnfg, layer_t *adap_layer);
+
+/**
+ * Adds a Adaptation Layer to the CAIF Stack.
+ * The Adaptation Layer is where the interface to application or higher-level
+ * driver functionality is implemented.
+ * \image html AddVeiCaifConfig.jpg "Add an Adaptation layer to CAIF Stack."
+ *
+ * @param cnfg Pointer to the CAIF Configuration Class, created by
+ * cfcnfg_create().
+ * @param linktype Type of link which is set up e.g. CF_AT_PLAIN.
+ * @param connid Connection ID, used for data-gram links.
+ * @param phyid PHY ID received from \ref cfcnfg_add_phy_layer,
+ * specifying the PHY device to use for this link.
+ * @param adap_layer Specify the adaptation layer, the receive
+ * and flow-control functions MUST be set in the structure.
+ * @return true on success, false upon failure.
+ */
+bool
+cfcnfg_add_adapt_layer(cfcnfg_t *cnfg, cfcnfg_link_type_t linktype,
+ uint32 connid, uint16 phyid, layer_t *adap_layer);
+
+
+
+/**
+ * Adds a Adaptation Layer to the CAIF Stack.
+ * The Adaptation Layer is where the interface to application or higher-level
+ * driver functionality is implemented.
+ * \image html AddVeiCaifConfig.jpg "Add an Adaptation layer to CAIF Stack."
+ *
+ * @param cnfg Pointer to the CAIF Configuration Class, created by
+ * cfcnfg_create().
+ * @param param Link setup parameters.
+ * @param adap_layer Specify the adaptation layer, the receive and flow-control functions MUST be set in the structure.
+ * @return true on success, false upon failure.
+ */
+bool
+cfcnfg_add_adaptation_layer(cfcnfg_t *cnfg, cfctrl_link_param_t *param,
+ layer_t *adap_layer);
+
+
+/**
+ * Returns a handle to the Packet Functions used to create packets with
+ * content, and extract information from packets.
+ */
+caif_packet_funcs_t cfcnfg_get_packet_funcs(void);
+
+/** Get Physical Id given type.
+ * @return Returns one of the physical interfaces matching the given type.
+ * Zero if no match is found.
+ */
+int cfcnfg_get_phyid(cfcnfg_t *cnfg, cfcnfg_phy_preference_t phy_pref);
+
+
+/** Get Physical Id given name.
+ * @return Returns the physical interface matching the specified name.
+ */
+int cfcnfg_get_named(cfcnfg_t *cnfg, char *name);
+
+int cfcnfg_instanciate(cfcnfg_t *cnfg, cfcnfg_phy_config_t *phy_config);
+int cfcnfg_instanciate2(cfcnfg_t *cnfg, cfcnfg_phy_type_t phy_type,
+ uint8 instance, char *name, bool checksum,
+ cfcnfg_phy_preference_t pref,
+ cfcnfg_phy_param_t *param);
+int cfcnfg_delete_phy_inst(cfcnfg_t *cfg, char *name);
+int cfcnfg_unregister_phy_type(cfcnfg_t *cfg, cfcnfg_phy_type_t type);
+int cfcnfg_register_phy_type(cfcnfg_t *cfg, cfcnfg_phy_mgmt_t *mgmt);
+int cfcnfg_del_phy_layer(struct _cfcnfg_t *cnfg, layer_t *phy_layer);
+
+/*! @} */
+#endif /* CFCNFG_H_ */
diff --git a/include/net/caif/generic/cfctrl.h b/include/net/caif/generic/cfctrl.h
new file mode 100644
index 0000000..428d781
--- /dev/null
+++ b/include/net/caif/generic/cfctrl.h
@@ -0,0 +1,139 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+
+
+
+
+
+
+#ifndef CFCTRL_H_
+#define CFCTRL_H_
+#include "caif_layer.h"
+#include "cfsrvl.h"
+
+
+/* CAIF Control packet commands*/
+typedef enum {
+ CFCTRL_CMD_LINK_SETUP = 0,
+ CFCTRL_CMD_LINK_DESTROY = 1,
+ CFCTRL_CMD_LINK_ERR = 2,
+ CFCTRL_CMD_ENUM = 3,
+ CFCTRL_CMD_SLEEP = 4,
+ CFCTRL_CMD_WAKE = 5,
+ CFCTRL_CMD_LINK_RECONF = 6,
+ CFCTRL_CMD_START_REASON = 7,
+ CFCTRL_CMD_RADIO_SET = 8,
+ CFCTRL_CMD_MODEM_SET = 9,
+ CFCTRL_CMD_DATA = 0,
+ CFCTRL_CMD_MASK = 0xf
+} cfctrl_cmd_t;
+
+typedef enum {
+ CFCTRL_SRV_DECM = 0,
+ CFCTRL_SRV_VEI = 1,
+ CFCTRL_SRV_VIDEO = 2,
+ CFCTRL_SRV_DBG = 3,
+ CFCTRL_SRV_DATAGRAM = 4,
+ CFCTRL_SRV_RFM = 5,
+ CFCTRL_SRV_UTIL = 6,
+ CFCTRL_SRV_MASK = 0xf
+} cfctrl_srv_t;
+
+#define CFCTRL_RSP_BIT 0x20
+#define CFCTRL_ERR_BIT 0x10
+typedef void
+ (*cfctrl_rspcp_t) (void);
+
+
+
+typedef void
+ (*cfctrl_linkdestroy_rspcb_t) (layer_t *layer, uint8 linkid,
+ layer_t *client_layer);
+
+typedef void
+ (*cfctrl_linksetup_rspcb_t) (layer_t *layer, uint8 linkid,
+ cfctrl_srv_t serv, uint8 phyid,
+ layer_t *adapt_layer);
+
+typedef struct _cfctrl_rsp_t {
+ cfctrl_linksetup_rspcb_t linksetup_rsp;
+ cfctrl_linkdestroy_rspcb_t linkdestroy_rsp;
+ cfctrl_rspcp_t linkerror_ind;
+ cfctrl_rspcp_t enum_rsp;
+ cfctrl_rspcp_t sleep_rsp;
+ cfctrl_rspcp_t wake_rsp;
+ cfctrl_rspcp_t restart_rsp;
+ cfctrl_rspcp_t radioset_rsp;
+ cfctrl_linkdestroy_rspcb_t reject_rsp;
+} cfctrl_rsp_t;
+
+/** Link Setup Parameters for CAIF-Links. */
+typedef struct _cfctrl_link_param_t {
+ cfctrl_srv_t linktype;/*!< (T3,T0) Type of Channel */
+ uint8 priority; /*!< (P4,P0) Priority of the channel */
+ uint8 phyid; /*!< (U2-U0) Physical interface to connect */
+ uint8 endpoint; /*!< (E1,E0) Endpoint for data channels */
+ uint8 chtype; /*!< (H1,H0) Channel-Type,
+ applies to VEI, DEBUG */
+ union {
+ struct {
+ uint8 connid; /*!< (D7,D0) Video LinkId */
+ } video;
+
+ struct {
+ uint32 connid; /*!< (N31,Ngit0) Connection ID used
+ * for Datagram */
+ } datagram;
+
+ struct {
+ uint32 connid; /*!< Connection ID used for RFM */
+ char volume[20]; /*!< Volume to mount for RFM */
+ } rfm; /*!< Configuration for RFM */
+
+ struct {
+ uint16 fifosize_kb; /*!< Psock FIFO size in KB */
+ uint16 fifosize_bufs; /*!< Psock # signal buffers */
+ char name[16]; /*!< Name of the PSOCK service */
+ uint8 params[255]; /*!< Link setup Parameters> */
+ uint16 paramlen; /*!< Length of Link Setup
+ * Parameters */
+ } utility; /*!< Configuration for Utility Links (Psock) */
+ } u;
+} cfctrl_link_param_t;
+
+/** This structure is used internally in CFCTRL */
+struct cfctrl_request_info {
+ int sequence_no;
+ cfctrl_cmd_t cmd;
+ uint8 channel_id;
+ cfctrl_link_param_t param;
+ struct cfctrl_request_info *next;
+ layer_t *client_layer;
+};
+
+
+void cfctrl_enum_req(layer_t *cfctrl, uint8 physlinkid);
+
+
+
+void cfctrl_linkup_request(layer_t *cfctrl, cfctrl_link_param_t *param,
+ layer_t *user_layer);
+
+
+void cfctrl_linkdown_req(layer_t *cfctrl, uint8 linkid, layer_t *client);
+void cfctrl_sleep_req(layer_t *cfctrl);
+void cfctrl_wake_req(layer_t *cfctrl);
+void cfctrl_getstartreason_req(layer_t *cfctrl);
+
+layer_t *cfctrl_create(void);
+void cfctrl_set_dnlayer(layer_t *this, layer_t *dn);
+void cfctrl_set_uplayer(layer_t *this, layer_t *up);
+void cfctrl_set_respfuncs(layer_t *this, cfctrl_rsp_t *respfuncs);
+#endif /* CFCTRL_H_ */
diff --git a/include/net/caif/generic/cffrml.h b/include/net/caif/generic/cffrml.h
new file mode 100644
index 0000000..13d3ff1
--- /dev/null
+++ b/include/net/caif/generic/cffrml.h
@@ -0,0 +1,29 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+
+
+
+
+
+
+#ifndef CFFRM_H_
+#define CFFRM_H_
+#include "cfglue.h"
+#include "caif_layer.h"
+#include "cflst.h"
+
+struct _cffrml_t;
+typedef struct _cffrml_t cffrml_t;
+layer_t *cffrml_create(uint16 phyid, bool DoFCS);
+void cffrml_set_uplayer(layer_t *this, layer_t *up);
+void cffrml_set_dnlayer(layer_t *this, layer_t *dn);
+void cffrml_destroy(layer_t *layer);
+
+#endif /* CFFRM_H_ */
diff --git a/include/net/caif/generic/cfglue.h b/include/net/caif/generic/cfglue.h
new file mode 100644
index 0000000..0930309
--- /dev/null
+++ b/include/net/caif/generic/cfglue.h
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2009
+ *
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ *
+ */
+
+/*
+ * Description: This file contains the OS and HW dependencies for CAIF.
+ */
+
+
+#ifndef CFGLU_H_
+#define CFGLU_H_
+
+/*! \addtogroup GenCaifGlue
+ * Additional documentation for group `GenCaifGlue'
+ * @{
+ */
+
+
+#define CFLOG_LEVEL_ERROR 1
+#define CFLOG_LEVEL_WARNING 2
+#define CFLOG_LEVEL_TRACE 3
+#define CFLOG_LEVEL_TRACE2 4
+#define CFLOG_LEVEL_TRACE3 5
+#define CFLOG_LEVEL_FUNC 6
+
+
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
+#include "linux/stddef.h"
+#include "linux/types.h"
+
+
+/** Unsigned 8 bit */
+typedef __u8 uint8;
+
+/** Unsigned 16 bit */
+typedef __u16 uint16;
+
+/** Unsigned 32 bit */
+typedef __u32 uint32;
+
+
+/******************************************
+ * HANDLING ENDIANNES.
+ * CAIF uses little-endian byte order.
+ ******************************************/
+
+/** CAIF Endian handling Net to Host of 16 bits unsigned */
+#define cfglu_le16_to_cpu(v) le16_to_cpu(v)
+
+/** CAIF Endian handling Host to Net of 16 bits unsigned */
+#define cfglu_cpu_to_le16(v) cpu_to_le16(v)
+
+/** CAIF Endian handling Host to Net of 32 bits unsigned */
+#define cfglu_le32_to_cpu(v) le32_to_cpu(v)
+
+/** CAIF Endian handling Net to Host of 32 bits unsigned */
+#define cfglu_cpu_to_le32(v) cpu_to_le32(v)
+
+extern int caif_dbg_level;
+
+
+/* LOGGING */
+
+#define _CFLOG_FATAL(format, args...) \
+ do {if (caif_dbg_level > CFLOG_LEVEL_ERROR) {\
+ printk(KERN_ERR "<%s:%d, FATAL> " format "\n",\
+ __func__, __LINE__ , ## args); } } while (0)
+
+#define CFLOG_FATAL(format) _CFLOG_FATAL format
+
+/** CAIF Error Logging. */
+#define _CFLOG_ERROR(format, args...)\
+ do {if (caif_dbg_level > CFLOG_LEVEL_ERROR) {\
+ printk(KERN_ERR "<%s:%d, ERROR> " format "\n",\
+ __func__, __LINE__ , ## args); } } while (0)
+
+#define CFLOG_ERROR(format) _CFLOG_ERROR format
+
+/** CAIF Warning Logging. */
+#define _CFLOG_WARN(format, args...)\
+ do {if (caif_dbg_level > CFLOG_LEVEL_WARNING) {\
+ printk(KERN_WARNING "<%s:%d, WARN> " format "\n",\
+ __func__, __LINE__ , ## args); } } while (0)
+
+#ifdef CAIF_DEBUG_ON
+#define CFLOG_WARN(format) _CFLOG_WARN format
+
+/** CAIF Trace Control Logging. Level 1 control trace (Channel setup etc) */
+#define _CFLOG_TRACE(format, args...) \
+ do { if (caif_dbg_level > CFLOG_LEVEL_TRACE) {\
+ printk(KERN_INFO "<%s:%d, TRACE> " format, \
+ __func__, __LINE__ , ## args); } } while (0)
+
+#define CFLOG_TRACE(format) _CFLOG_TRACE format
+
+/** CAIF Trace Payload Logging. Level payload trace */
+#define _CFLOG_TRACE2(format, args...) \
+ do {if (caif_dbg_level > CFLOG_LEVEL_TRACE2) {\
+ printk(KERN_INFO "<%s:%d, TRACE2> " format, \
+ __func__, __LINE__ , ## args); } } while (0)
+
+#define CFLOG_TRACE2(format) _CFLOG_TRACE2 format
+
+/** CAIF Trace Detailed Logging including packet dumps */
+#define _CFLOG_TRACE3(format, args...)\
+ do {if (caif_dbg_level > CFLOG_LEVEL_TRACE3) {\
+ printk(KERN_INFO "<%s:%d, TRACE3> " format, \
+ __func__, __LINE__ , ## args); } } while (0)
+
+#define CFLOG_TRACE3(format) _CFLOG_TRACE3 format
+
+/** CAIF Trace Entering Function */
+#define _CFLOG_ENTER(format, args...) \
+ do {if (caif_dbg_level > CFLOG_LEVEL_FUNC) {\
+ printk("KERN_INFO <%s:%d, ENTER> " format, \
+ __func__, __LINE__ , ## args); } } while (0)
+#define CFLOG_ENTER(format) _CFLOG_ENTER format
+
+/** CAIF Trace Exiting Function */
+
+#define _CFLOG_EXIT(format, args...) \
+ do {if (caif_dbg_level > CFLOG_LEVEL_FUNC) {\
+ printk("KERN_INFO <%s:%d, EXIT> " format "\n",\
+ __func__, __LINE__ , ## args); } } while (0)
+#define CFLOG_EXIT(format) _CFLOG_EXIT format
+
+#else
+
+#define CFLOG_WARN(args)
+#define CFLOG_TRACE(args)
+#define CFLOG_TRACE2(args)
+#define CFLOG_TRACE3(args)
+#define CFLOG_ENTER(args)
+#define CFLOG_EXIT(args)
+
+#endif
+
+
+
+
+
+/* Critical Section support, one thread only between startsync
+ * and endsync */
+#define cfglu_lock_t spinlock_t
+#define cfglu_init_lock(sync) spin_lock_init(&(sync))
+#define cfglu_lock(sync) spin_lock(&(sync))
+#define cfglu_unlock(sync) spin_unlock(&(sync))
+#define cfglu_deinit_lock(sync)
+
+/* Read/Write lock, allows multiple readers, one writer */
+#define cfglu_rwlock_t rwlock_t
+#define cfglu_init_rwlock(rwlock) rwlock_init(&(rwlock))
+#define cfglu_deinit_rwlock(rwlock)
+#define cfglu_read_lock(rwlock) read_lock(&rwlock)
+#define cfglu_read_unlock(rwlock) read_unlock(&rwlock)
+#define cfglu_write_lock(rwlock) write_lock(&rwlock)
+#define cfglu_write_unlock(rwlock) write_unlock(&rwlock)
+
+
+
+/* Atomic counting */
+#define cfglu_atomic_t atomic_t
+#define cfglu_atomic_read(a) atomic_read(&a)
+#define cfglu_atomic_set(a, val) atomic_set(&a, val)
+#define cfglu_atomic_inc(a) atomic_inc(&a)
+#define cfglu_atomic_dec(a) atomic_dec(&a)
+
+/* HEAP */
+#define cfglu_alloc(size) kmalloc(size, GFP_KERNEL)
+#define cfglu_free(ptr) kfree(ptr)
+
+/* ASSERT */
+#define cfglu_assert(exp) BUG_ON(!(exp))
+
+
+#define cfglu_container_of(p, t, m) container_of(p, t, m)
+
+/*FIXME: Comment error codes*/
+enum cfglu_errno {
+ CFGLU_EOK = 0,
+ CFGLU_EPKT = -EPROTO,
+ CFGLU_EADDRINUSE = -EADDRINUSE,
+ CFGLU_EIO = -EIO,
+ CFGLU_EFCS = -EILSEQ,
+ CFGLU_EBADPARAM = -EINVAL,
+ CFGLU_EINVAL = -EINVAL,
+ CFGLU_ENODEV = -ENODEV,
+ CFGLU_ENOTCONN = -ENOTCONN,
+ CFGLU_EPROTO = -EPROTO,
+ CFGLU_EOVERFLOW = -EOVERFLOW,
+ CFGLU_ENOMEM = -ENOMEM,
+ CFGLU_ERETRY = -EAGAIN,
+ CFGLU_ENOSPC = -ENOSPC,
+ CFGLU_ENXIO = -ENXIO
+
+};
+
+#endif /* CFGLU_H_ */
diff --git a/include/net/caif/generic/cfloopcfg.h b/include/net/caif/generic/cfloopcfg.h
new file mode 100644
index 0000000..ab982cb
--- /dev/null
+++ b/include/net/caif/generic/cfloopcfg.h
@@ -0,0 +1,28 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+
+
+
+
+
+
+#ifndef CFLOOPCFG_H_
+#define CFLOOPCFG_H_
+#include "caif_layer.h"
+struct _cfloopcfg_t {
+ layer_t *loop;
+};
+typedef struct _cfloopcfg_t cfloopcfg_t;
+cfloopcfg_t *cfloopcfg_create(void);
+void cfloopcfg_add_phy_layer(cfloopcfg_t *cnfg,
+ cfcnfg_phy_type_t phy_type,
+ layer_t *phy_layer);
+
+#endif /* CFLOOPCFG_H_ */
diff --git a/include/net/caif/generic/cflst.h b/include/net/caif/generic/cflst.h
new file mode 100644
index 0000000..25a7cb3
--- /dev/null
+++ b/include/net/caif/generic/cflst.h
@@ -0,0 +1,27 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+
+
+
+
+
+
+#ifndef CFLST_H_
+#define CFLST_H_
+
+#include "cfglue.h"
+bool cflst_put(layer_t **lst, uint8 id, layer_t *node);
+layer_t *cflst_get(layer_t **lst, uint8 id);
+layer_t *cflst_del(layer_t **lst, uint8 id);
+#define CFLST_FIRST(lst) lst
+#define CFLST_MORE(node) ((node) != NULL)
+#define CFLST_NEXT(node) ((node)->next)
+void cflst_init(layer_t **lst);
+#endif /* CFLST_H_ */
diff --git a/include/net/caif/generic/cfmsll.h b/include/net/caif/generic/cfmsll.h
new file mode 100644
index 0000000..f356b87
--- /dev/null
+++ b/include/net/caif/generic/cfmsll.h
@@ -0,0 +1,22 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+
+
+
+
+
+
+#ifndef CFMSLL_H_
+#define CFMSLL_H_
+#include "caif_layer.h"
+#include "cfglue.h"
+layer_t *cfmsll_create(int type, int instance);
+
+#endif /* CFMSLL_H_ */
diff --git a/include/net/caif/generic/cfmuxl.h b/include/net/caif/generic/cfmuxl.h
new file mode 100644
index 0000000..79c3974
--- /dev/null
+++ b/include/net/caif/generic/cfmuxl.h
@@ -0,0 +1,30 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+
+
+
+
+
+
+#ifndef CFMUXL_H_
+#define CFMUXL_H_
+#include "caif_layer.h"
+struct _cfsrvl_t;
+struct _cffrml_t;
+
+layer_t *cfmuxl_create(void);
+bool cfmuxl_set_uplayer(layer_t *layr, layer_t *up, uint8 linkid);
+layer_t *cfmuxl_remove_dnlayer(layer_t *layr, uint8 phyid);
+bool cfmuxl_set_dnlayer(layer_t *layr, layer_t *up, uint8 phyid);
+layer_t *cfmuxl_remove_uplayer(layer_t *layr, uint8 linkid);
+bool cfmuxl_is_phy_inuse(layer_t *layr, uint8 phyid);
+uint8 cfmuxl_get_phyid(layer_t *layr, uint8 channel_id);
+
+#endif /* CFMUXL_H_ */
diff --git a/include/net/caif/generic/cfpkt.h b/include/net/caif/generic/cfpkt.h
new file mode 100644
index 0000000..0bdfbe7
--- /dev/null
+++ b/include/net/caif/generic/cfpkt.h
@@ -0,0 +1,246 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+
+
+
+
+
+
+#ifndef CFPKT_H_
+#define CFPKT_H_
+#include "caif_layer.h"
+
+
+struct _cfpkt_t;
+/*! \addtogroup GenCaifGlue
+ * Additional documentation for group `GenCaifGlue'
+ * @{
+ */
+
+/** Checksum iteration function used to iterate buffers
+ * (we may have packets consisting of a chain of buffers)
+ * @param chs Checksum calculated so far.
+ * @param buf pointer to the buffer to checksum
+ * @param len length of buf.
+ * @return checksum of buffer
+ */
+typedef uint16(*iterfunc_t)(uint16 chks, void *buf, uint16 len);
+
+caif_packet_funcs_t caif_get_packet_funcs(void);
+/** Create a Caif packet.
+ * @param len Length of packet to be created
+ * @returns new packet.
+ */
+cfpkt_t *cfpkt_create(uint16 len);
+/**
+ * Destroy a CAIF Packet.
+ * @param pkt Packet to be destoyed.
+ */
+void cfpkt_destroy(cfpkt_t *pkt);
+
+/**
+ * Extract header from packet.
+ * \image html ExtractCaifPacketHeader.jpg "Extract Caif Packet Header"
+ *
+ * @param pkt Packet to extract header data from.
+ * @param data pointer to copy the header data into.
+ * @param len length of head data to copy.
+ * @return \ref true on success \ref false on failure
+ */
+bool cfpkt_extr_head(cfpkt_t *pkt, void *data, uint16 len);
+
+/**
+ * Peek header from packet.
+ * Reads data from packet without changing packet.
+ *
+ * @param pkt Packet to extract header data from.
+ * @param data pointer to copy the header data into.
+ * @param len length of head data to copy.
+ * @return \ref true on success \ref false on failure
+ */
+bool cfpkt_peek_head(cfpkt_t *pkt, void *data, uint16 len);
+
+/**
+ * Extract header from trailer (end of packet).
+ * \image html ExtractCaifPacketTrailer.jpg "Extract Caif Packet Trailer"
+ *
+ * @param pkt Packet to extract header data from.
+ * @param data pointer to copy the trailer data into.
+ * @param len length of head data to copy.
+ * @return \ref true on success \ref false on failure
+ */
+bool cfpkt_extr_trail(cfpkt_t *pkt, void *data, uint16 len);
+/**
+ * Add header to packet.
+ *
+ *
+ * @param pkt Packet to add header data to.
+ * @param data pointer to copy into the header.
+ * @param len length of head data to copy.
+ * @return \ref true on success \ref false on failure
+ */
+bool cfpkt_add_head(cfpkt_t *pkt, const void *data, uint16 len);
+
+/**
+ * Add trailer to packet.
+ *
+ *
+ * @param pkt Packet to add trailer data from.
+ * @param data pointer to copy into the trailer.
+ * @param len length of head data to copy.
+ * @return \ref true on success \ref false on failure
+ */
+bool cfpkt_add_trail(cfpkt_t *pkt, const void *data, uint16 len);
+
+/**
+ * Pad trailer on packet.
+ * Moves data pointer in packet, no content copied.
+ *
+ * @param pkt Packet to add trailer data from.
+ * @param data pointer to copy into the trailer.
+ * @param len length of head data to copy.
+ * @return \ref true on success \ref false on failure
+ */
+bool cfpkt_pad_trail(cfpkt_t *pkt, uint16 len);
+
+/**
+ * Add a single byte to packet body (tail).
+ */
+
+bool cfpkt_addbdy(cfpkt_t *pkt, const uint8 data);
+
+/**
+ * Add a data to packet body (tail).
+ */
+bool cfpkt_add_body(cfpkt_t *pkt, const void *data, uint16 len);
+
+/**
+ * Checks if there is more data to process in packet.
+ * @param pkt Packet to check.
+ * @return \ref true on if more data is available in packet \ref false if no more data can be extracted
+ */
+bool cfpkt_more(cfpkt_t *pkt);
+/**
+ * Checks if the packet is erroneous, i.e. if it has been attempted to extract more data than available in packet
+ * or writing more data than has been allocated in \ref cfpkt_create().
+ * @param pkt Packet to check.
+ * @return \ref true on error \ref false otherwise
+ */
+bool cfpkt_erroneous(cfpkt_t *pkt);
+
+/**
+ * Get the packet length.
+ * @param pkt Packet to get lenght from.
+ * @return number of bytes in packet.
+ */
+uint16 cfpkt_getlen(cfpkt_t *pkt);
+
+/**
+ * Set the packet length, by adjusting the tailer pointer according to length.
+ * @param pkt Packet to set lenght.
+ * @param len Packet length.
+ * @return number of bytes in packet.
+ */
+int cfpkt_setlen(cfpkt_t *pkt, uint16 len);
+
+/**
+ * Appends a packet's data to another packet.
+ * NB: Input packets will be destroyed after appending and cannot be used
+ * after calling this function.
+ * @param dstpkt Packet to append data into, WILL BE FREED BY THIS FUNCTION
+ * @param addpkt Packet to be appended and automatically released, WILL BE FREED BY THIS FUNCTION.
+ * @param expectlen Packet's expected total length, this should be considered a hint.
+ * @returns the new appended packet.
+ */
+cfpkt_t *cfpkt_append(cfpkt_t *dstpkt, cfpkt_t *addpkt,
+ uint16 expectlen);
+/**
+ * Split a packet into two packet at the specified split point.
+ * @param pkt Packet to be split
+ * @param pos Position to split packet in two part.
+ */
+cfpkt_t *cfpkt_split(cfpkt_t *pkt, uint16 pos);
+
+/** Iteration function, iterates the packet buffers from start to end*/
+uint16 cfpkt_iterate(cfpkt_t *pkt, iterfunc_t func, uint16 data);
+
+void
+cfpkt_extract(cfpkt_t *cfpkt, void *buf, unsigned int buflen,
+ unsigned int *actual_len);
+
+/** Append by giving user access to packet buffer
+ * @param pkt Packet to append to
+ * @param buf Buffer inside pkt that user shall copy data into
+ * @param buflen Length of buffer and number of bytes added to packet
+ * @return 0 on error, 1 on success
+ */
+
+int cfpkt_raw_append(cfpkt_t *cfpkt, void **buf, unsigned int buflen);
+
+/** Extract by giving user access to packet buffer
+ * @param pkt Packet to extract from
+ * @param buf Buffer inside pkt that user shall copy data from
+ * @param buflen Length of buffer and number of bytes removed from packet
+ * @return 0 on error, 1 on success
+ */
+int cfpkt_raw_extract(cfpkt_t *cfpkt, void **buf, unsigned int buflen);
+
+
+
+/** Used map from a "native" packet e.g. Linux Socket Buffer to a CAIF packet.
+ * @param dir - Direction telling if this is an packet to be sent or received.
+ * @param nativepkt - The native packet to be transformed to a CAIF packet
+ * @returns the mapped CAIF Packet CFPKT.
+ */
+cfpkt_t *cfpkt_fromnative(caif_direction_t dir, void *nativepkt);
+
+/** Used map from a CAIF packet to a "native" packet e.g. Linux Socket Buffer.
+ * @param pkt - The CAIF packet to be transformed to a "native" packet.
+ * @returns The native packet transformed from a CAIF packet.
+ */
+void *cfpkt_tonative(cfpkt_t *pkt);
+
+
+caif_packet_funcs_t cfpkt_get_packet_funcs(void);
+
+/**
+ * Insert a packet in the packet queue.
+ * @param pkt Packet to be inserted in queue
+ * @param pktq Packet queue to insert into
+ * @param prio Priority of packet
+ */
+void cfpkt_queue(cfpktq_t *pktq, cfpkt_t *pkt, unsigned short prio);
+
+/**
+ * Remove a packet from the packet queue.
+ * @param pktq Packet queue to fetch packets from.
+ * @returns dequeued packet.
+ */
+cfpkt_t *cfpkt_dequeue(cfpktq_t *pktq);
+
+/**
+ * Peek into a packet from the packet queue.
+ * @param pktq Packet queue to fetch packets from.
+ * @returns peek'ed packet.
+ */
+cfpkt_t *cfpkt_qpeek(cfpktq_t *pktq);
+
+/**
+ * Initiates the packet queue.
+ * @param pktq Packet queue to fetch packets from.
+ */
+cfpktq_t *cfpkt_queuecreate(void);
+
+/** Put content of packet into buffer for debuging purposes */
+char *cfpkt_log_pkt(cfpkt_t *pkt, char *buf, int buflen);
+
+
+/*! @} */
+#endif /* CFPKT_H_ */
diff --git a/include/net/caif/generic/cfserl.h b/include/net/caif/generic/cfserl.h
new file mode 100644
index 0000000..2ed198d
--- /dev/null
+++ b/include/net/caif/generic/cfserl.h
@@ -0,0 +1,22 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+
+
+
+
+
+
+#ifndef CFSERL_H_
+#define CFSERL_H_
+#include "caif_layer.h"
+#include "cfglue.h"
+layer_t *cfserl_create(int type, int instance, bool use_stx);
+
+#endif /* CFSERL_H_ */
diff --git a/include/net/caif/generic/cfshml.h b/include/net/caif/generic/cfshml.h
new file mode 100644
index 0000000..d7e0247
--- /dev/null
+++ b/include/net/caif/generic/cfshml.h
@@ -0,0 +1,21 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+
+
+
+
+
+
+#ifndef CFSHML_H_
+#define CFSHML_H_
+
+layer_t *cfshml_create(int type, int instance);
+
+#endif /* CFSHML_H_ */
diff --git a/include/net/caif/generic/cfspil.h b/include/net/caif/generic/cfspil.h
new file mode 100644
index 0000000..062d67c
--- /dev/null
+++ b/include/net/caif/generic/cfspil.h
@@ -0,0 +1,80 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+
+
+
+
+
+
+#ifndef CFSPIL_H_
+#define CFSPIL_H_
+#include "cfpkt.h"
+#define CAIF_MAX_SPI_FRAME 4096
+
+/* Forward declaration */
+struct _cfspil_t;
+
+typedef struct _cfspil_t cfspil_t;
+
+/** @page SPI PHY Layer description.
+ *
+ * SPI Physical layer is not implemented in GenCaif. The SPI PHY Layer
+ * is HW dependent. But the CFSPIL (Caif SPI Layer) provides support for
+ * implementing the SPI Layer Protocol.
+ *
+ * SPI PHY uses a different paradigm for transmit than the rest of GenCaif.
+ * SPI PHY is pulling packets from CFSPIL. The SPI-PHY get a notification
+ * about a transfer, and then request transfer length, and data to transfer
+ * in the following way:
+ * \image html caif-spi.jpg "CAIF SPI Flow"
+ *
+ * -# Wait for transmit request (packet will be \b null pointer) indicating
+ * GenCaif want something to be sent to the modem.
+ * -# Request the transfer length by using function \ref cfspil_xmitlen,
+ * -# Add Caif SPI Command to SPI transfer.
+ * -# When SPI is ready for transfer, call \ref cfspil_getxmitpkt to get
+ * the transfer packet.
+ * -# Request new transfer length ( \ref cfspil_xmitlen) unless zero
+ * length is returned.
+ * -# Wait for next transfer request.
+ *
+ *
+ * * CFSPIL Specification:
+ * \see { GenCaifSPI }
+ *
+ */
+/*! \addtogroup GenCaifSPI
+ * Additional documentation for group `GenCaifSPI'
+ * @{
+ */
+
+/** SPI-Layer
+ * Create and initializes SPI layer.
+ */
+layer_t *cfspil_create(int type, int instance);
+
+/**
+ * Check the length of the next SPI frame to send.
+ * @param layr Pointer to SPI layer
+ * @return Length of next SPI transfer, 0 if nothink to send.
+ */
+int cfspil_xmitlen(cfspil_t *layr);
+
+/**
+ * Get the next CAIF SPI frame to send. This packet is guaranteed to have equal size to the
+ * length given in \ref cfspil_getxmitpkt.
+ * @param layr Pointer to SPI layer
+ * @return The CAIF Packet to be sent.
+ */
+cfpkt_t *cfspil_getxmitpkt(cfspil_t *layr);
+
+/*! @} */
+
+#endif /* CFSPIL_H_ */
diff --git a/include/net/caif/generic/cfsrvl.h b/include/net/caif/generic/cfsrvl.h
new file mode 100644
index 0000000..0286644
--- /dev/null
+++ b/include/net/caif/generic/cfsrvl.h
@@ -0,0 +1,48 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+
+
+
+
+
+
+#ifndef CFSRVL_H_
+#define CFSRVL_H_
+#include "cflst.h"
+#include "cfglue.h"
+#include <stddef.h>
+
+
+
+typedef struct _cfsrvl_t {
+ layer_t layer;
+ /** Physical ID of the logical physical connection */
+ uint8 phid;
+ bool open;
+ bool phy_flow_on;
+ bool modem_flow_on;
+} cfsrvl_t;
+
+layer_t *cfvei_create(uint8 linkid, uint8 phyid);
+layer_t *cfdgml_create(uint8 linkid, uint8 phyid);
+
+layer_t *cfutill_create(uint8 linkid, uint8 phyid);
+layer_t *cfvidl_create(uint8 linkid, uint8 phyid);
+layer_t *cfrfml_create(uint8 linkid, uint8 phyid);
+bool cfsrvl_phyid_match(layer_t *layer, int phyid);
+void cfservl_destroy(layer_t *layer);
+
+
+void cfsrvl_init(cfsrvl_t *service, uint8 channel_id, uint8 phyid);
+bool cfsrvl_ready(cfsrvl_t *service, int *err);
+uint8 cfsrvl_getphyid(layer_t *layer);
+
+
+#endif /* CFSRVL_H_ */
diff --git a/include/net/caif/generic/fcs.h b/include/net/caif/generic/fcs.h
new file mode 100644
index 0000000..0b82e2d
--- /dev/null
+++ b/include/net/caif/generic/fcs.h
@@ -0,0 +1,22 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+
+
+
+
+
+
+#ifndef FCS_H_
+#define FCS_H_
+
+uint16
+fcs16(uint16 fcs, uint8 *cp, uint16 len);
+
+#endif /* FCS_H_ */
--
1.6.0.4
^ permalink raw reply related
* [PATCH 1/8] [RFC] CAIF Protocol Stack
From: sjur.brandeland @ 2009-09-23 17:30 UTC (permalink / raw)
To: netdev; +Cc: Kim.xx.Lilliestierna, sjur.brandeland
From: Kim Lilliestierna <Kim.xx.Lilliestierna@ericsson.com>
Signed-off-by: sjur.brandeland@stericsson.com
---
include/linux/caif/caif_config.h | 203 ++++++++++++++++++++++++++++++++++++++
include/linux/caif/caif_ioctl.h | 114 +++++++++++++++++++++
2 files changed, 317 insertions(+), 0 deletions(-)
create mode 100644 include/linux/caif/caif_config.h
create mode 100644 include/linux/caif/caif_ioctl.h
diff --git a/include/linux/caif/caif_config.h b/include/linux/caif/caif_config.h
new file mode 100644
index 0000000..6ea934b
--- /dev/null
+++ b/include/linux/caif/caif_config.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2009
+ *
+ * CAIF Channel Configuration definitions.
+ *
+ * Author: Sjur Brendeland/ sjur.brandeland@stericsson.com
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ *
+ */
+
+#ifndef CAIF_CONFIG_H_
+#define CAIF_CONFIG_H_
+
+/**
+ * enum caif_phy_preference - Types of Physical HW Interfaces
+ * towards modem defined in CAIF Stack,
+ * @CAIF_PHYPREF_UNSPECIFIED: Default Physical Interface
+ * @CAIF_PHYPREF_LOW_LAT: Default Physical Interface for Low Latency
+ * Traffic
+ * @CAIF_PHYPREF_HIGH_BW: Default Physical Interface for High Bandwidth
+ * Traffic
+ * @_CAIF_PHYPREF_LOOP: TEST Loop-back Interface Simulating Acc side
+ * responses
+ * @_CAIF_PHYPREF_RAW_LOOP: TEST ONLY Raw loopback interface
+ *
+ * For Client convenience to special types are defined:
+ * CAIF_PHYPREF_LOW_LAT is the preferred low latency physical link.
+ * Typically used for "control" purposes.
+ * CAIF_PHYPREF_HIGH_BW is the preferred high bandwidth physical link.
+ * Typically used for "payload" purposes.
+ *
+ */
+enum caif_phy_preference {
+ CAIF_PHYPREF_UNSPECIFIED = 0x00,
+ CAIF_PHYPREF_LOW_LAT = 0xd0,
+ CAIF_PHYPREF_HIGH_BW = 0xe0,
+ _CAIF_PHYPREF_LOOP = 0x70,
+ _CAIF_PHYPREF_RAW_LOOP = 0x80
+};
+
+
+/*!
+ * define CAIF Channel Priority.
+ * Used when setting up a Channel to specify the
+ * priority level of the channel.
+ */
+
+#define CAIF_PRIO_UNSPCEIFIED 0x0
+#define CAIF_PRIO_MIN 0x01 /*! Minimum Priority Level */
+#define CAIF_PRIO_LOW 0x04 /*!< Suggested Priority Level for
+ * Low Priority Channel */
+#define CAIF_PRIO_NORMAL 0x0f /*!< Suggested Normal/Default
+ * priority Level */
+#define CAIF_PRIO_HIGH 0x14 /*!< Suggested High Priority Level */
+#define CAIF_PRIO_MAX 0x1F /*!< Max Priority for Channel
+ (do not use)
+ */
+
+/**
+ * enum caif_channel_type Types of CAIF Channel type defined in CAIF Stack.
+ * @CAIF_CHTY_AT: Classical AT
+ * @CAIF_CHTY_AT_CTRL: AT Control only
+ * @CAIF_CHTY_AT_PAIRED: Paired control and data
+ * @CAIF_CHTY_DATAGRAM: Datagram, Requires: connection_id
+ * @CAIF_CHTY_DATAGRAM_LOOP: Datagram Loopback (testing purposes only)
+ * @CAIF_CHTY_VIDEO: Video Channel
+ * @CAIF_CHTY_DEBUG: Debug Service (Debug Server and
+ * Interactive Debug)
+ * @CAIF_CHTY_DEBUG_TRACE: Debug Server only
+ * @CAIF_CHTY_DEBUG_INTERACT: Debug Interactive
+ * @CAIF_CHTY_RFM: RFM Service. Params: connection_id, volume
+ * @CAIF_CHTY_UTILITY: Utility (Psock) Service.
+ * Params: fifo_kb,fifo_pkt, name, psock_param
+ * @CAIF_CHTY_RAW: DO NOT USE. This is for testing only
+ *
+ * This is used for Channel Configuration specifying the type of channel.
+ */
+
+enum caif_channel_type {
+ CAIF_CHTY_AT,
+ CAIF_CHTY_AT_CTRL,
+ CAIF_CHTY_AT_PAIRED,
+ CAIF_CHTY_DATAGRAM,
+ CAIF_CHTY_DATAGRAM_LOOP,
+ CAIF_CHTY_VIDEO,
+ CAIF_CHTY_DEBUG,
+ CAIF_CHTY_DEBUG_TRACE,
+ CAIF_CHTY_DEBUG_INTERACT,
+ CAIF_CHTY_RFM,
+ CAIF_CHTY_UTILITY,
+ CAIF_CHTY_RAW
+};
+
+/**
+ *struct caif_channel_config This structures is used for configuring
+ * CAIF Channels.
+ * @name: Mandatory: Nickname for this device
+ * @type: Mandatory Define the type of caif service
+ * @priority: Mandatory Value between CAIF_PRIO_MIN and
+ * CAIF_PRIO_MAX,
+ * CAIF_PRIO_LOW, CAIF_PRIO_NORMAL, CAIF_PRIO_HIGH
+ * are suggested values.
+ * @phy_pref: Either: Specify type of physical interface to use.
+ * @phy_name: Or: Specify identity of the physical interface.
+ *
+ * @u: Union of Channel Type Specific configuration
+ * parameters
+ *
+ * @u.dgm: CAIF_CHTYPE_DATAGRAM
+ * @u.dgm.connection_id: Mandatory Connection ID must be specified.
+ *
+ * @u.video: CAIF_CHTYPE_VIDEO
+ * @u.video.connection_id: Mandatory Connection ID must be specified.
+ *
+ * @u.rfm CAIF_CHTYPE_RFM
+ * @u.rfm.connection_id: Mandatory Connection ID must be specified.
+ * @u.rfm.volume: Mandatory Volume to mount.
+ *
+ * @u.utility: CAIF_CHTYPE_UTILITY
+ * @u.utility.fifosize_kb: Psock: FIFO size in KB
+ * @u.utility.fifosize_bufs: Psock: # signal buffers
+ * @u.utility.name: Psock: Name of service
+ * @u.utility.params: Psock: Channel Config Parameters
+ * @u.utility.paramlen: Psock: Length of Channel Config Parameters
+ *
+ *
+ * It holds configuration parameters for setting up all devined CAIF
+ * Channel types.
+ * The four first fields are mandatory, then Physical Device can be specified
+ * either by name
+ * or by prefered characteristics.
+ * The rest of the configuration fields are hold in a union for each
+ * channel type and are channel type specific.
+ * \b Documentation see STE Doc No: 155 19-CRH 109 913.
+ */
+
+struct caif_channel_config {
+ /* Mandatory: Nickname for this device */
+ char name[16];
+ /* Mandatory: Define the type of caif service */
+ enum caif_channel_type type;
+ /** Mandatory: Mandatory - Value between
+ * CAIF_PRIO_MIN and CAIF_PRIO_MAX, CAIF_PRIO_LOW, CAIF_PRIO_NORMAL,
+ * CAIF_PRIO_HIGH are suggested values. */
+ unsigned priority;
+
+ /** Either: Specify type of physical interface to use. */
+ enum caif_phy_preference phy_pref;
+ /** Or: Specify identity of the physical interface. */
+ char phy_name[16];
+
+ /** Union of Channel Type Specific configuration parameters
+ * 'switched' by attribute type */
+ union {
+ /* CAIF_CHTYPE_DATAGRAM */
+ struct {
+ /** Mandatory Connection ID must be specified. */
+ unsigned connection_id;
+ } dgm;
+ /* CAIF_CHTYPE_VIDEO */
+ struct {
+ /** Datagram: Mandatory Connection ID Must be
+ * specified. */
+ unsigned connection_id;
+ } video;
+ /* CAIF_CHTYPE_RFM */
+ struct {
+ /** RFM: Mandatory Connection ID. */
+ unsigned connection_id;
+ /** RFM: Mandatory Volume to mount. */
+ char volume[20];
+ } rfm;
+ /* CAIF_CHTYPE_UTILITY */
+ struct {
+/** Psock: FIFO size in KB */
+ unsigned fifosize_kb;
+/** Psock: # signal buffers */
+ unsigned fifosize_bufs;
+/** Psock: Name of service */
+ char name[16];
+/** Psock: Channel Config Parameters> */
+ unsigned char params[256];
+/** Psock: Length of Channel Config Parameters */
+ int paramlen;
+ } utility;
+
+
+ /* Raw Data configuration: DO NOT USE,
+ * applies for testing only */
+
+ struct raw {
+ unsigned channeltype;
+ unsigned char endpoint:2;
+ unsigned char subtype:2;
+ unsigned char serviceconfig[512];
+ unsigned int service_length;
+ } _raw;
+ } u;
+
+};
+
+#endif /* CAIF_CONFIG_H_ */
diff --git a/include/linux/caif/caif_ioctl.h b/include/linux/caif/caif_ioctl.h
new file mode 100644
index 0000000..4fdd648
--- /dev/null
+++ b/include/linux/caif/caif_ioctl.h
@@ -0,0 +1,114 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Daniel Martensson / Daniel.Martensson@stericsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+
+
+
+
+
+#ifndef CAIF_IOCTL_H_
+#define CAIF_IOCTL_H_
+#include "caif_config.h"
+
+
+
+/*!\page caif_ioctl.h
+ * This file defines the management interface to CAIF.
+ * It defines how CAIF Channels are configured and become visible in Linux
+ * file system under "/dev/caifconfig".
+ *
+ *\b Example - creating a new AT character device:
+ * \code
+ fd = open("/dev/caifconfig",..);
+ struct caif_channel_create_action at_config = {
+ .name = "cnhl2",
+ .config = {
+ .channel = CAIF_CHTY_AT,
+ .phy_ref = CAIF_PHY_LOW_LAT,
+ .priority = CAIF_PRIO_HIGH
+ }};
+ ioctl(fd, CAIF_IOC_CONFIG_DEVICE,&at_config);
+ close(fd);
+ * \endcode
+ * This will cause a new AT channel to be available in at "/dev/chnl2".
+ * This CAIF channel can then be connected by using \ref open.
+ *
+*/
+
+/*! \addtogroup caif_ioctl
+ * Additional documentation for group `caif_config.h'
+ * @{
+ */
+
+
+
+/* Use 'g' as magic number. 'g' is the first free letter in
+ * Documentation/ioctl-number.txt*/
+#define CAIF_IOC_MAGIC 'g'
+#define DEVICE_NAME_LEN 16
+
+/* Specifies the type of device to create NET device or CHAR device*/
+enum caif_dev_type {
+ CAIF_DEV_CHR = 1,
+ CAIF_DEV_NET = 2
+};
+
+
+/** Used for identifying devices, PHY interfaces etc*/
+struct caif_device_name {
+ char name[DEVICE_NAME_LEN]; /*!< Device name */
+ enum caif_dev_type devtype; /*!< Device type */
+};
+
+
+/**
+ * CAIF ACTION for \ref CAIF_ACT_CHANNEL_CONFIG.
+ * This structure is used to configure a new CAIF Channel and
+ * create the corresponding character device.
+ */
+struct caif_channel_create_action {
+ /** \b in CAIF Configuration Request */
+ struct caif_channel_config config;
+ /** \b in/out Device name returned from ACTION */
+ struct caif_device_name name;
+ /** \b out Major device id */
+ int major;
+ /** \b out Minor device id */
+ int minor;
+};
+
+/**
+ * union caif_action
+ * This union is used to configure a new CAIF Channel and
+ */
+
+union caif_action {
+ struct caif_device_name delete_channel;
+ struct caif_channel_create_action create_channel;
+};
+
+
+/**
+ * CAIF IOCTL for \ref CAIF_IOC_CHANNEL_CONFIG.
+ * This structure is used to configure a new CAIF Channel and
+ * create the corresponding character device.
+ */
+
+/** Create and Configure a new CAIF device.
+ * Note that the device is not implicitly connected. */
+#define CAIF_IOC_CONFIG_DEVICE _IOWR(CAIF_IOC_MAGIC, 1,\
+struct caif_channel_create_action)
+
+/** Remove a CAIF device. Requires the device to be previously disconnected. */
+#define CAIF_IOC_REMOVE_DEVICE _IOWR(CAIF_IOC_MAGIC, 2,\
+ struct caif_device_name)
+#define CAIF__IOC_MAXNR 9
+/*! @} */
+
+#endif /* CAIF_IOCTL_H_ */
--
1.6.0.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox