* Re: [PATCH V2 net-next 14/15] drivers/net/typhoon.c: Use (pr|netdev)_<level> macro helpers
From: David Miller @ 2010-02-22 23:43 UTC (permalink / raw)
To: dave; +Cc: joe, netdev
In-Reply-To: <1266808995.2930.24.camel@obelisk.thedillows.org>
From: David Dillow <dave@thedillows.org>
Date: Sun, 21 Feb 2010 22:23:15 -0500
> On Sun, 2010-02-21 at 19:08 -0800, Joe Perches wrote:
>> David Dillow took my suggestions and improved on them.
>> Here is this latest version.
>>
>> Add #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>> Remove #define PFX
>> Remove #define ERR_PFX
>> Remove now unused member name from struct typhoon
>> Use pr_<level>
>> Use netdev_<level>
>> Coalesce long formats
>> Remove version information
>>
>> Signed-off-by: Joe Perches <joe@perches.com>
>> Signed-off-by: David Dillow <dave@thedillows.org>
>
> Perhaps commit text as follows?
Man you are picky :-/ Are you a control freak?
You can't even let Joe write his commit comments consistently with the
ones he is using for all the other similar network driver transmations?
What makes doing the same exact transformations to your favorite
driver so special that it deserves a specially constructed commit
message?
Tell me.
Stuff like this drives me crazy.
^ permalink raw reply
* Re: [PATCH 1/1] ARC vmac ethernet driver.
From: David Miller @ 2010-02-22 23:40 UTC (permalink / raw)
To: andreas.fenkart; +Cc: netdev
In-Reply-To: <1266798092-25938-1-git-send-email-andreas.fenkart@streamunlimited.com>
From: Andreas Fenkart <andreas.fenkart@streamunlimited.com>
Date: Mon, 22 Feb 2010 01:21:32 +0100
> drivers/net/arcvmac.h | 122 ++++
> drivers/net/arcvmac_fifo.h | 109 ++++
There is no reason to create a completely seperate header file just
for one DMA fifo data structure and a bunch of helper inline
functions.
Put this into arcvmac.h, thanks.
^ permalink raw reply
* Re: [PATCH 1/2] can:ti_hecc: Add pm hook-up
From: David Miller @ 2010-02-22 23:33 UTC (permalink / raw)
To: wg-5Yr1BZd7O62+XT7JhA+gdA
Cc: srk-l0cyMroinI0, socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
krbaalaaji-l0cyMroinI0, netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <4B828E3C.40203-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>
From: Wolfgang Grandegger <wg-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>
Date: Mon, 22 Feb 2010 15:01:32 +0100
> What is the reason for moving around module_init? Please revert. Then
> you can add my "Acked-by: Wolfgang Grandegger <wg-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>".
I don't think this is enough to warrant rejection of this patch.
^ permalink raw reply
* Re: RFC: netfilter: nf_conntrack: add support for "conntrack zones"
From: Eric W. Biederman @ 2010-02-22 23:17 UTC (permalink / raw)
To: hadi
Cc: Patrick McHardy, Linux Netdev List, containers,
Netfilter Development Mailinglist, Ben Greear
In-Reply-To: <1266875729.3673.12.camel@bigi>
jamal <hadi@cyberus.ca> writes:
> On Mon, 2010-02-22 at 12:46 -0800, Eric W. Biederman wrote:
>> jamal <hadi@cyberus.ca> writes:
>
>>
>> This is one of the long standing issues that we have always known
>> we needed to solve, but have not taken the time to do it. Now that
>> the need is more real it looks about time to solve this one.
>>
>> There are currently two problems.
>> 1) A process is needed to hold a reference to the network namespace.
>> 2) We use pids which are an awkward way of talking about network
>> namespaces.
>>
>> The solution I have been playing with involves.
>> - Using a file descriptor to refer to a network namespace.
>> - Using a trivial virtual filesystem to persistently hold onto
>> a namespace without the need of a process.
>> - Have a convention of mounting the fs at something like
>> /var/run/netns/<name>
>>
>
> I didnt quiet follow how i could use the above to do:
> "ip ns <name/id> route add blah" from namespace0.
>
> I tend to think in packets and wires instead of files;
> How about just allowing a "control" channel from which
> i could discover the namespace?
> Example, assuming i have the right permissions:
> 1) listen to async events example on a multicast bus when
> a namespace is created or destroyed. Provide me a little more info on
> the created namespace such as its pid, name(?), types of namespace, etc
> 2) send a query to dump existing namespace or query by name, id etc.
> I get the same details as above.
>
> using genetlink should provide you with sufficient ability to do this.
What I am thinking is:
"ip ns <name> route add blah" is:
fd = open("/var/run/netns/<name>");
sys_setns(fd); /* Like unshare but takes an existing namespace */
/* Then the rest of the existing ip command */
"ip ns list" is:
dfd = open("/var/run/netns", O_DIRECTORY);
getdents(dfd, buf, count);
"ip ns new <name>" is:
unshare(CLONE_NEWNS);
fd = nsfd(NETNS);
mkdir("/var/run/netns/<name>");
mount("none", "/var/run/netns/<name>", "ns", 0, fd);
Using unix domain names means that which namespaces you see is under
control of userspace. Which allows for nested containers (something I
use today), and ultimately container migration.
Using genetlink userspace doesn't result in a nestable implementation
unless I introduce yet another namespace, ugh.
Eric
^ permalink raw reply
* [PATCH 4/4] isdn: remove ishexdigit() in regard to isxdigit()
From: Tilman Schmidt @ 2010-02-22 23:10 UTC (permalink / raw)
To: Karsten Keil, David Miller
Cc: Hansjoerg Lipp, Karsten Keil, isdn4linux, i4ldeveloper, netdev,
linux-kernel
In-Reply-To: <20100221-patch-gigaset-00.tilman@imap.cc>
From: Andy Shevchenko <ext-andriy.shevchenko@nokia.com>
Subject: [PATCH 4/4] isdn: remove ishexdigit() in regard to isxdigit()
Samll cleanup in drivers/isdn/gigaset/capi.c where own implementation of
isxdigit() has been changed to kernel native one.
Signed-off-by: Andy Shevchenko <ext-andriy.shevchenko@nokia.com>
Acked-by: Tilman Schmidt <tilman@imap.cc>
---
drivers/isdn/gigaset/capi.c | 16 +---------------
1 files changed, 1 insertions(+), 15 deletions(-)
diff --git a/drivers/isdn/gigaset/capi.c b/drivers/isdn/gigaset/capi.c
index 7ac71c5..c949df5 100644
--- a/drivers/isdn/gigaset/capi.c
+++ b/drivers/isdn/gigaset/capi.c
@@ -171,20 +171,6 @@ static inline void ignore_cstruct_param(struct cardstate *cs, _cstruct param,
}
/*
- * check for legal hex digit
- */
-static inline int ishexdigit(char c)
-{
- if (c >= '0' && c <= '9')
- return 1;
- if (c >= 'A' && c <= 'F')
- return 1;
- if (c >= 'a' && c <= 'f')
- return 1;
- return 0;
-}
-
-/*
* convert hex to binary
*/
static inline u8 hex2bin(char c)
@@ -204,7 +190,7 @@ static int encode_ie(char *in, u8 *out, int maxlen)
{
int l = 0;
while (*in) {
- if (!ishexdigit(in[0]) || !ishexdigit(in[1]) || l >= maxlen)
+ if (!isxdigit(in[0]) || !isxdigit(in[1]) || l >= maxlen)
return -1;
out[++l] = (hex2bin(in[0]) << 4) + hex2bin(in[1]);
in += 2;
--
1.6.5.3.298.g39add
^ permalink raw reply related
* [PATCH 3/4] gigaset: reduce syslog clutter
From: Tilman Schmidt @ 2010-02-22 23:09 UTC (permalink / raw)
To: Karsten Keil, David Miller
Cc: Hansjoerg Lipp, Karsten Keil, isdn4linux, i4ldeveloper, netdev,
linux-kernel
In-Reply-To: <20100221-patch-gigaset-00.tilman@imap.cc>
From: Tilman Schmidt <tilman@imap.cc>
Subject: [PATCH 3/4] gigaset: reduce syslog clutter
Improve readability of the Gigaset driver's kernel messages by
removing a few unnecessary messages and limiting the emission
of some debug messages more narrowly.
Impact: logging
Signed-off-by: Tilman Schmidt <tilman@imap.cc>
---
drivers/isdn/gigaset/asyncdata.c | 4 +-
drivers/isdn/gigaset/bas-gigaset.c | 18 +++-------
drivers/isdn/gigaset/capi.c | 15 ++------
drivers/isdn/gigaset/common.c | 49 +++++++++++----------------
drivers/isdn/gigaset/ev-layer.c | 63 ++++++++++++++++++-----------------
drivers/isdn/gigaset/gigaset.h | 7 +---
drivers/isdn/gigaset/i4l.c | 52 +++--------------------------
drivers/isdn/gigaset/interface.c | 12 ++----
drivers/isdn/gigaset/proc.c | 2 -
drivers/isdn/gigaset/usb-gigaset.c | 2 +-
10 files changed, 77 insertions(+), 147 deletions(-)
diff --git a/drivers/isdn/gigaset/asyncdata.c b/drivers/isdn/gigaset/asyncdata.c
index e913beb..c5016bd 100644
--- a/drivers/isdn/gigaset/asyncdata.c
+++ b/drivers/isdn/gigaset/asyncdata.c
@@ -67,14 +67,14 @@ static unsigned cmd_loop(unsigned numbytes, struct inbuf_t *inbuf)
/* --v-- fall through --v-- */
case '\r':
/* end of message line, pass to response handler */
- gig_dbg(DEBUG_TRANSCMD, "%s: End of Message (%d Bytes)",
- __func__, cbytes);
if (cbytes >= MAX_RESP_SIZE) {
dev_warn(cs->dev, "response too large (%d)\n",
cbytes);
cbytes = MAX_RESP_SIZE;
}
cs->cbytes = cbytes;
+ gigaset_dbg_buffer(DEBUG_TRANSCMD, "received response",
+ cbytes, cs->respdata);
gigaset_handle_modem_response(cs);
cbytes = 0;
diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c
index 95ebc51..0be15c7 100644
--- a/drivers/isdn/gigaset/bas-gigaset.c
+++ b/drivers/isdn/gigaset/bas-gigaset.c
@@ -347,12 +347,7 @@ static inline void error_hangup(struct bc_state *bcs)
{
struct cardstate *cs = bcs->cs;
- gig_dbg(DEBUG_ANY, "%s: scheduling HUP for channel %d",
- __func__, bcs->channel);
-
- if (!gigaset_add_event(cs, &bcs->at_state, EV_HUP, NULL, 0, NULL))
- dev_err(cs->dev, "event queue full\n");
-
+ gigaset_add_event(cs, &bcs->at_state, EV_HUP, NULL, 0, NULL);
gigaset_schedule_event(cs);
}
@@ -1706,8 +1701,7 @@ static void complete_cb(struct cardstate *cs)
/* unqueue completed buffer */
cs->cmdbytes -= cs->curlen;
- gig_dbg(DEBUG_TRANSCMD|DEBUG_LOCKCMD,
- "write_command: sent %u bytes, %u left",
+ gig_dbg(DEBUG_OUTPUT, "write_command: sent %u bytes, %u left",
cs->curlen, cs->cmdbytes);
if (cb->next != NULL) {
cs->cmdbuf = cb->next;
@@ -1881,13 +1875,13 @@ static int start_cbsend(struct cardstate *cs)
/* check if suspend requested */
if (ucs->basstate & BS_SUSPEND) {
- gig_dbg(DEBUG_TRANSCMD|DEBUG_LOCKCMD, "suspending");
+ gig_dbg(DEBUG_OUTPUT, "suspending");
return -EHOSTUNREACH;
}
/* check if AT channel is open */
if (!(ucs->basstate & BS_ATOPEN)) {
- gig_dbg(DEBUG_TRANSCMD|DEBUG_LOCKCMD, "AT channel not open");
+ gig_dbg(DEBUG_OUTPUT, "AT channel not open");
rc = req_submit(cs->bcs, HD_OPEN_ATCHANNEL, 0, BAS_TIMEOUT);
if (rc < 0) {
/* flush command queue */
@@ -2251,7 +2245,7 @@ static int gigaset_probe(struct usb_interface *interface,
int i, j;
int rc;
- gig_dbg(DEBUG_ANY,
+ gig_dbg(DEBUG_INIT,
"%s: Check if device matches .. (Vendor: 0x%x, Product: 0x%x)",
__func__, le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));
@@ -2259,7 +2253,7 @@ static int gigaset_probe(struct usb_interface *interface,
/* set required alternate setting */
hostif = interface->cur_altsetting;
if (hostif->desc.bAlternateSetting != 3) {
- gig_dbg(DEBUG_ANY,
+ gig_dbg(DEBUG_INIT,
"%s: wrong alternate setting %d - trying to switch",
__func__, hostif->desc.bAlternateSetting);
if (usb_set_interface(udev, hostif->desc.bInterfaceNumber, 3)
diff --git a/drivers/isdn/gigaset/capi.c b/drivers/isdn/gigaset/capi.c
index 3f5cd06..7ac71c5 100644
--- a/drivers/isdn/gigaset/capi.c
+++ b/drivers/isdn/gigaset/capi.c
@@ -1427,9 +1427,10 @@ static void do_connect_req(struct gigaset_capi_ctr *iif,
/* queue & schedule EV_DIAL event */
if (!gigaset_add_event(cs, &bcs->at_state, EV_DIAL, commands,
- bcs->at_state.seq_index, NULL))
- goto oom;
- gig_dbg(DEBUG_CMD, "scheduling DIAL");
+ bcs->at_state.seq_index, NULL)) {
+ info = CAPI_MSGOSRESOURCEERR;
+ goto error;
+ }
gigaset_schedule_event(cs);
ap->connected = APCONN_SETUP;
send_conf(iif, ap, skb, CapiSuccess);
@@ -1543,7 +1544,6 @@ static void do_connect_resp(struct gigaset_capi_ctr *iif,
if (!gigaset_add_event(cs, &cs->bcs[channel-1].at_state,
EV_ACCEPT, NULL, 0, NULL))
return;
- gig_dbg(DEBUG_CMD, "scheduling ACCEPT");
gigaset_schedule_event(cs);
return;
@@ -1584,7 +1584,6 @@ static void do_connect_resp(struct gigaset_capi_ctr *iif,
if (!gigaset_add_event(cs, &cs->bcs[channel-1].at_state,
EV_HUP, NULL, 0, NULL))
return;
- gig_dbg(DEBUG_CMD, "scheduling HUP");
gigaset_schedule_event(cs);
return;
}
@@ -1667,11 +1666,9 @@ static void do_connect_b3_resp(struct gigaset_capi_ctr *iif,
/* trigger hangup, causing eventual DISCONNECT_IND */
if (!gigaset_add_event(cs, &bcs->at_state,
EV_HUP, NULL, 0, NULL)) {
- dev_err(cs->dev, "%s: out of memory\n", __func__);
dev_kfree_skb_any(skb);
return;
}
- gig_dbg(DEBUG_CMD, "scheduling HUP");
gigaset_schedule_event(cs);
/* emit DISCONNECT_B3_IND */
@@ -1770,11 +1767,9 @@ static void do_disconnect_req(struct gigaset_capi_ctr *iif,
/* trigger hangup, causing eventual DISCONNECT_IND */
if (!gigaset_add_event(cs, &bcs->at_state, EV_HUP, NULL, 0, NULL)) {
- dev_err(cs->dev, "%s: out of memory\n", __func__);
send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR);
return;
}
- gig_dbg(DEBUG_CMD, "scheduling HUP");
gigaset_schedule_event(cs);
/* emit reply */
@@ -1817,11 +1812,9 @@ static void do_disconnect_b3_req(struct gigaset_capi_ctr *iif,
/* trigger hangup, causing eventual DISCONNECT_B3_IND */
if (!gigaset_add_event(cs, &cs->bcs[channel-1].at_state,
EV_HUP, NULL, 0, NULL)) {
- dev_err(cs->dev, "%s: out of memory\n", __func__);
send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR);
return;
}
- gig_dbg(DEBUG_CMD, "scheduling HUP");
gigaset_schedule_event(cs);
/* NCPI parameter: not applicable for B3 Transparent */
diff --git a/drivers/isdn/gigaset/common.c b/drivers/isdn/gigaset/common.c
index 664b0c5..85de339 100644
--- a/drivers/isdn/gigaset/common.c
+++ b/drivers/isdn/gigaset/common.c
@@ -149,10 +149,8 @@ static int test_timeout(struct at_state_t *at_state)
return 0;
}
- if (!gigaset_add_event(at_state->cs, at_state, EV_TIMEOUT, NULL,
- at_state->timer_index, NULL))
- dev_err(at_state->cs->dev, "%s: out of memory\n",
- __func__);
+ gigaset_add_event(at_state->cs, at_state, EV_TIMEOUT, NULL,
+ at_state->timer_index, NULL);
return 1;
}
@@ -180,7 +178,7 @@ static void timer_tick(unsigned long data)
if (cs->running) {
mod_timer(&cs->timer, jiffies + msecs_to_jiffies(GIG_TICK));
if (timeout) {
- gig_dbg(DEBUG_CMD, "scheduling timeout");
+ gig_dbg(DEBUG_EVENT, "scheduling timeout");
tasklet_schedule(&cs->event_tasklet);
}
}
@@ -194,14 +192,14 @@ int gigaset_get_channel(struct bc_state *bcs)
spin_lock_irqsave(&bcs->cs->lock, flags);
if (bcs->use_count || !try_module_get(bcs->cs->driver->owner)) {
- gig_dbg(DEBUG_ANY, "could not allocate channel %d",
+ gig_dbg(DEBUG_CHANNEL, "could not allocate channel %d",
bcs->channel);
spin_unlock_irqrestore(&bcs->cs->lock, flags);
return 0;
}
++bcs->use_count;
bcs->busy = 1;
- gig_dbg(DEBUG_ANY, "allocated channel %d", bcs->channel);
+ gig_dbg(DEBUG_CHANNEL, "allocated channel %d", bcs->channel);
spin_unlock_irqrestore(&bcs->cs->lock, flags);
return 1;
}
@@ -213,7 +211,7 @@ struct bc_state *gigaset_get_free_channel(struct cardstate *cs)
spin_lock_irqsave(&cs->lock, flags);
if (!try_module_get(cs->driver->owner)) {
- gig_dbg(DEBUG_ANY,
+ gig_dbg(DEBUG_CHANNEL,
"could not get module for allocating channel");
spin_unlock_irqrestore(&cs->lock, flags);
return NULL;
@@ -223,12 +221,12 @@ struct bc_state *gigaset_get_free_channel(struct cardstate *cs)
++cs->bcs[i].use_count;
cs->bcs[i].busy = 1;
spin_unlock_irqrestore(&cs->lock, flags);
- gig_dbg(DEBUG_ANY, "allocated channel %d", i);
+ gig_dbg(DEBUG_CHANNEL, "allocated channel %d", i);
return cs->bcs + i;
}
module_put(cs->driver->owner);
spin_unlock_irqrestore(&cs->lock, flags);
- gig_dbg(DEBUG_ANY, "no free channel");
+ gig_dbg(DEBUG_CHANNEL, "no free channel");
return NULL;
}
@@ -238,14 +236,15 @@ void gigaset_free_channel(struct bc_state *bcs)
spin_lock_irqsave(&bcs->cs->lock, flags);
if (!bcs->busy) {
- gig_dbg(DEBUG_ANY, "could not free channel %d", bcs->channel);
+ gig_dbg(DEBUG_CHANNEL, "could not free channel %d",
+ bcs->channel);
spin_unlock_irqrestore(&bcs->cs->lock, flags);
return;
}
--bcs->use_count;
bcs->busy = 0;
module_put(bcs->cs->driver->owner);
- gig_dbg(DEBUG_ANY, "freed channel %d", bcs->channel);
+ gig_dbg(DEBUG_CHANNEL, "freed channel %d", bcs->channel);
spin_unlock_irqrestore(&bcs->cs->lock, flags);
}
@@ -258,14 +257,15 @@ int gigaset_get_channels(struct cardstate *cs)
for (i = 0; i < cs->channels; ++i)
if (cs->bcs[i].use_count) {
spin_unlock_irqrestore(&cs->lock, flags);
- gig_dbg(DEBUG_ANY, "could not allocate all channels");
+ gig_dbg(DEBUG_CHANNEL,
+ "could not allocate all channels");
return 0;
}
for (i = 0; i < cs->channels; ++i)
++cs->bcs[i].use_count;
spin_unlock_irqrestore(&cs->lock, flags);
- gig_dbg(DEBUG_ANY, "allocated all channels");
+ gig_dbg(DEBUG_CHANNEL, "allocated all channels");
return 1;
}
@@ -275,7 +275,7 @@ void gigaset_free_channels(struct cardstate *cs)
unsigned long flags;
int i;
- gig_dbg(DEBUG_ANY, "unblocking all channels");
+ gig_dbg(DEBUG_CHANNEL, "unblocking all channels");
spin_lock_irqsave(&cs->lock, flags);
for (i = 0; i < cs->channels; ++i)
--cs->bcs[i].use_count;
@@ -287,7 +287,7 @@ void gigaset_block_channels(struct cardstate *cs)
unsigned long flags;
int i;
- gig_dbg(DEBUG_ANY, "blocking all channels");
+ gig_dbg(DEBUG_CHANNEL, "blocking all channels");
spin_lock_irqsave(&cs->lock, flags);
for (i = 0; i < cs->channels; ++i)
++cs->bcs[i].use_count;
@@ -338,6 +338,8 @@ struct event_t *gigaset_add_event(struct cardstate *cs,
unsigned next, tail;
struct event_t *event = NULL;
+ gig_dbg(DEBUG_EVENT, "queueing event %d", type);
+
spin_lock_irqsave(&cs->ev_lock, flags);
tail = cs->ev_tail;
@@ -934,11 +936,8 @@ int gigaset_start(struct cardstate *cs)
if (!gigaset_add_event(cs, &cs->at_state, EV_START, NULL, 0, NULL)) {
cs->waiting = 0;
- dev_err(cs->dev, "%s: out of memory\n", __func__);
goto error;
}
-
- gig_dbg(DEBUG_CMD, "scheduling START");
gigaset_schedule_event(cs);
wait_event(cs->waitqueue, !cs->waiting);
@@ -973,12 +972,8 @@ int gigaset_shutdown(struct cardstate *cs)
cs->waiting = 1;
- if (!gigaset_add_event(cs, &cs->at_state, EV_SHUTDOWN, NULL, 0, NULL)) {
- dev_err(cs->dev, "%s: out of memory\n", __func__);
+ if (!gigaset_add_event(cs, &cs->at_state, EV_SHUTDOWN, NULL, 0, NULL))
goto exit;
- }
-
- gig_dbg(DEBUG_CMD, "scheduling SHUTDOWN");
gigaset_schedule_event(cs);
wait_event(cs->waitqueue, !cs->waiting);
@@ -1004,12 +999,8 @@ void gigaset_stop(struct cardstate *cs)
cs->waiting = 1;
- if (!gigaset_add_event(cs, &cs->at_state, EV_STOP, NULL, 0, NULL)) {
- dev_err(cs->dev, "%s: out of memory\n", __func__);
+ if (!gigaset_add_event(cs, &cs->at_state, EV_STOP, NULL, 0, NULL))
goto exit;
- }
-
- gig_dbg(DEBUG_CMD, "scheduling STOP");
gigaset_schedule_event(cs);
wait_event(cs->waitqueue, !cs->waiting);
diff --git a/drivers/isdn/gigaset/ev-layer.c b/drivers/isdn/gigaset/ev-layer.c
index ddeb045..c8f89b7 100644
--- a/drivers/isdn/gigaset/ev-layer.c
+++ b/drivers/isdn/gigaset/ev-layer.c
@@ -427,7 +427,7 @@ static int isdn_getnum(char *p)
{
int v = -1;
- gig_dbg(DEBUG_TRANSCMD, "string: %s", p);
+ gig_dbg(DEBUG_EVENT, "string: %s", p);
while (*p >= '0' && *p <= '9')
v = ((v < 0) ? 0 : (v * 10)) + (int) ((*p++) - '0');
@@ -444,7 +444,7 @@ static int isdn_gethex(char *p)
int v = 0;
int c;
- gig_dbg(DEBUG_TRANSCMD, "string: %s", p);
+ gig_dbg(DEBUG_EVENT, "string: %s", p);
if (!*p)
return -1;
@@ -517,7 +517,6 @@ void gigaset_handle_modem_response(struct cardstate *cs)
return;
}
cs->respdata[len] = 0;
- gig_dbg(DEBUG_TRANSCMD, "raw string: '%s'", cs->respdata);
argv[0] = cs->respdata;
params = 1;
if (cs->at_state.getstring) {
@@ -552,14 +551,14 @@ void gigaset_handle_modem_response(struct cardstate *cs)
for (j = 1; j < params; ++j)
argv[j][-1] = 0;
- gig_dbg(DEBUG_TRANSCMD, "CMD received: %s", argv[0]);
+ gig_dbg(DEBUG_EVENT, "CMD received: %s", argv[0]);
if (cid) {
--params;
- gig_dbg(DEBUG_TRANSCMD, "CID: %s", argv[params]);
+ gig_dbg(DEBUG_EVENT, "CID: %s", argv[params]);
}
- gig_dbg(DEBUG_TRANSCMD, "available params: %d", params - 1);
+ gig_dbg(DEBUG_EVENT, "available params: %d", params - 1);
for (j = 1; j < params; j++)
- gig_dbg(DEBUG_TRANSCMD, "param %d: %s", j, argv[j]);
+ gig_dbg(DEBUG_EVENT, "param %d: %s", j, argv[j]);
}
spin_lock_irqsave(&cs->ev_lock, flags);
@@ -642,7 +641,7 @@ void gigaset_handle_modem_response(struct cardstate *cs)
dev_err(cs->dev, "out of memory\n");
++curarg;
}
- gig_dbg(DEBUG_CMD, "string==%s",
+ gig_dbg(DEBUG_EVENT, "string==%s",
event->ptr ? (char *) event->ptr : "NULL");
break;
case RT_ZCAU:
@@ -669,7 +668,7 @@ void gigaset_handle_modem_response(struct cardstate *cs)
++curarg;
} else
event->parameter = -1;
- gig_dbg(DEBUG_CMD, "parameter==%d", event->parameter);
+ gig_dbg(DEBUG_EVENT, "parameter==%d", event->parameter);
break;
}
@@ -684,7 +683,7 @@ void gigaset_handle_modem_response(struct cardstate *cs)
spin_unlock_irqrestore(&cs->ev_lock, flags);
if (curarg != params)
- gig_dbg(DEBUG_ANY,
+ gig_dbg(DEBUG_EVENT,
"invalid number of processed parameters: %d/%d",
curarg, params);
}
@@ -705,8 +704,8 @@ static void disconnect(struct at_state_t **at_state_p)
/* revert to selected idle mode */
if (!cs->cidmode) {
cs->at_state.pending_commands |= PC_UMMODE;
+ gig_dbg(DEBUG_EVENT, "Scheduling PC_UMMODE");
cs->commands_pending = 1;
- gig_dbg(DEBUG_CMD, "Scheduling PC_UMMODE");
}
spin_unlock_irqrestore(&cs->lock, flags);
@@ -784,15 +783,15 @@ static void init_failed(struct cardstate *cs, int mode)
static void schedule_init(struct cardstate *cs, int state)
{
if (cs->at_state.pending_commands & PC_INIT) {
- gig_dbg(DEBUG_CMD, "not scheduling PC_INIT again");
+ gig_dbg(DEBUG_EVENT, "not scheduling PC_INIT again");
return;
}
cs->mstate = state;
cs->mode = M_UNKNOWN;
gigaset_block_channels(cs);
cs->at_state.pending_commands |= PC_INIT;
+ gig_dbg(DEBUG_EVENT, "Scheduling PC_INIT");
cs->commands_pending = 1;
- gig_dbg(DEBUG_CMD, "Scheduling PC_INIT");
}
/* Add "AT" to a command, add the cid, dle encode it, send the result to the
@@ -923,7 +922,7 @@ static void start_dial(struct at_state_t *at_state, void *data,
}
at_state->pending_commands |= PC_CID;
- gig_dbg(DEBUG_CMD, "Scheduling PC_CID");
+ gig_dbg(DEBUG_EVENT, "Scheduling PC_CID");
cs->commands_pending = 1;
return;
@@ -933,7 +932,7 @@ error:
commands[i] = NULL;
}
at_state->pending_commands |= PC_NOCID;
- gig_dbg(DEBUG_CMD, "Scheduling PC_NOCID");
+ gig_dbg(DEBUG_EVENT, "Scheduling PC_NOCID");
cs->commands_pending = 1;
return;
}
@@ -955,7 +954,7 @@ static void start_accept(struct at_state_t *at_state)
dev_err(at_state->cs->dev, "out of memory\n");
/* error reset */
at_state->pending_commands |= PC_HUP;
- gig_dbg(DEBUG_CMD, "Scheduling PC_HUP");
+ gig_dbg(DEBUG_EVENT, "Scheduling PC_HUP");
cs->commands_pending = 1;
return;
}
@@ -964,7 +963,7 @@ static void start_accept(struct at_state_t *at_state)
snprintf(bcs->commands[AT_ISO], 9, "^SISO=%u\r", bcs->channel + 1);
at_state->pending_commands |= PC_ACCEPT;
- gig_dbg(DEBUG_CMD, "Scheduling PC_ACCEPT");
+ gig_dbg(DEBUG_EVENT, "Scheduling PC_ACCEPT");
cs->commands_pending = 1;
}
@@ -1009,8 +1008,8 @@ static void do_shutdown(struct cardstate *cs)
if (cs->mstate == MS_READY) {
cs->mstate = MS_SHUTDOWN;
cs->at_state.pending_commands |= PC_SHUTDOWN;
+ gig_dbg(DEBUG_EVENT, "Scheduling PC_SHUTDOWN");
cs->commands_pending = 1;
- gig_dbg(DEBUG_CMD, "Scheduling PC_SHUTDOWN");
} else
finish_shutdown(cs);
}
@@ -1191,8 +1190,8 @@ static void do_action(int action, struct cardstate *cs,
}
spin_unlock_irqrestore(&cs->lock, flags);
cs->at_state.pending_commands |= PC_CIDMODE;
+ gig_dbg(DEBUG_EVENT, "Scheduling PC_CIDMODE");
cs->commands_pending = 1;
- gig_dbg(DEBUG_CMD, "Scheduling PC_CIDMODE");
break;
case ACT_FAILINIT:
dev_warn(cs->dev, "Could not initialize the device.\n");
@@ -1443,7 +1442,7 @@ static void do_action(int action, struct cardstate *cs,
case ACT_GOTVER:
if (cs->gotfwver == 0) {
cs->gotfwver = 1;
- gig_dbg(DEBUG_ANY,
+ gig_dbg(DEBUG_EVENT,
"firmware version %02d.%03d.%02d.%02d",
cs->fwver[0], cs->fwver[1],
cs->fwver[2], cs->fwver[3]);
@@ -1481,8 +1480,8 @@ static void do_action(int action, struct cardstate *cs,
break;
case ACT_HUP:
at_state->pending_commands |= PC_HUP;
+ gig_dbg(DEBUG_EVENT, "Scheduling PC_HUP");
cs->commands_pending = 1;
- gig_dbg(DEBUG_CMD, "Scheduling PC_HUP");
break;
/* hotplug events */
@@ -1519,10 +1518,10 @@ static void do_action(int action, struct cardstate *cs,
cs->cidmode = ev->parameter;
if (ev->parameter) {
cs->at_state.pending_commands |= PC_CIDMODE;
- gig_dbg(DEBUG_CMD, "Scheduling PC_CIDMODE");
+ gig_dbg(DEBUG_EVENT, "Scheduling PC_CIDMODE");
} else {
cs->at_state.pending_commands |= PC_UMMODE;
- gig_dbg(DEBUG_CMD, "Scheduling PC_UMMODE");
+ gig_dbg(DEBUG_EVENT, "Scheduling PC_UMMODE");
}
cs->commands_pending = 1;
}
@@ -1573,6 +1572,8 @@ static void process_event(struct cardstate *cs, struct event_t *ev)
if (ev->cid >= 0) {
at_state = at_state_from_cid(cs, ev->cid);
if (!at_state) {
+ gig_dbg(DEBUG_EVENT, "event %d for invalid cid %d",
+ ev->type, ev->cid);
gigaset_add_event(cs, &cs->at_state, RSP_WRONG_CID,
NULL, 0, NULL);
return;
@@ -1580,13 +1581,13 @@ static void process_event(struct cardstate *cs, struct event_t *ev)
} else {
at_state = ev->at_state;
if (at_state_invalid(cs, at_state)) {
- gig_dbg(DEBUG_ANY, "event for invalid at_state %p",
+ gig_dbg(DEBUG_EVENT, "event for invalid at_state %p",
at_state);
return;
}
}
- gig_dbg(DEBUG_CMD, "connection state %d, event %d",
+ gig_dbg(DEBUG_EVENT, "connection state %d, event %d",
at_state->ConState, ev->type);
bcs = at_state->bcs;
@@ -1600,11 +1601,11 @@ static void process_event(struct cardstate *cs, struct event_t *ev)
if (ev->parameter != at_state->timer_index
|| !at_state->timer_active) {
ev->type = RSP_NONE; /* old timeout */
- gig_dbg(DEBUG_ANY, "old timeout");
+ gig_dbg(DEBUG_EVENT, "old timeout");
} else if (!at_state->waiting)
- gig_dbg(DEBUG_ANY, "timeout occurred");
+ gig_dbg(DEBUG_EVENT, "timeout occurred");
else
- gig_dbg(DEBUG_ANY, "stopped waiting");
+ gig_dbg(DEBUG_EVENT, "stopped waiting");
}
spin_unlock_irqrestore(&cs->lock, flags);
@@ -1712,11 +1713,11 @@ static void process_command_flags(struct cardstate *cs)
cs->commands_pending = 0;
if (cs->cur_at_seq) {
- gig_dbg(DEBUG_CMD, "not searching scheduled commands: busy");
+ gig_dbg(DEBUG_EVENT, "not searching scheduled commands: busy");
return;
}
- gig_dbg(DEBUG_CMD, "searching scheduled commands");
+ gig_dbg(DEBUG_EVENT, "searching scheduled commands");
sequence = SEQ_NONE;
@@ -1857,7 +1858,7 @@ static void process_command_flags(struct cardstate *cs)
switch (cs->mode) {
case M_UNIMODEM:
cs->at_state.pending_commands |= PC_CIDMODE;
- gig_dbg(DEBUG_CMD, "Scheduling PC_CIDMODE");
+ gig_dbg(DEBUG_EVENT, "Scheduling PC_CIDMODE");
cs->commands_pending = 1;
return;
#ifdef GIG_MAYINITONDIAL
diff --git a/drivers/isdn/gigaset/gigaset.h b/drivers/isdn/gigaset/gigaset.h
index c9ccf7d..1875ab8 100644
--- a/drivers/isdn/gigaset/gigaset.h
+++ b/drivers/isdn/gigaset/gigaset.h
@@ -78,9 +78,10 @@ enum debuglevel {
DEBUG_STREAM = 0x00040, /* application data stream I/O events */
DEBUG_STREAM_DUMP = 0x00080, /* application data stream content */
DEBUG_LLDATA = 0x00100, /* sent/received LL data */
+ DEBUG_EVENT = 0x00200, /* event processing */
DEBUG_DRIVER = 0x00400, /* driver structure */
DEBUG_HDLC = 0x00800, /* M10x HDLC processing */
- DEBUG_WRITE = 0x01000, /* M105 data write */
+ DEBUG_CHANNEL = 0x01000, /* channel allocation/deallocation */
DEBUG_TRANSCMD = 0x02000, /* AT-COMMANDS+RESPONSES */
DEBUG_MCMD = 0x04000, /* COMMANDS THAT ARE SENT VERY OFTEN */
DEBUG_INIT = 0x08000, /* (de)allocation+initialization of data
@@ -785,8 +786,6 @@ static inline void gigaset_schedule_event(struct cardstate *cs)
static inline void gigaset_bchannel_down(struct bc_state *bcs)
{
gigaset_add_event(bcs->cs, &bcs->at_state, EV_BC_CLOSED, NULL, 0, NULL);
-
- gig_dbg(DEBUG_CMD, "scheduling BC_CLOSED");
gigaset_schedule_event(bcs->cs);
}
@@ -795,8 +794,6 @@ static inline void gigaset_bchannel_down(struct bc_state *bcs)
static inline void gigaset_bchannel_up(struct bc_state *bcs)
{
gigaset_add_event(bcs->cs, &bcs->at_state, EV_BC_OPEN, NULL, 0, NULL);
-
- gig_dbg(DEBUG_CMD, "scheduling BC_OPEN");
gigaset_schedule_event(bcs->cs);
}
diff --git a/drivers/isdn/gigaset/i4l.c b/drivers/isdn/gigaset/i4l.c
index c129ee4..f0acb9d 100644
--- a/drivers/isdn/gigaset/i4l.c
+++ b/drivers/isdn/gigaset/i4l.c
@@ -216,7 +216,7 @@ static int command_from_LL(isdn_ctrl *cntrl)
return -EINVAL;
case ISDN_CMD_DIAL:
- gig_dbg(DEBUG_ANY,
+ gig_dbg(DEBUG_CMD,
"ISDN_CMD_DIAL (phone: %s, msn: %s, si1: %d, si2: %d)",
cntrl->parm.setup.phone, cntrl->parm.setup.eazmsn,
cntrl->parm.setup.si1, cntrl->parm.setup.si2);
@@ -304,11 +304,10 @@ static int command_from_LL(isdn_ctrl *cntrl)
gigaset_free_channel(bcs);
return -ENOMEM;
}
-
- gig_dbg(DEBUG_CMD, "scheduling DIAL");
gigaset_schedule_event(cs);
break;
case ISDN_CMD_ACCEPTD:
+ gig_dbg(DEBUG_CMD, "ISDN_CMD_ACCEPTD");
if (ch >= cs->channels) {
dev_err(cs->dev,
"ISDN_CMD_ACCEPTD: invalid channel (%d)\n", ch);
@@ -318,14 +317,11 @@ static int command_from_LL(isdn_ctrl *cntrl)
if (!gigaset_add_event(cs, &bcs->at_state,
EV_ACCEPT, NULL, 0, NULL))
return -ENOMEM;
-
- gig_dbg(DEBUG_CMD, "scheduling ACCEPT");
gigaset_schedule_event(cs);
break;
- case ISDN_CMD_ACCEPTB:
- break;
case ISDN_CMD_HANGUP:
+ gig_dbg(DEBUG_CMD, "ISDN_CMD_HANGUP");
if (ch >= cs->channels) {
dev_err(cs->dev,
"ISDN_CMD_HANGUP: invalid channel (%d)\n", ch);
@@ -335,8 +331,6 @@ static int command_from_LL(isdn_ctrl *cntrl)
if (!gigaset_add_event(cs, &bcs->at_state,
EV_HUP, NULL, 0, NULL))
return -ENOMEM;
-
- gig_dbg(DEBUG_CMD, "scheduling HUP");
gigaset_schedule_event(cs);
break;
@@ -376,6 +370,7 @@ static int command_from_LL(isdn_ctrl *cntrl)
}
break;
case ISDN_CMD_SETL3: /* Set L3 to given protocol */
+ gig_dbg(DEBUG_CMD, "ISDN_CMD_SETL3");
if (ch >= cs->channels) {
dev_err(cs->dev,
"ISDN_CMD_SETL3: invalid channel (%d)\n", ch);
@@ -390,44 +385,9 @@ static int command_from_LL(isdn_ctrl *cntrl)
}
break;
- case ISDN_CMD_PROCEED:
- gig_dbg(DEBUG_ANY, "ISDN_CMD_PROCEED");
- break;
- case ISDN_CMD_ALERT:
- gig_dbg(DEBUG_ANY, "ISDN_CMD_ALERT");
- if (cntrl->arg >= cs->channels) {
- dev_err(cs->dev,
- "ISDN_CMD_ALERT: invalid channel (%d)\n",
- (int) cntrl->arg);
- return -EINVAL;
- }
- break;
- case ISDN_CMD_REDIR:
- gig_dbg(DEBUG_ANY, "ISDN_CMD_REDIR");
- break;
- case ISDN_CMD_PROT_IO:
- gig_dbg(DEBUG_ANY, "ISDN_CMD_PROT_IO");
- break;
- case ISDN_CMD_FAXCMD:
- gig_dbg(DEBUG_ANY, "ISDN_CMD_FAXCMD");
- break;
- case ISDN_CMD_GETL2:
- gig_dbg(DEBUG_ANY, "ISDN_CMD_GETL2");
- break;
- case ISDN_CMD_GETL3:
- gig_dbg(DEBUG_ANY, "ISDN_CMD_GETL3");
- break;
- case ISDN_CMD_GETEAZ:
- gig_dbg(DEBUG_ANY, "ISDN_CMD_GETEAZ");
- break;
- case ISDN_CMD_SETSIL:
- gig_dbg(DEBUG_ANY, "ISDN_CMD_SETSIL");
- break;
- case ISDN_CMD_GETSIL:
- gig_dbg(DEBUG_ANY, "ISDN_CMD_GETSIL");
- break;
+
default:
- dev_err(cs->dev, "unknown command %d from LL\n",
+ gig_dbg(DEBUG_CMD, "unknown command %d from LL",
cntrl->command);
return -EINVAL;
}
diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c
index d2260b0..a1bcbc2 100644
--- a/drivers/isdn/gigaset/interface.c
+++ b/drivers/isdn/gigaset/interface.c
@@ -45,8 +45,6 @@ static int if_lock(struct cardstate *cs, int *arg)
cs->waiting = 0;
return -ENOMEM;
}
-
- gig_dbg(DEBUG_CMD, "scheduling IF_LOCK");
gigaset_schedule_event(cs);
wait_event(cs->waitqueue, !cs->waiting);
@@ -81,8 +79,6 @@ static int if_version(struct cardstate *cs, unsigned arg[4])
cs->waiting = 0;
return -ENOMEM;
}
-
- gig_dbg(DEBUG_CMD, "scheduling IF_VER");
gigaset_schedule_event(cs);
wait_event(cs->waitqueue, !cs->waiting);
@@ -274,7 +270,7 @@ static int if_ioctl(struct tty_struct *tty, struct file *file,
? -EFAULT : 0;
break;
default:
- gig_dbg(DEBUG_ANY, "%s: arg not supported - 0x%04x",
+ gig_dbg(DEBUG_IF, "%s: arg not supported - 0x%04x",
__func__, cmd);
retval = -ENOIOCTLCMD;
}
@@ -455,7 +451,7 @@ static void if_throttle(struct tty_struct *tty)
else if (!cs->open_count)
dev_warn(cs->dev, "%s: device not opened\n", __func__);
else
- gig_dbg(DEBUG_ANY, "%s: not implemented\n", __func__);
+ gig_dbg(DEBUG_IF, "%s: not implemented\n", __func__);
mutex_unlock(&cs->mutex);
}
@@ -479,7 +475,7 @@ static void if_unthrottle(struct tty_struct *tty)
else if (!cs->open_count)
dev_warn(cs->dev, "%s: device not opened\n", __func__);
else
- gig_dbg(DEBUG_ANY, "%s: not implemented\n", __func__);
+ gig_dbg(DEBUG_IF, "%s: not implemented\n", __func__);
mutex_unlock(&cs->mutex);
}
@@ -630,7 +626,7 @@ void gigaset_if_receive(struct cardstate *cs,
spin_lock_irqsave(&cs->lock, flags);
tty = cs->tty;
if (tty == NULL)
- gig_dbg(DEBUG_ANY, "receive on closed device");
+ gig_dbg(DEBUG_IF, "receive on closed device");
else {
tty_buffer_request_room(tty, len);
tty_insert_flip_string(tty, buffer, len);
diff --git a/drivers/isdn/gigaset/proc.c b/drivers/isdn/gigaset/proc.c
index 758a00c..b69f73a 100644
--- a/drivers/isdn/gigaset/proc.c
+++ b/drivers/isdn/gigaset/proc.c
@@ -48,8 +48,6 @@ static ssize_t set_cidmode(struct device *dev, struct device_attribute *attr,
mutex_unlock(&cs->mutex);
return -ENOMEM;
}
-
- gig_dbg(DEBUG_CMD, "scheduling PROC_CIDMODE");
gigaset_schedule_event(cs);
wait_event(cs->waitqueue, !cs->waiting);
diff --git a/drivers/isdn/gigaset/usb-gigaset.c b/drivers/isdn/gigaset/usb-gigaset.c
index 3ab1dae..9430a2b 100644
--- a/drivers/isdn/gigaset/usb-gigaset.c
+++ b/drivers/isdn/gigaset/usb-gigaset.c
@@ -628,7 +628,7 @@ static int write_modem(struct cardstate *cs)
struct usb_cardstate *ucs = cs->hw.usb;
unsigned long flags;
- gig_dbg(DEBUG_WRITE, "len: %d...", bcs->tx_skb->len);
+ gig_dbg(DEBUG_OUTPUT, "len: %d...", bcs->tx_skb->len);
if (!bcs->tx_skb->len) {
dev_kfree_skb_any(bcs->tx_skb);
--
1.6.5.3.298.g39add
^ permalink raw reply related
* [PATCH 2/4] bas_gigaset: collapse CR/LF at end of AT response
From: Tilman Schmidt @ 2010-02-22 23:09 UTC (permalink / raw)
To: Karsten Keil, David Miller
Cc: Hansjoerg Lipp, Karsten Keil, isdn4linux, i4ldeveloper, netdev,
linux-kernel
In-Reply-To: <20100221-patch-gigaset-00.tilman@imap.cc>
From: Tilman Schmidt <tilman@imap.cc>
Subject: [PATCH 2/4] bas_gigaset: collapse CR/LF at end of AT response
Copy the mechanism from ser_/usb_gigaset to avoid producing
spurious empty responses for CR/LF sequences from the device.
Add a comment in all drivers documenting that behaviour.
Correct an off by one error that might result in a one byte
buffer overflow when receiving an unexpectedly long reply.
Impact: minor bugfix
Signed-off-by: Tilman Schmidt <tilman@imap.cc>
---
drivers/isdn/gigaset/asyncdata.c | 2 +
drivers/isdn/gigaset/gigaset.h | 4 +-
drivers/isdn/gigaset/isocdata.c | 44 ++++++++++++++++++++++++++-----------
3 files changed, 35 insertions(+), 15 deletions(-)
diff --git a/drivers/isdn/gigaset/asyncdata.c b/drivers/isdn/gigaset/asyncdata.c
index ccb2a7b..e913beb 100644
--- a/drivers/isdn/gigaset/asyncdata.c
+++ b/drivers/isdn/gigaset/asyncdata.c
@@ -40,6 +40,8 @@ static inline int muststuff(unsigned char c)
* Append received bytes to the command response buffer and forward them
* line by line to the response handler. Exit whenever a mode/state change
* might have occurred.
+ * Note: Received lines may be terminated by CR, LF, or CR LF, which will be
+ * removed before passing the line to the response handler.
* Return value:
* number of processed bytes
*/
diff --git a/drivers/isdn/gigaset/gigaset.h b/drivers/isdn/gigaset/gigaset.h
index e963a6c..c9ccf7d 100644
--- a/drivers/isdn/gigaset/gigaset.h
+++ b/drivers/isdn/gigaset/gigaset.h
@@ -38,7 +38,7 @@
#define GIG_COMPAT {0, 4, 0, 0}
#define MAX_REC_PARAMS 10 /* Max. number of params in response string */
-#define MAX_RESP_SIZE 512 /* Max. size of a response string */
+#define MAX_RESP_SIZE 511 /* Max. size of a response string */
#define MAX_EVENTS 64 /* size of event queue */
@@ -498,7 +498,7 @@ struct cardstate {
spinlock_t ev_lock;
/* current modem response */
- unsigned char respdata[MAX_RESP_SIZE];
+ unsigned char respdata[MAX_RESP_SIZE+1];
unsigned cbytes;
/* private data of hardware drivers */
diff --git a/drivers/isdn/gigaset/isocdata.c b/drivers/isdn/gigaset/isocdata.c
index 85394a6..16fd3bd 100644
--- a/drivers/isdn/gigaset/isocdata.c
+++ b/drivers/isdn/gigaset/isocdata.c
@@ -905,29 +905,49 @@ void gigaset_isoc_receive(unsigned char *src, unsigned count,
/* == data input =========================================================== */
+/* process a block of received bytes in command mode (mstate != MS_LOCKED)
+ * Append received bytes to the command response buffer and forward them
+ * line by line to the response handler.
+ * Note: Received lines may be terminated by CR, LF, or CR LF, which will be
+ * removed before passing the line to the response handler.
+ */
static void cmd_loop(unsigned char *src, int numbytes, struct inbuf_t *inbuf)
{
struct cardstate *cs = inbuf->cs;
unsigned cbytes = cs->cbytes;
+ unsigned char c;
while (numbytes--) {
- /* copy next character, check for end of line */
- switch (cs->respdata[cbytes] = *src++) {
- case '\r':
+ c = *src++;
+ switch (c) {
case '\n':
- /* end of line */
- gig_dbg(DEBUG_TRANSCMD, "%s: End of Command (%d Bytes)",
- __func__, cbytes);
- if (cbytes >= MAX_RESP_SIZE - 1)
- dev_warn(cs->dev, "response too large\n");
+ if (cbytes == 0 && cs->respdata[0] == '\r') {
+ /* collapse LF with preceding CR */
+ cs->respdata[0] = 0;
+ break;
+ }
+ /* --v-- fall through --v-- */
+ case '\r':
+ /* end of message line, pass to response handler */
+ if (cbytes >= MAX_RESP_SIZE) {
+ dev_warn(cs->dev, "response too large (%d)\n",
+ cbytes);
+ cbytes = MAX_RESP_SIZE;
+ }
cs->cbytes = cbytes;
+ gigaset_dbg_buffer(DEBUG_TRANSCMD, "received response",
+ cbytes, cs->respdata);
gigaset_handle_modem_response(cs);
cbytes = 0;
+
+ /* store EOL byte for CRLF collapsing */
+ cs->respdata[0] = c;
break;
default:
- /* advance in line buffer, checking for overflow */
- if (cbytes < MAX_RESP_SIZE - 1)
- cbytes++;
+ /* append to line buffer if possible */
+ if (cbytes < MAX_RESP_SIZE)
+ cs->respdata[cbytes] = c;
+ cbytes++;
}
}
@@ -958,8 +978,6 @@ void gigaset_isoc_input(struct inbuf_t *inbuf)
numbytes, src);
gigaset_if_receive(inbuf->cs, src, numbytes);
} else {
- gigaset_dbg_buffer(DEBUG_CMD, "received response",
- numbytes, src);
cmd_loop(src, numbytes, inbuf);
}
--
1.6.5.3.298.g39add
^ permalink raw reply related
* [PATCH 1/4] gigaset: small documentation improvement
From: Tilman Schmidt @ 2010-02-22 23:08 UTC (permalink / raw)
To: Karsten Keil, David Miller
Cc: Hansjoerg Lipp, Karsten Keil, isdn4linux, i4ldeveloper, netdev,
linux-kernel
In-Reply-To: <20100221-patch-gigaset-00.tilman@imap.cc>
From: Tilman Schmidt <tilman@imap.cc>
Subject: [PATCH 1/4] gigaset: small documentation improvement
Clarify the non-support by isdnlog, and propose a better standard
debug mask.
Impact: Documentation
Signed-off-by: Tilman Schmidt <tilman@imap.cc>
---
Documentation/isdn/README.gigaset | 10 +++++-----
1 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/Documentation/isdn/README.gigaset b/Documentation/isdn/README.gigaset
index 794941f..e472df8 100644
--- a/Documentation/isdn/README.gigaset
+++ b/Documentation/isdn/README.gigaset
@@ -292,10 +292,10 @@ GigaSet 307x Device Driver
to /etc/modprobe.d/gigaset, /etc/modprobe.conf.local or a similar file.
Problem:
- Your isdn script aborts with a message about isdnlog.
+ The isdnlog program emits error messages or just doesn't work.
Solution:
- Try deactivating (or commenting out) isdnlog. This driver does not
- support it.
+ Isdnlog supports only the HiSax driver. Do not attempt to use it with
+ other drivers such as Gigaset.
Problem:
You have two or more DECT data adapters (M101/M105) and only the
@@ -321,8 +321,8 @@ GigaSet 307x Device Driver
writing an appropriate value to /sys/module/gigaset/parameters/debug, e.g.
echo 0 > /sys/module/gigaset/parameters/debug
switches off debugging output completely,
- echo 0x10a020 > /sys/module/gigaset/parameters/debug
- enables the standard set of debugging output messages. These values are
+ echo 0x302020 > /sys/module/gigaset/parameters/debug
+ enables a reasonable set of debugging output messages. These values are
bit patterns where every bit controls a certain type of debugging output.
See the constants DEBUG_* in the source file gigaset.h for details.
--
1.6.5.3.298.g39add
^ permalink raw reply related
* [PATCH 0/4] gigaset: patches for 2.6.34
From: Tilman Schmidt @ 2010-02-22 23:08 UTC (permalink / raw)
To: Karsten Keil, David Miller
Cc: Hansjoerg Lipp, Karsten Keil, isdn4linux, i4ldeveloper, netdev,
linux-kernel
Karsten, David,
following are four patches to the Gigaset driver I'd like you to
forward for merging into kernel release 2.6.34.
Thanks,
Tilman
Andy Shevchenko (1):
isdn: remove ishexdigit() in regard to isxdigit()
Tilman Schmidt (3):
gigaset: small documentation improvement
bas_gigaset: collapse CR/LF at end of AT response
gigaset: reduce syslog clutter
Documentation/isdn/README.gigaset | 10 ++---
drivers/isdn/gigaset/asyncdata.c | 6 ++-
drivers/isdn/gigaset/bas-gigaset.c | 18 +++-------
drivers/isdn/gigaset/capi.c | 31 ++----------------
drivers/isdn/gigaset/common.c | 49 +++++++++++-----------------
drivers/isdn/gigaset/ev-layer.c | 63 ++++++++++++++++++-------------------
drivers/isdn/gigaset/gigaset.h | 11 ++----
drivers/isdn/gigaset/i4l.c | 52 +++---------------------------
drivers/isdn/gigaset/interface.c | 12 ++-----
drivers/isdn/gigaset/isocdata.c | 44 ++++++++++++++++++-------
drivers/isdn/gigaset/proc.c | 2 -
drivers/isdn/gigaset/usb-gigaset.c | 2 -
^ permalink raw reply
* Re: [PATCH]: tipc: Fix oops on send prior to entering networked mode
From: Stephens, Allan @ 2010-02-22 22:44 UTC (permalink / raw)
To: Neil Horman, netdev; +Cc: per.liden, jon.maloy, tipc-discussion, davem
In-Reply-To: <20100219194033.GA28743@hmsreliant.think-freely.org>
Hi Neil:
Good work on spotting this bug, and in tracking down the cause. I took
a look at your patch, but there are a couple of problems I can see with
the approach you've taken to fix things:
1) The check you've added to send_msg() in net/tipc/socket.c will help
prevent things from blowing up if the message sender is using an AF_MIPC
socket from user space, but it won't help prevent a similar oops if an
"application" uses TIPC's native API to send a message directly from
kernel space.
2) The other change you made to defer setting tipc_mode to TIPC_NET_MODE
will cause problems if TIPC has to bail out during tipc_net_start()
because of problems. Specifically, the ensuing call to tipc_net_stop()
won't get a chance to clean up any partial initialization that got done
prior to the initialization problem, which could result in memory leaks.
Fortunately, I think there's a relatively easy solution. Since TIPC
always needs to call tipc_node_select() in order to send an off-node
message, you should be able to add the necessary error checking there.
I'm thinking of something along the lines of:
static inline struct tipc_node *tipc_node_select(u32 addr, u32 selector)
{
if (likely(in_own_cluster(addr)))
return tipc_local_nodes ?
tipc_local_nodes[tipc_node(addr)] : NULL;
return tipc_net->zones ? tipc_node_select_next_hop(addr,
selector) : NULL;
}
Please give this a try and let us know how things go.
Regards,
Al
> -----Original Message-----
> From: Neil Horman [mailto:nhorman@tuxdriver.com]
> Sent: Friday, February 19, 2010 2:41 PM
> To: netdev@vger.kernel.org
> Cc: per.liden@ericsson.com; jon.maloy@ericsson.com; Stephens,
> Allan; tipc-discussion@lists.sourceforge.net;
> davem@davemloft.net; nhorman@tuxdriver.com
> Subject: [PATCH]: tipc: Fix oops on send prior to entering
> networked mode
>
> Fix TIPC to disallow sending to remote addresses prior to
> entering NET_MODE
>
> user programs can oops the kernel by sending datagrams via
> AF_TIPC prior to entering networked mode. The following
> backtrace has been observed:
>
> ID: 13459 TASK: ffff810014640040 CPU: 0 COMMAND: "tipc-client"
> #0 [ffff81002d9a5810] crash_kexec at ffffffff800ac5b9
> #1 [ffff81002d9a58d0] __die at ffffffff80065127
> #2 [ffff81002d9a5910] do_page_fault at ffffffff80066da7
> #3 [ffff81002d9a5a00] error_exit at ffffffff8005dde9
> [exception RIP: tipc_node_select_next_hop+90]
> RIP: ffffffff8869d3c3 RSP: ffff81002d9a5ab8 RFLAGS: 00010202
> RAX: 0000000000000001 RBX: 0000000000000001 RCX: 0000000000000001
> RDX: 0000000000000000 RSI: 0000000000000001 RDI: 0000000001001001
> RBP: 0000000001001001 R8: 0074736575716552 R9: 0000000000000000
> R10: ffff81003fbd0680 R11: 00000000000000c8 R12: 0000000000000008
> R13: 0000000000000001 R14: 0000000000000001 R15: ffff810015c6ca00
> ORIG_RAX: ffffffffffffffff CS: 0010 SS: 0018
> #4 [ffff81002d9a5ab0] tipc_node_select_next_hop at ffffffff8869d3b1
> #5 [ffff81002d9a5ae0] tipc_link_send_sections_fast at ffffffff88698558
> #6 [ffff81002d9a5be0] tipc_forward2port at ffffffff8869eb1d
> #7 [ffff81002d9a5c10] tipc_send2port at ffffffff8869eb79
> #8 [ffff81002d9a5c30] send_msg at ffffffff886a1d0b
> #9 [ffff81002d9a5cb0] sock_sendmsg at ffffffff80055261
> RIP: 0000003cbd8d49a3 RSP: 00007fffc84e0be8 RFLAGS: 00010206
> RAX: 000000000000002c RBX: ffffffff8005d116 RCX: 0000000000000000
> RDX: 0000000000000008 RSI: 00007fffc84e0c00 RDI: 0000000000000003
> RBP: 0000000000000000 R8: 00007fffc84e0c10 R9: 0000000000000010
> R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
> R13: 00007fffc84e0d10 R14: 0000000000000000 R15: 00007fffc84e0c30
> ORIG_RAX: 000000000000002c CS: 0033 SS: 002b
>
> What happens is that, when the tipc module in inserted it
> enters a standalone node mode in which communication to its
> own address is allowed <0.0.0> but not to other addresses,
> since the appropriate data structures have not been allocated
> yet (specifically the tipc_net pointer). There is nothing
> stopping a client from trying to send such a message however,
> and if that happens, we attempt to dereference tipc_net.zones
> while the pointer is still NULL, and explode. The fix is to
> add a check at the start of the send_msg path to ensure that
> we've allocated the tipc_net pointers and entered networked
> mode prior to allowing a send to any destination other than
> our loopback address.
>
> This patch has received minimal testing, but fixes the issue.
> Through reviews are appreciated.
>
> Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
>
> CC: Per Liden <per.liden@ericsson.com>
> CC: Jon Maloy <jon.maloy@ericsson.com>
> CC: Allan Stephens <allan.stephens@windriver.com>
> CC: David S. Miller <davem@davemloft.net>
> CC: Neil Horman <nhorman@tuxdriver.com>
> CC: tipc-discussion@lists.sourceforge.net
>
>
> net.c | 2 +-
> socket.c | 4 ++++
> 2 files changed, 5 insertions(+), 1 deletion(-)
>
> diff --git a/net/tipc/net.c b/net/tipc/net.c index
> 7906608..512b33c 100644
> --- a/net/tipc/net.c
> +++ b/net/tipc/net.c
> @@ -278,7 +278,6 @@ int tipc_net_start(u32 addr)
> tipc_cfg_stop();
>
> tipc_own_addr = addr;
> - tipc_mode = TIPC_NET_MODE;
> tipc_named_reinit();
> tipc_port_reinit();
>
> @@ -289,6 +288,7 @@ int tipc_net_start(u32 addr)
> return res;
> }
>
> + tipc_mode = TIPC_NET_MODE;
> tipc_k_signal((Handler)tipc_subscr_start, 0);
> tipc_k_signal((Handler)tipc_cfg_init, 0);
>
> diff --git a/net/tipc/socket.c b/net/tipc/socket.c index
> 1ea64f0..45229fd 100644
> --- a/net/tipc/socket.c
> +++ b/net/tipc/socket.c
> @@ -526,6 +526,10 @@ static int send_msg(struct kiocb *iocb,
> struct socket *sock,
> if (iocb)
> lock_sock(sk);
>
> + if ((tipc_mode != TIPC_NET_MODE) &&
> + (dest->addr.name.domain != tipc_own_addr))
> + return -EHOSTUNREACH;
> +
> needs_conn = (sock->state != SS_READY);
> if (unlikely(needs_conn)) {
> if (sock->state == SS_LISTENING) {
>
------------------------------------------------------------------------------
Download Intel® Parallel Studio Eval
Try the new software tools for yourself. Speed compiling, find bugs
proactively, and fine-tune applications for parallel performance.
See why Intel Parallel Studio got high marks during beta.
http://p.sf.net/sfu/intel-sw-dev
^ permalink raw reply
* [PATCH] IPv6: better document max_addresses parameter
From: Brian Haley @ 2010-02-22 22:27 UTC (permalink / raw)
To: nedyalkor; +Cc: Andrew Morton, netdev, bugzilla-daemon, bugme-daemon
In-Reply-To: <20100222130858.3c186888.akpm@linux-foundation.org>
Andrew Morton wrote:
>> >From ip-sysctl.txt file in kernel documentation I can see following description
>> for max_addresses:
>> max_addresses - INTEGER
>> Number of maximum addresses per interface. 0 disables limitation.
>> It is recommended not set too large value (or 0) because it would
>> be too easy way to crash kernel to allow to create too much of
>> autoconfigured addresses.
^^^^^^^^^^^^^^
>> If this parameter applies only for auto-configured IP addressed, please state
>> it more clearly in docs or rename the parameter to show that it refers to
>> auto-configuration.
It did mention autoconfigured in the text, but the below makes it more obvious.
More clearly document IPv6 max_addresses parameter.
Signed-off-by: Brian Haley <brian.haley@hp.com>
---
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index 2571a62..8b72c88 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -1126,10 +1126,10 @@ regen_max_retry - INTEGER
Default: 5
max_addresses - INTEGER
- Number of maximum addresses per interface. 0 disables limitation.
- It is recommended not set too large value (or 0) because it would
- be too easy way to crash kernel to allow to create too much of
- autoconfigured addresses.
+ Maximum number of autoconfigured addresses per interface. Setting
+ to zero disables the limitation. It is not recommended to set this
+ value too large (or to zero) because it would be an easy way to
+ crash the kernel by allowing too many addresses to be created.
Default: 16
disable_ipv6 - BOOLEAN
^ permalink raw reply related
* Re: [PATCH 5/5] temac: add missing mask
From: Grant Likely @ 2010-02-22 22:25 UTC (permalink / raw)
To: gfilip; +Cc: netdev
In-Reply-To: <1266865621-28955-5-git-send-email-gfilip@ee.ethz.ch>
On Mon, Feb 22, 2010 at 12:07 PM, <gfilip@ee.ethz.ch> wrote:
> For Virtex4-FX this mask is necessary. I think this is also true for Virtex5. Please test.
>
> Signed-off-by: Filip Gospodinov <gfilip@ee.ethz.ch>
> ---
> drivers/net/ll_temac_main.c | 2 +-
> 1 files changed, 1 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/net/ll_temac_main.c b/drivers/net/ll_temac_main.c
> index 8b4e1b9..43d51b7 100644
> --- a/drivers/net/ll_temac_main.c
> +++ b/drivers/net/ll_temac_main.c
> @@ -627,7 +627,7 @@ static void ll_temac_recv(struct net_device *ndev)
> while ((bdstat & STS_CTRL_APP0_CMPLT)) {
>
> skb = lp->rx_skb[lp->rx_bd_ci];
> - length = cur_p->app4;
> + length = (cur_p->app4 & 0x3fff);
A patch making this change has already been merged into david's next tree.
g.
^ permalink raw reply
* Re: [PATCH 2/5] temac: dma init for Virtex4
From: Grant Likely @ 2010-02-22 22:23 UTC (permalink / raw)
To: gfilip; +Cc: netdev
In-Reply-To: <1266865621-28955-2-git-send-email-gfilip@ee.ethz.ch>
On Mon, Feb 22, 2010 at 12:06 PM, <gfilip@ee.ethz.ch> wrote:
> This patch belongs to a set of patches which extends the temac driver to support Virtex4-FX. It was successfully tested on the ML403 evaluation board.
>
> Signed-off-by: Filip Gospodinov <gfilip@ee.ethz.ch>
> ---
> drivers/net/ll_temac.h | 4 ++++
> drivers/net/ll_temac_main.c | 17 ++++++++++++++++-
> 2 files changed, 20 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/net/ll_temac.h b/drivers/net/ll_temac.h
> index 95dd650..51824a8 100644
> --- a/drivers/net/ll_temac.h
> +++ b/drivers/net/ll_temac.h
> @@ -383,7 +383,11 @@ struct temac_local {
>
> /* IO registers and IRQs */
> void __iomem *regs;
> +#ifdef CONFIG_XILINX_VIRTEX_4_FX
> + void __iomem *dma_regs;
> +#else
> dcr_host_t sdma_dcrs;
> +#endif
Hi Filip,
Rather than using a CONFIG symbol to select between MMIO and DCR
modes, you should look at how this patch implemented it:
http://www.gossamer-threads.com/lists/linux/kernel/1184834?page=last
John Tyner's approach is pretty good, but it needs a bit more work
before merging and he is no longer working on a virtex platform so he
isn't planning to finish it. Basically, there is no reason for the
driver to have to choose at compile time whether DCR or MMIO will be
used.
g.
> int tx_irq;
> int rx_irq;
> int emac_num;
> diff --git a/drivers/net/ll_temac_main.c b/drivers/net/ll_temac_main.c
> index a8522bd..0d4b1c5 100644
> --- a/drivers/net/ll_temac_main.c
> +++ b/drivers/net/ll_temac_main.c
> @@ -812,7 +812,6 @@ temac_of_probe(struct of_device *op, const struct of_device_id *match)
> struct net_device *ndev;
> const void *addr;
> int size, rc = 0;
> - unsigned int dcrs;
>
> /* Init network device structure */
> ndev = alloc_etherdev(sizeof(*lp));
> @@ -862,6 +861,21 @@ temac_of_probe(struct of_device *op, const struct of_device_id *match)
> goto nodev;
> }
>
> +#ifdef CONFIG_XILINX_VIRTEX_4_FX
> + unsigned int ds;
> + const u32 *dr = of_get_property(np, "reg", &ds);
> + if (dr == NULL || ds & 1 || 0 >= ds ) {
> + dev_err(&op->dev, "could not get DMA register address\n");
> + goto nodev;
> + }
> + lp->dma_regs = (u32) ioremap(*dr, 4096);
> + if (!lp->dma_regs) {
> + dev_err(&op->dev, "could not get DMA virtual address\n");
> + goto nodev;
> + }
> + dev_dbg(&op->dev, "DMA base: %x\n", lp->dma_regs);
> +#else /*CONFIG_XILINX_VIRTEX_4_FX*/
> + unsigned int dcrs;
> dcrs = dcr_resource_start(np, 0);
> if (dcrs == 0) {
> dev_err(&op->dev, "could not get DMA register address\n");
> @@ -869,6 +883,7 @@ temac_of_probe(struct of_device *op, const struct of_device_id *match)
> }
> lp->sdma_dcrs = dcr_map(np, dcrs, dcr_resource_len(np, 0));
> dev_dbg(&op->dev, "DCR base: %x\n", dcrs);
> +#endif /*CONFIG_XILINX_VIRTEX_4_FX*/'
>
> lp->rx_irq = irq_of_parse_and_map(np, 0);
> lp->tx_irq = irq_of_parse_and_map(np, 1);
> --
> 1.6.4.4
>
>
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* Re: [PATCH 1/5] temac: Add Virtex4 address mappings
From: Grant Likely @ 2010-02-22 22:10 UTC (permalink / raw)
To: gfilip; +Cc: netdev
In-Reply-To: <1266865621-28955-1-git-send-email-gfilip@ee.ethz.ch>
On Mon, Feb 22, 2010 at 12:06 PM, <gfilip@ee.ethz.ch> wrote:
> This patch belongs to a set of patches which extends the temac driver to support Virtex4-FX. It was successfully tested on the ML403 evaluation board.
>
> Signed-off-by: Filip Gospodinov <gfilip@ee.ethz.ch>
> ---
> drivers/net/ll_temac.h | 46 ++++++++++++++++++++++++++++++++++++++++++++++
> 1 files changed, 46 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/net/ll_temac.h b/drivers/net/ll_temac.h
> index 1af66a1..95dd650 100644
> --- a/drivers/net/ll_temac.h
> +++ b/drivers/net/ll_temac.h
> @@ -56,7 +56,52 @@ This option defaults to enabled (set) */
> XTE_OPTION_RXEN)
>
> /* XPS_LL_TEMAC SDMA registers definition */
> +#ifdef CONFIG_XILINX_VIRTEX_4_FX
> +#define TX_NXTDESC_PTR 0x00 /* r */
> +#define TX_CURBUF_ADDR 0x04 /* r */
> +#define TX_CURBUF_LENGTH 0x08 /* r */
> +#define TX_CURDESC_PTR 0x0C /* rw */
> +#define TX_TAILDESC_PTR 0x10 /* rw */
> +#define TX_CHNL_CTRL 0x14 /* rw */
> +#define CHNL_CTRL_IRQ_IOE (1 << 9)
> +#define CHNL_CTRL_IRQ_EN (1 << 7)
> +#define CHNL_CTRL_IRQ_ERR_EN (1 << 2)
> +#define CHNL_CTRL_IRQ_DLY_EN (1 << 1)
> +#define CHNL_CTRL_IRQ_COAL_EN (1 << 0)
> +#define TX_IRQ_REG 0x18 /* rw */
> +#define TX_CHNL_STS 0x1C /* r */
> +#define RX_NXTDESC_PTR 0x20 /* r */
> +#define RX_CURBUF_ADDR 0x24 /* r */
> +#define RX_CURBUF_LENGTH 0x28 /* r */
> +#define RX_CURDESC_PTR 0x2C /* rw */
> +#define RX_TAILDESC_PTR 0x30 /* rw */
> +#define RX_CHNL_CTRL 0x34 /* rw */
> +#define RX_IRQ_REG 0x38 /* rw */
> +#define IRQ_COAL (1 << 0)
> +#define IRQ_DLY (1 << 1)
> +#define IRQ_ERR (1 << 2)
> +#define IRQ_DMAERR (1 << 7) /* this is not documented ??? */
> +#define RX_CHNL_STS 0x3C /* r */
> +#define CHNL_STS_ENGBUSY (1 << 1)
> +#define CHNL_STS_EOP (1 << 2)
> +#define CHNL_STS_SOP (1 << 3)
> +#define CHNL_STS_CMPLT (1 << 4)
> +#define CHNL_STS_SOE (1 << 5)
> +#define CHNL_STS_IOE (1 << 6)
> +#define CHNL_STS_ERR (1 << 7)
> +
> +#define CHNL_STS_BSYWR (1 << 16)
> +#define CHNL_STS_CURPERR (1 << 17)
> +#define CHNL_STS_NXTPERR (1 << 18)
> +#define CHNL_STS_ADDRERR (1 << 19)
> +#define CHNL_STS_CMPERR (1 << 20)
> +#define CHNL_STS_TAILERR (1 << 21)
> +
> +#define DMA_CONTROL_REG 0x40 /* rw */
> +#define DMA_CONTROL_RST (1 << 0)
> +#define DMA_TAIL_ENABLE (1 << 2)
>
Oh, ugly. The register definitions are identical for virtex4 mmio
access and virtex5 DCR access. The only difference is the multiplier
(DCRs increase by 1, mmio increments by 4) to the register. Rather
than defining a whole new register block, just multiply the offset by
4 when doing an MMIO access.
g.
^ permalink raw reply
* Re: [net-next PATCH v4 1/3] net: TCP thin-stream detection
From: David Miller @ 2010-02-22 22:13 UTC (permalink / raw)
To: andi
Cc: lars.eggert, zimmermann, pavel, apetlund, netdev, ilpo.jarvinen,
eric.dumazet, Arnd.Hannemann, linux-kernel, shemminger,
william.allen.simpson, damian, ebiederm
In-Reply-To: <87k4u5mh9u.fsf@basil.nowhere.org>
From: Andi Kleen <andi@firstfloor.org>
Date: Mon, 22 Feb 2010 16:17:33 +0100
> Lars Eggert <lars.eggert@nokia.com> writes:
>
>> If it's off by default there is no issue.
>
> Still a sufficient large number of people might turn it on
> and then the network would melt.
And they can do the same by changing the initial congestion window
setting, which we also support.
The foundation of your argument is as solid as quicksand.
^ permalink raw reply
* [PATCH net-next-2.6 v3 12/12] net-caif-driver: add CAIF serial driver (ldisc)
From: sjur.brandeland @ 2010-02-22 22:05 UTC (permalink / raw)
To: netdev, davem, marcel
Cc: daniel.martensson, kaber, stefano.babic, randy.dunlap,
Sjur Braendeland
In-Reply-To: <1266876331-23988-12-git-send-email-sjur.brandeland@stericsson.com>
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
Add CAIF Serial driver. This driver is implemented as a line discipline.
Changes from PATCH v2:
- Minor cleanup, removing unused members and includes.
caif_serial uses the following module parameters:
ser_use_stx - specifies if STart of frame eXtension is in use.
ser_loop - sets the interface in loopback mode.
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
drivers/net/Kconfig | 2 +
drivers/net/Makefile | 1 +
drivers/net/caif/Kconfig | 24 ++
drivers/net/caif/Makefile | 14 ++
drivers/net/caif/caif_serial.c | 463 ++++++++++++++++++++++++++++++++++++++++
include/linux/tty.h | 4 +-
6 files changed, 506 insertions(+), 2 deletions(-)
create mode 100644 drivers/net/caif/Kconfig
create mode 100644 drivers/net/caif/Makefile
create mode 100644 drivers/net/caif/caif_serial.c
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index dd9a09c..c2e670c 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2789,6 +2789,8 @@ source "drivers/ieee802154/Kconfig"
source "drivers/s390/net/Kconfig"
+source "drivers/net/caif/Kconfig"
+
config XEN_NETDEV_FRONTEND
tristate "Xen network device frontend driver"
depends on XEN
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index ad1346d..b7ffa35 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -285,5 +285,6 @@ obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
obj-$(CONFIG_SFC) += sfc/
obj-$(CONFIG_WIMAX) += wimax/
+obj-$(CONFIG_CAIF) += caif/
obj-$(CONFIG_OCTEON_MGMT_ETHERNET) += octeon/
diff --git a/drivers/net/caif/Kconfig b/drivers/net/caif/Kconfig
new file mode 100644
index 0000000..504b90a
--- /dev/null
+++ b/drivers/net/caif/Kconfig
@@ -0,0 +1,24 @@
+#
+# CAIF physical drivers
+#
+
+if CAIF
+
+comment "CAIF transport drivers"
+
+config CAIF_TTY
+ tristate "CAIF TTY transport driver"
+ default n
+ ---help---
+ The CAIF TTY transport driver.
+
+if CAIF_TTY
+config CAIF_TTY_NOFCS
+ bool "Don't use checksum for CAIF TTY transport driver"
+ default n
+ ---help---
+ If the CAIF transport driver is reliable checksumming is
+ not needed.
+endif # CAIF_TTY
+
+endif # CAIF
diff --git a/drivers/net/caif/Makefile b/drivers/net/caif/Makefile
new file mode 100644
index 0000000..01784a0
--- /dev/null
+++ b/drivers/net/caif/Makefile
@@ -0,0 +1,14 @@
+ifeq ($(CONFIG_CAIF_DEBUG),1)
+CAIF_DBG_FLAGS := -DDEBUG
+endif
+
+KBUILD_EXTRA_SYMBOLS=net/caif/Module.symvers
+
+ccflags-y := $(CAIF_FLAGS) $(CAIF_DBG_FLAGS)
+clean-dirs:= .tmp_versions
+clean-files:= Module.symvers modules.order *.cmd *~ \
+
+# Serial interface
+obj-$(CONFIG_CAIF_TTY) += caif_serial.o
+
+
diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c
new file mode 100644
index 0000000..e5b19fa
--- /dev/null
+++ b/drivers/net/caif/caif_serial.c
@@ -0,0 +1,463 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland / sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/tty.h>
+#include <linux/file.h>
+#include <linux/if_arp.h>
+#include <net/caif/caif_device.h>
+#include <net/caif/cfcnfg.h>
+#include <linux/err.h>
+#include <linux/debugfs.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sjur Brendeland<sjur.brandeland@stericsson.com>");
+MODULE_DESCRIPTION("CAIF serial device TTY line discipline");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_LDISC(N_CAIF);
+
+
+#define CAIF_SENDING 1 /* Bit 1 = 0x02*/
+#define CAIF_UART_TX_COMPLETED 2 /* Bit 2 = 0x04 */
+#define CAIF_FLOW_OFF_SENT 4 /* Bit 4 = 0x10 */
+
+#define MAX_WRITE_CHUNK 4096
+#define ON 1
+#define OFF 0
+#define CAIF_MAX_MTU 4096
+
+
+/*This list is protected by the rtnl lock. */
+static LIST_HEAD(ser_list);
+
+static int ser_loop;
+module_param(ser_loop, bool, S_IRUGO);
+MODULE_PARM_DESC(ser_loop, "Run in simulated loopback mode.");
+
+static int ser_use_stx = 1;
+module_param(ser_use_stx, bool, S_IRUGO);
+MODULE_PARM_DESC(ser_use_stx, "STX enabled or not.");
+
+static int ser_use_fcs = 1;
+
+module_param(ser_use_fcs, bool, S_IRUGO);
+MODULE_PARM_DESC(ser_use_fcs, "FCS enabled or not.");
+
+static int ser_write_chunk = MAX_WRITE_CHUNK;
+module_param(ser_write_chunk, int, S_IRUGO);
+
+MODULE_PARM_DESC(ser_write_chunk, "Maximum size of data written to UART.");
+
+static struct dentry *debugfsdir;
+
+static int caif_net_open(struct net_device *dev);
+static int caif_net_close(struct net_device *dev);
+
+struct ser_device {
+ struct caif_dev_common common;
+ struct list_head node;
+ struct net_device *dev;
+ struct sk_buff_head head;
+ struct tty_struct *tty;
+ bool tx_started;
+ unsigned long state;
+ char *tty_name;
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs_tty_dir;
+ struct debugfs_blob_wrapper tx_blob;
+ struct debugfs_blob_wrapper rx_blob;
+ u8 rx_data[128];
+ u8 tx_data[128];
+ u8 tty_status;
+
+#endif
+};
+
+static int ser_phy_tx(struct ser_device *ser, struct sk_buff *skb);
+static void caifdev_setup(struct net_device *dev);
+static void ldisc_tx_wakeup(struct tty_struct *tty);
+#ifdef CONFIG_DEBUG_FS
+static inline void update_tty_status(struct ser_device *ser)
+{
+ ser->tty_status =
+ ser->tty->stopped << 5 |
+ ser->tty->hw_stopped << 4 |
+ ser->tty->flow_stopped << 3 |
+ ser->tty->packet << 2 |
+ ser->tty->low_latency << 1 |
+ ser->tty->warned;
+}
+static inline void debugfs_init(struct ser_device *ser, struct tty_struct *tty)
+{
+ ser->debugfs_tty_dir =
+ debugfs_create_dir(tty->name, debugfsdir);
+ if (!IS_ERR(ser->debugfs_tty_dir)) {
+ debugfs_create_blob("last_tx_msg", S_IRUSR,
+ ser->debugfs_tty_dir,
+ &ser->tx_blob);
+
+ debugfs_create_blob("last_rx_msg", S_IRUSR,
+ ser->debugfs_tty_dir,
+ &ser->rx_blob);
+
+ debugfs_create_x32("ser_state", S_IRUSR,
+ ser->debugfs_tty_dir,
+ (u32 *)&ser->state);
+
+ debugfs_create_x8("tty_status", S_IRUSR,
+ ser->debugfs_tty_dir,
+ &ser->tty_status);
+
+ }
+ ser->tx_blob.data = ser->tx_data;
+ ser->tx_blob.size = 0;
+ ser->rx_blob.data = ser->rx_data;
+ ser->rx_blob.size = 0;
+}
+
+static inline void debugfs_deinit(struct ser_device *ser)
+{
+ debugfs_remove_recursive(ser->debugfs_tty_dir);
+}
+
+static inline void debugfs_rx(struct ser_device *ser, const u8 *data, int size)
+{
+ if (size > sizeof(ser->rx_data))
+ size = sizeof(ser->rx_data);
+ memcpy(ser->rx_data, data, size);
+ ser->rx_blob.data = ser->rx_data;
+ ser->rx_blob.size = size;
+}
+
+static inline void debugfs_tx(struct ser_device *ser, const u8 *data, int size)
+{
+ if (size > sizeof(ser->tx_data))
+ size = sizeof(ser->tx_data);
+ memcpy(ser->tx_data, data, size);
+ ser->tx_blob.data = ser->tx_data;
+ ser->tx_blob.size = size;
+}
+#else
+static inline void debugfs_init(struct ser_device *ser, struct tty_struct *tty)
+{
+}
+
+static inline void debugfs_deinit(struct ser_device *ser)
+{
+}
+
+static inline void update_tty_status(struct ser_device *ser)
+{
+}
+
+static inline void debugfs_rx(struct ser_device *ser, const u8 *data, int size)
+{
+}
+
+static inline void debugfs_tx(struct ser_device *ser, const u8 *data, int size)
+{
+}
+
+#endif
+
+static void ldisc_receive(struct tty_struct *tty, const u8 *data,
+ char *flags, int count)
+{
+ struct sk_buff *skb = NULL;
+ struct ser_device *ser;
+ int ret;
+ u8 *p;
+ ser = tty->disc_data;
+
+ /*
+ * Workaround for garbage at start of transmission,
+ * only enable if STX handling is not enables
+ */
+ if (!ser->common.use_stx && !ser->tx_started) {
+ dev_info(&ser->dev->dev,
+ "Bytes received before initial transmission -"
+ "bytes discarded.\n");
+ return;
+ }
+
+ BUG_ON(ser->dev == NULL);
+
+ /* Get a suitable caif packet and copy in data. */
+ skb = netdev_alloc_skb(ser->dev, count+1);
+ BUG_ON(skb == NULL);
+ p = skb_put(skb, count);
+ memcpy(p, data, count);
+
+ skb->protocol = htons(ETH_P_CAIF);
+ skb_reset_mac_header(skb);
+ skb->dev = ser->dev;
+ debugfs_rx(ser, data, count);
+ /* Push received packet up the stack. */
+ ret = netif_rx(skb);
+ if (!ret) {
+ ser->dev->stats.rx_packets++;
+ ser->dev->stats.rx_bytes += count;
+ } else
+ ++ser->dev->stats.rx_dropped;
+ update_tty_status(ser);
+}
+
+static int handle_tx(struct ser_device *ser)
+{
+ struct tty_struct *tty;
+ struct sk_buff *skb;
+ char *buf;
+ int tty_wr, len, room, pktlen;
+ tty = ser->tty;
+
+ /*
+ * NOTE: This workaround is not really needed when STX is enabled.
+ * Remove?
+ */
+ if (ser->tx_started == false)
+ ser->tx_started = true;
+
+ if (test_and_set_bit(CAIF_SENDING, &ser->state)) {
+ set_bit(CAIF_UART_TX_COMPLETED, &ser->state);
+ return 0;
+ }
+
+ do {
+ skb = skb_peek(&ser->head);
+ if (skb != NULL && skb->len == 0) {
+ struct sk_buff *tmp;
+ tmp = skb_dequeue(&ser->head);
+ BUG_ON(tmp != skb);
+ kfree_skb(skb);
+ skb = skb_peek(&ser->head);
+ }
+
+ if (skb == NULL) {
+ if (test_and_clear_bit(
+ CAIF_FLOW_OFF_SENT,
+ &ser->state)) {
+ if (ser->common.flowctrl != NULL)
+ ser->common.flowctrl(ser->dev, ON);
+ }
+ break;
+ }
+
+
+ buf = skb->data;
+ pktlen = len = skb->len;
+
+ clear_bit(CAIF_UART_TX_COMPLETED, &ser->state);
+ room = tty_write_room(tty);
+ if (room > ser_write_chunk)
+ room = ser_write_chunk;
+
+ if (len > room)
+ len = room;
+ debugfs_tx(ser, buf, len);
+ if (!ser_loop) {
+ tty_wr = tty->ops->write(tty, buf, len);
+ } else {
+ tty_wr = len;
+ ldisc_receive(tty, buf, 0, len);
+ }
+ ser->dev->stats.tx_packets++;
+ ser->dev->stats.tx_bytes += tty_wr;
+ if (tty_wr > 0)
+ skb_pull(skb, tty_wr);
+
+ if (ser_loop)
+ ldisc_tx_wakeup(tty);
+ update_tty_status(ser);
+
+ } while (test_bit(CAIF_UART_TX_COMPLETED, &(ser->state)));
+
+ clear_bit(CAIF_SENDING, &ser->state);
+ return 0;
+}
+
+static int ser_phy_tx(struct ser_device *ser, struct sk_buff *skb)
+{
+ if (skb_peek(&ser->head) != NULL) {
+ if (!test_and_set_bit(CAIF_FLOW_OFF_SENT, &ser->state)
+ && ser->common.flowctrl != NULL)
+ ser->common.flowctrl(ser->dev, OFF);
+ }
+ skb_queue_tail(&ser->head, skb);
+ if (!test_bit(CAIF_SENDING, &ser->state))
+ handle_tx(ser);
+ update_tty_status(ser);
+ return 0;
+}
+
+static int caif_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ser_device *ser;
+ if (!dev)
+ return -EINVAL;
+ ser = netdev_priv(dev);
+ return ser_phy_tx(ser, skb);
+}
+
+
+static void ldisc_tx_wakeup(struct tty_struct *tty)
+{
+ struct ser_device *ser;
+ ser = tty->disc_data;
+ if (ser == NULL)
+ return;
+ set_bit(CAIF_UART_TX_COMPLETED, &ser->state);
+ if (ser->tty != tty)
+ return;
+ handle_tx(ser);
+}
+
+
+static int ldisc_open(struct tty_struct *tty)
+{
+ struct ser_device *ser;
+ struct net_device *dev;
+ char name[64];
+ int result;
+
+ sprintf(name, "caif_%s", tty->name);
+
+ dev = alloc_netdev(sizeof(*ser), name, caifdev_setup);
+
+
+ ser = netdev_priv(dev);
+ ser->tty = tty;
+ ser->dev = dev;
+ debugfs_init(ser, tty);
+ tty->receive_room = N_TTY_BUF_SIZE;
+ tty->disc_data = ser;
+
+ set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+ rtnl_lock();
+ result = register_netdevice(dev);
+ if (result) {
+ rtnl_unlock();
+ free_netdev(dev);
+ return -ENODEV;
+ }
+
+ list_add(&ser->node, &ser_list);
+ rtnl_unlock();
+ netif_stop_queue(dev);
+ update_tty_status(ser);
+ return 0;
+}
+
+static void ldisc_close(struct tty_struct *tty)
+{
+ struct ser_device *ser = tty->disc_data;
+ /* Remove may be called inside or outside of rtnl_lock */
+ int islocked = rtnl_is_locked();
+ if (!islocked)
+ rtnl_lock();
+ /* device is freed automagically by net-sysfs */
+ dev_close(ser->dev);
+ unregister_netdevice(ser->dev);
+ list_del(&ser->node);
+ debugfs_deinit(ser);
+ if (!islocked)
+ rtnl_unlock();
+}
+
+/* The line discipline structure. */
+static struct tty_ldisc_ops caif_ldisc = {
+ .owner = THIS_MODULE,
+ .magic = TTY_LDISC_MAGIC,
+ .name = "n_caif",
+ .open = ldisc_open,
+ .close = ldisc_close,
+ .receive_buf = ldisc_receive,
+ .write_wakeup = ldisc_tx_wakeup
+};
+
+static int register_ldisc(void)
+{
+ int result;
+ result = tty_register_ldisc(N_CAIF, &caif_ldisc);
+ if (result < 0) {
+ pr_err("cannot register CAIF ldisc=%d err=%d\n", N_CAIF,
+ result);
+ return result;
+ }
+ return result;
+}
+
+static const struct net_device_ops netdev_ops = {
+ .ndo_open = caif_net_open,
+ .ndo_stop = caif_net_close,
+ .ndo_start_xmit = caif_xmit
+};
+static void caifdev_setup(struct net_device *dev)
+{
+ struct ser_device *serdev = netdev_priv(dev);
+ dev->features = 0;
+
+ dev->netdev_ops = &netdev_ops;
+
+ dev->type = ARPHRD_CAIF;
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_POINTOPOINT;
+ dev->mtu = CAIF_MAX_MTU;
+ dev->hard_header_len = CAIF_NEEDED_HEADROOM;
+ dev->tx_queue_len = 0;
+ dev->destructor = free_netdev;
+ skb_queue_head_init(&serdev->head);
+ serdev->common.link_select = CAIF_LINK_LOW_LATENCY;
+ serdev->common.use_frag = true;
+ serdev->common.use_stx = ser_use_stx;
+ serdev->common.use_fcs = ser_use_fcs;
+ serdev->dev = dev;
+}
+
+static int caif_net_open(struct net_device *dev)
+{
+ struct ser_device *ser;
+ ser = netdev_priv(dev);
+ netif_wake_queue(dev);
+ return 0;
+}
+
+static int caif_net_close(struct net_device *dev)
+{
+ netif_stop_queue(dev);
+ return 0;
+}
+
+static int __init caif_ser_init(void)
+{
+ int ret;
+ ret = register_ldisc();
+ debugfsdir = debugfs_create_dir("caif_serial", NULL);
+ return ret;
+}
+
+static void __exit caif_ser_exit(void)
+{
+ struct ser_device *ser = NULL;
+ struct list_head *node;
+ struct list_head *_tmp;
+ list_for_each_safe(node, _tmp, &ser_list) {
+ ser = list_entry(node, struct ser_device, node);
+ dev_close(ser->dev);
+ unregister_netdevice(ser->dev);
+ list_del(node);
+ }
+ tty_unregister_ldisc(N_CAIF);
+ debugfs_remove_recursive(debugfsdir);
+}
+
+module_init(caif_ser_init);
+module_exit(caif_ser_exit);
diff --git a/include/linux/tty.h b/include/linux/tty.h
index ef3a294..5dd674b 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -23,7 +23,7 @@
*/
#define NR_UNIX98_PTY_DEFAULT 4096 /* Default maximum for Unix98 ptys */
#define NR_UNIX98_PTY_MAX (1 << MINORBITS) /* Absolute limit */
-#define NR_LDISCS 20
+#define NR_LDISCS 21
/* line disciplines */
#define N_TTY 0
@@ -46,8 +46,8 @@
#define N_GIGASET_M101 16 /* Siemens Gigaset M101 serial DECT adapter */
#define N_SLCAN 17 /* Serial / USB serial CAN Adaptors */
#define N_PPS 18 /* Pulse per Second */
-
#define N_V253 19 /* Codec control over voice modem */
+#define N_CAIF 20 /* CAIF protocol for talking to modems */
/*
* This character is the same as _POSIX_VDISABLE: it cannot be used as
--
1.6.3.3
^ permalink raw reply related
* [PATCH net-next-2.6 v3 11/12] net-caif: add CAIF documentation
From: sjur.brandeland @ 2010-02-22 22:05 UTC (permalink / raw)
To: netdev, davem, marcel
Cc: daniel.martensson, kaber, stefano.babic, randy.dunlap,
Sjur Braendeland
In-Reply-To: <1266876331-23988-11-git-send-email-sjur.brandeland@stericsson.com>
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
Documentation of the CAIF Protocol.
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
Documentation/networking/caif/Linux-CAIF.txt | 212 ++++++++++++++++++++++++++
Documentation/networking/caif/README | 110 +++++++++++++
2 files changed, 322 insertions(+), 0 deletions(-)
diff --git a/Documentation/networking/caif/Linux-CAIF.txt b/Documentation/networking/caif/Linux-CAIF.txt
new file mode 100644
index 0000000..da57f16
--- /dev/null
+++ b/Documentation/networking/caif/Linux-CAIF.txt
@@ -0,0 +1,212 @@
+Linux CAIF
+===========
+copyright (C) ST-Ericsson AB 2010
+Author: Sjur Brendeland/ sjur.brandeland@stericsson.com
+License terms: GNU General Public License (GPL) version 2
+
+
+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 Loopback are available for Linux.
+
+
+Architecture:
+------------
+The implementation of CAIF is divided into:
+* CAIF Socket Layer, Kernel API, and Net Device.
+* CAIF Core Protocol Implementation
+* CAIF Link Layer, implemented as NET devices.
+
+
+ RTNL
+ !
+ ! +------+ +------+ +------+
+ ! +------+! +------+! +------+!
+ ! ! Sock !! !Kernel!! ! Net !!
+ ! ! API !+ ! API !+ ! Dev !+ <- CAIF Client APIs
+ ! +------+ +------! +------+
+ ! ! ! !
+ ! +----------!----------+
+ ! +------+ <- CAIF Protocol Implementation
+ +-------> ! CAIF !
+ ! Core !
+ +------+
+ +--------!--------+
+ ! !
+ +------+ +-----+
+ ! ! ! TTY ! <- Link Layer (Net Devices)
+ +------+ +-----+
+
+
+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 gives a CAIF packet as a SKB. The control
+callback will
+notify of 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
+===========================
+===========================
+
+CAIF Core Protocol Layer
+=========================================
+
+CAIF Core layer implements the CAIF protocol as defined by ST-Ericsson.
+It 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 Core CAIF implementation contains:
+ - Simple implementation of CAIF.
+ - Layered architecture (a la Streams), each layer in the CAIF
+ specification is implemented in a separate c-file.
+ - Clients must implement PHY layer to access physical HW
+ with receive and transmit functions.
+ - Clients must call configuration function to add PHY layer.
+ - Clients must implement CAIF layer to consume/produce
+ CAIF payload with receive and transmit functions.
+ - Clients must call configuration function to add and connect the
+ Client layer.
+ - When receiving / transmitting CAIF Packets (cfpkt), ownership is passed
+ to the called function (except for framing layers' receive functions
+ or if a transmit function returns an error, in which case the caller
+ must free the packet).
+
+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 and adding content
+ and for 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 provides a 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. Also matches request and
+ response messages.
+
+ - CFSERVL General CAIF Service Layer functionality; handles flow
+ control and remote shutdown requests.
+
+ - CFVEI CAIF VEI layer. Handles CAIF AT Channels on VEI (Virtual
+ External Interface). This layer encodes/decodes VEI frames.
+
+ - CFDGML CAIF Datagram layer. Handles CAIF Datagram layer (IP
+ traffic), encodes/decodes Datagram frames.
+
+ - CFMUX CAIF Mux layer. Handles multiplexing between multiple
+ physical bearers and multiple channels such as VEI, Datagram, etc.
+ The MUX keeps 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.
+
+
+
+ +---------+
+ | Config |
+ | CFCNFG |
+ +---------+
+ !
+ +---------+ +---------+ +---------+
+ | AT | | Control | | Datagram|
+ | CFVEIL | | CFCTRL | | CFDGML |
+ +---------+ +---------+ +---------+
+ \_____________!______________/
+ !
+ +---------+
+ | MUX |
+ | |
+ +---------+
+ _____!_____
+ / \
+ +---------+ +---------+
+ | CFFRML | | CFFRML |
+ | Framing | | Framing |
+ +---------+ +---------+
+ ! !
+ +---------+ +---------+
+ | | | Serial |
+ | | | CFSERL |
+ +---------+ +---------+
+
+
+In this layered approach the following "rules" apply.
+ - All layers embed the same structure "struct layer"
+ - A layer does not depend on any other layer's 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 order to send data downwards, each layer should do
+ layer->dn->transmit(layer->dn, packet);
+
+
+Linux Driver Implementation
+===========================
+
+Linux GPRS Net Device and CAIF socket are implemented on top of the
+CAIF Core protocol. The Net device and CAIF socket have an instance of
+'struct layer', just like the CAIF Core protocol stack.
+Net device and Socket implement the 'receive()' function defined by
+'struct layer', just like the rest of the CAIF stack. In this way, transmit and
+receive of packets is handled as by the rest of the layers: the 'dn->transmit()'
+function is called in order to transmit data.
+
+The layer on top of the CAIF Core implementation is
+sometimes referred to as the "Client layer".
+
+
+Configuration of Link Layer
+---------------------------
+The Link Layer is implemented as Linux net devices (struct net_device).
+Payload handling and registration is done using standard Linux mechanisms.
+
+The CAIF Protocol relies on a loss-less link layer without implementing
+retransmission. This implies that packet drops must not happen.
+Therefore a flow-control mechanism is implemented where the physical
+interface can initiate flow stop for all CAIF Channels.
diff --git a/Documentation/networking/caif/README b/Documentation/networking/caif/README
new file mode 100644
index 0000000..f11cea3
--- /dev/null
+++ b/Documentation/networking/caif/README
@@ -0,0 +1,110 @@
+Copyright (C) ST-Ericsson AB 2010
+Author: Sjur Brendeland/ sjur.brandeland@stericsson.com
+License terms: GNU General Public License (GPL) version 2
+
+=== Start ===
+If you have compiled CAIF for modules do:
+
+$modprobe crc_ccitt
+$modprobe caif
+$modprobe caif_socket
+$modprobe chnl_net
+
+
+=== Preparing the setup with a STE modem ===
+
+If you are working on integration of CAIF you should 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
+set up 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 to install the N_CAIF ldisc from user space.
+The benefit is that we can hook up to any TTY.
+
+The use of Start-of-frame-extension (STX) must also be set as
+module parameter "ser_use_stx".
+
+Normally Frame Checksum is always used on UART, but this is also provided as a
+module parameter "ser_use_fcs".
+
+$ modprobe caif_serial ser_ttyname=/dev/ttyS0 ser_use_stx=yes
+$ ifconfig caif_ttyS0 up
+
+PLEASE NOTE: There is a limitation in Android shell.
+ It only accepts one argument to insmod/modprobe!
+
+=== Trouble shooting ===
+
+There are debugfs parameters provided for serial communication.
+/sys/kernel/debug/caif_serial/<tty-name>/
+
+* ser_state: Prints the bit-mask status where
+ - 0x02 means SENDING, this is a transient state.
+ - 0x04 means TX_COMPLETE, i.e. a frame has been sent by the tty
+ - 0x10 means FLOW_OFF_SENT, i.e. the previous frame has not been sent
+ and is blocking further send operation. Flow OFF has been propagated
+ to all CAIF Channels using this TTY.
+
+* tty_status: Prints the bit-mask tty status information
+ - 0x01 - tty->warned is on.
+ - 0x02 - tty->low_latency is on.
+ - 0x04 - tty->packed is on.
+ - 0x08 - tty->flow_stopped is on.
+ - 0x10 - tty->hw_stopped is on.
+ - 0x20 - tty->stopped is on.
+
+* last_tx_msg: Binary blob Prints the last transmitted frame.
+ This can be printed with
+ $od --format=x1 /sys/kernel/debug/caif_serial/<tty>/last_rx_msg.
+ The first two tx messages sent look like this. Note: The initial
+ byte 02 is start of frame extension (STX) used for re-syncing
+ upon errors.
+
+ - Enumeration:
+ 0000000 02 05 00 00 03 01 d2 02
+ | | | | | |
+ STX(1) | | | |
+ Length(2)| | |
+ Control Channel(1)
+ Command:Enumeration(1)
+ Link-ID(1)
+ Checksum(2)
+ - Channel Setup:
+ 0000000 02 07 00 00 00 21 a1 00 48 df
+ | | | | | | | |
+ STX(1) | | | | | |
+ Length(2)| | | | |
+ Control Channel(1)
+ Command:Channel Setup(1)
+ Channel Type(1)
+ Priority and Link-ID(1)
+ Endpoint(1)
+ Checksum(2)
+
+* last_rx_msg: Prints the last transmitted frame.
+ The RX messages for LinkSetup look almost identical but they have the
+ bit 0x20 set in the command bit, and Channel Setup has added one byte
+ before Checksum containing Channel ID.
+ NOTE: Several CAIF Messages might be concatenated. The maximum debug
+ buffer size is 128 bytes.
+
+== Error Scenarios:
+- last_tx_msg contains channel setup message and last_rx_msg is empty ->
+ The host seems to be able to send over the UART, at least the CAIF ldisc get
+ notified that sending is completed.
+
+- last_tx_msg contains enumeration message and last_rx_msg is empty ->
+ The host is not able to send the message from UART, the tty has not been
+ able to complete the transmit operation.
+
+- if /sys/kernel/debug/caif_serial/<tty>/tty_status is non-zero there
+ might be problems transmitting over UART.
+ E.g. host and modem wiring is not correct you will typically see
+ tty_status = 0x10 (hw_stopped) and ser_state = 0x10 (FLOW_OFF_SENT).
+ You will probably see the enumeration message in last_tx_message
+ and empty last_rx_message.
+
--
1.6.3.3
^ permalink raw reply related
* [PATCH net-next-2.6 v3 10/12] net-caif: add CAIF Kconfig and Makefiles
From: sjur.brandeland @ 2010-02-22 22:05 UTC (permalink / raw)
To: netdev, davem, marcel
Cc: daniel.martensson, kaber, stefano.babic, randy.dunlap,
Sjur Braendeland
In-Reply-To: <1266876331-23988-10-git-send-email-sjur.brandeland@stericsson.com>
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
Kconfig and Makefiles with options for:
CAIF: Including caif
CAIF_DEBUG: CAIF Debug
CAIF_SOCK: CAIF Socket Implementation
CAIF_NETDEV: CAIF Network Device for GPRS Contexts
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
net/Kconfig | 2 ++
net/Makefile | 1 +
net/caif/Kconfig | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
net/caif/Makefile | 28 ++++++++++++++++++++++++++++
4 files changed, 79 insertions(+), 0 deletions(-)
diff --git a/net/Kconfig b/net/Kconfig
index 041c35e..9342f7b 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -275,5 +275,7 @@ source "net/wimax/Kconfig"
source "net/rfkill/Kconfig"
source "net/9p/Kconfig"
+source "net/caif/Kconfig"
+
endif # if NET
diff --git a/net/Makefile b/net/Makefile
index 1542e72..a5eae27 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -56,6 +56,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
diff --git a/net/caif/Kconfig b/net/caif/Kconfig
new file mode 100644
index 0000000..65a926d
--- /dev/null
+++ b/net/caif/Kconfig
@@ -0,0 +1,48 @@
+#
+# CAIF net configurations
+#
+
+#menu "CAIF Support"
+comment "CAIF Support"
+menuconfig CAIF
+ tristate "Enable CAIF support"
+ select CRC_CCITT
+ default n
+ ---help---
+ The "Communication CPU to Application CPU Interface" (CAIF) is a packet
+ based connection-oriented MUX protocol developed by ST-Ericsson for use
+ with its modems. It is accessed from user space as sockets (PF_CAIF).
+
+ Say Y (or M) here if you build for a phone product (e.g. Android) that
+ uses CAIF as transport, if unsure say N.
+
+ If you select to build it as module then CAIF_SOCK and CAIF_NETDEV also
+ needs to be built as modules. You will also need to say yes to any CAIF
+ physical devices that your platform requires.
+
+ See Documentation/networking/caif for a further explanation on how to
+ use and configure CAIF.
+
+if CAIF
+
+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.
+
+
+config CAIF_NETDEV
+ tristate "CAIF GPRS Network device"
+ default CAIF
+ ---help---
+ Say Y if you will be using a CAIF based GPRS network device.
+ This can be either built-in or a loadable module,
+ If you select to build it as a built-in then the main CAIF device must
+ also be a built-in.
+ If unsure say Y.
+
+endif
+#endmenu
diff --git a/net/caif/Makefile b/net/caif/Makefile
new file mode 100644
index 0000000..028029d
--- /dev/null
+++ b/net/caif/Makefile
@@ -0,0 +1,28 @@
+ifeq ($(CONFIG_CAIF_DEBUG),1)
+CAIF_DBG_FLAGS := -DDEBUG
+endif
+
+ccflags-y := $(CAIF_FLAGS) $(CAIF_DBG_FLAGS)
+
+caif-objs := caif_dev.o \
+ cfcnfg.o cfmuxl.o cfctrl.o \
+ cffrml.o cfveil.o cfdbgl.o\
+ cfserl.o cfdgml.o \
+ cfrfml.o cfvidl.o cfutill.o \
+ cfsrvl.o cfpkt_skbuff.o caif_config_util.o
+
+clean-dirs:= .tmp_versions
+
+clean-files:= \
+ Module.symvers \
+ modules.order \
+ *.cmd \
+ *.o \
+ *~
+
+obj-$(CONFIG_CAIF) += caif.o
+obj-$(CONFIG_CAIF_NETDEV) += chnl_net.o
+obj-$(CONFIG_CAIF) += caif_socket.o
+
+export-objs := caif.o
+
--
1.6.3.3
^ permalink raw reply related
* [PATCH net-next-2.6 v3 09/12] net-caif: add CAIF netdevice
From: sjur.brandeland @ 2010-02-22 22:05 UTC (permalink / raw)
To: netdev, davem, marcel
Cc: daniel.martensson, kaber, stefano.babic, randy.dunlap,
Sjur Braendeland
In-Reply-To: <1266876331-23988-9-git-send-email-sjur.brandeland@stericsson.com>
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
Changes from PATCHv2:
- Use socket address types when creating channels.
Adding GPRS Net Device for PDP Contexts.
The device can be managed by IOCTLs defined in if_caif.h and
RTNL.
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
net/caif/chnl_net.c | 429 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 429 insertions(+), 0 deletions(-)
diff --git a/net/caif/chnl_net.c b/net/caif/chnl_net.c
new file mode 100644
index 0000000..ae37fd2
--- /dev/null
+++ b/net/caif/chnl_net.c
@@ -0,0 +1,429 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Authors: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * Daniel Martensson / Daniel.Martensson@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/version.h>
+#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>
+#include <linux/sched.h>
+#include <linux/sockios.h>
+#include <linux/caif/if_caif.h>
+#include <net/rtnetlink.h>
+#include <net/caif/caif_layer.h>
+#include <net/caif/cfcnfg.h>
+#include <net/caif/cfpkt.h>
+#include <net/caif/caif_dev.h>
+
+#define CAIF_CONNECT_TIMEOUT 30
+#define SIZE_MTU 1500
+#define SIZE_MTU_MAX 4080
+#define SIZE_MTU_MIN 68
+#define CAIF_NET_DEFAULT_QUEUE_LEN 500
+
+#undef pr_debug
+#define pr_debug pr_warning
+
+/*This list is protected by the rtnl lock. */
+static LIST_HEAD(chnl_net_list);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_RTNL_LINK("caif");
+
+struct chnl_net {
+ struct layer chnl;
+ struct net_device_stats stats;
+ struct caif_connect_request conn_req;
+ struct list_head list_field;
+ struct net_device *netdev;
+ char name[256];
+ wait_queue_head_t netmgmt_wq;
+ /* Flow status to remember and control the transmission. */
+ bool flowenabled;
+};
+
+static void robust_list_del(struct list_head *delete_node)
+{
+ struct list_head *list_node;
+ struct list_head *n;
+ ASSERT_RTNL();
+ list_for_each_safe(list_node, n, &chnl_net_list) {
+ if (list_node == delete_node) {
+ list_del(list_node);
+ break;
+ }
+ }
+}
+
+static int chnl_recv_cb(struct layer *layr, struct cfpkt *pkt)
+{
+ struct sk_buff *skb;
+ struct chnl_net *priv = NULL;
+ int pktlen;
+ int err = 0;
+
+ priv = container_of(layr, struct chnl_net, chnl);
+
+ if (!priv)
+ return -EINVAL;
+
+ /* Get length of CAIF packet. */
+ pktlen = cfpkt_getlen(pkt);
+
+ skb = (struct sk_buff *) cfpkt_tonative(pkt);
+ /* Pass some minimum information and
+ * send the packet to the net stack.
+ */
+ skb->dev = priv->netdev;
+ skb->protocol = htons(ETH_P_IP);
+
+ /* If we change the header in loop mode, the checksum is corrupted. */
+ if (priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP)
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ else
+ skb->ip_summed = CHECKSUM_COMPLETE;
+
+ /* FIXME: Drivers should call this in tasklet context. */
+ if (in_interrupt())
+ netif_rx(skb);
+ else
+ netif_rx_ni(skb);
+
+ /* Update statistics. */
+ priv->netdev->stats.rx_packets++;
+ priv->netdev->stats.rx_bytes += pktlen;
+
+ return err;
+}
+
+static void chnl_flowctrl_cb(struct layer *layr, enum caif_ctrlcmd flow,
+ int phyid)
+{
+ struct chnl_net *priv = NULL;
+ pr_debug("CAIF: %s(): NET flowctrl func called flow: %s.\n",
+ __func__,
+ 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:
+ priv->flowenabled = false;
+ netif_tx_disable(priv->netdev);
+ wake_up_interruptible(&priv->netmgmt_wq);
+ break;
+ case CAIF_CTRLCMD_FLOW_ON_IND:
+ case CAIF_CTRLCMD_INIT_RSP:
+ priv->flowenabled = true;
+ netif_wake_queue(priv->netdev);
+ wake_up_interruptible(&priv->netmgmt_wq);
+ break;
+ default:
+ break;
+ }
+}
+
+static int chnl_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct chnl_net *priv;
+ struct cfpkt *pkt = NULL;
+ int len;
+ int result = -1;
+ /* Get our private data. */
+ priv = netdev_priv(dev);
+
+ if (skb->len > priv->netdev->mtu) {
+ pr_warning("CAIF: %s(): Size of skb exceeded MTU\n", __func__);
+ return -ENOSPC;
+ }
+
+ if (!priv->flowenabled) {
+ pr_debug("CAIF: %s(): dropping packets flow off\n", __func__);
+ return NETDEV_TX_BUSY;
+ }
+
+ if (priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP)
+ swap(ip_hdr(skb)->saddr, ip_hdr(skb)->daddr);
+
+ /* Store original SKB length. */
+ len = skb->len;
+
+ pkt = cfpkt_fromnative(CAIF_DIR_OUT, (void *) skb);
+
+ /* Send the packet down the stack. */
+ result = priv->chnl.dn->transmit(priv->chnl.dn, pkt);
+ if (result) {
+ if (result == -EAGAIN)
+ result = NETDEV_TX_BUSY;
+ return result;
+ }
+
+ /* Update statistics. */
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += len;
+
+ return NETDEV_TX_OK;
+}
+
+
+static int chnl_net_open(struct net_device *dev)
+{
+ struct chnl_net *priv = NULL;
+ int result = -1;
+ ASSERT_RTNL();
+
+ priv = netdev_priv(dev);
+ pr_debug("CAIF: %s(): dev name: %s\n", __func__, priv->name);
+
+ if (!priv) {
+ pr_debug("CAIF: %s(): chnl_net_open: no priv\n", __func__);
+ return -ENODEV;
+ }
+ result = caif_connect_client(&priv->conn_req, &priv->chnl);
+ if (result != 0) {
+ pr_debug("CAIF: %s(): err: "
+ "Unable to register and open device, Err:%d\n",
+ __func__,
+ result);
+ return -ENODEV;
+ }
+ result = wait_event_interruptible(priv->netmgmt_wq, priv->flowenabled);
+
+ if (result == -ERESTARTSYS) {
+ pr_debug("CAIF: %s(): wait_event_interruptible"
+ " woken by a signal\n", __func__);
+ return -ERESTARTSYS;
+ } else
+ pr_debug("CAIF: %s(): Flow on recieved\n", __func__);
+
+ return 0;
+}
+
+static int chnl_net_stop(struct net_device *dev)
+{
+ struct chnl_net *priv;
+ int result = -1;
+ ASSERT_RTNL();
+ priv = netdev_priv(dev);
+
+ result = caif_disconnect_client(&priv->chnl);
+ if (result != 0) {
+ pr_debug("CAIF: %s(): chnl_net_stop: err: "
+ "Unable to STOP device, Err:%d\n",
+ __func__, result);
+ return -EBUSY;
+ }
+ result = wait_event_interruptible(priv->netmgmt_wq,
+ !priv->flowenabled);
+
+ if (result == -ERESTARTSYS) {
+ pr_debug("CAIF: %s(): wait_event_interruptible woken by"
+ " signal, signal_pending(current) = %d\n",
+ __func__,
+ signal_pending(current));
+ } else {
+ pr_debug("CAIF: %s(): disconnect received\n", __func__);
+
+ }
+
+ return 0;
+}
+
+int chnl_net_init(struct net_device *dev)
+{
+ struct chnl_net *priv;
+ ASSERT_RTNL();
+ priv = netdev_priv(dev);
+ strncpy(priv->name, dev->name, sizeof(priv->name));
+ return 0;
+}
+
+void chnl_net_uninit(struct net_device *dev)
+{
+ struct chnl_net *priv;
+ ASSERT_RTNL();
+ priv = netdev_priv(dev);
+ robust_list_del(&priv->list_field);
+}
+
+static const struct net_device_ops netdev_ops = {
+ .ndo_open = chnl_net_open,
+ .ndo_stop = chnl_net_stop,
+ .ndo_init = chnl_net_init,
+ .ndo_uninit = chnl_net_uninit,
+ .ndo_start_xmit = chnl_net_start_xmit,
+};
+
+static void ipcaif_net_setup(struct net_device *dev)
+{
+ struct chnl_net *priv;
+ dev->netdev_ops = &netdev_ops;
+ dev->destructor = free_netdev;
+ dev->flags |= IFF_NOARP;
+ dev->flags |= IFF_POINTOPOINT;
+ dev->needed_headroom = CAIF_NEEDED_HEADROOM;
+ dev->needed_tailroom = CAIF_NEEDED_TAILROOM;
+ dev->mtu = SIZE_MTU;
+ dev->tx_queue_len = CAIF_NET_DEFAULT_QUEUE_LEN;
+
+ priv = netdev_priv(dev);
+ priv->chnl.receive = chnl_recv_cb;
+ priv->chnl.ctrlcmd = chnl_flowctrl_cb;
+ priv->netdev = dev;
+ priv->conn_req.protocol = CAIFPROTO_DATAGRAM;
+ priv->conn_req.link_selector = CAIF_LINK_HIGH_BANDW;
+ priv->conn_req.priority = CAIF_PRIO_LOW;
+ /* Insert illegal value */
+ priv->conn_req.sockaddr.u.dgm.connection_id = -1;
+ priv->flowenabled = false;
+
+ ASSERT_RTNL();
+ init_waitqueue_head(&priv->netmgmt_wq);
+ list_add(&priv->list_field, &chnl_net_list);
+}
+
+static int delete_device(struct chnl_net *dev)
+{
+ ASSERT_RTNL();
+ if (dev->netdev)
+ unregister_netdevice(dev->netdev);
+ return 0;
+}
+
+static int ipcaif_fill_info(struct sk_buff *skb, const struct net_device *dev)
+{
+ struct chnl_net *priv;
+ u8 loop;
+ priv = netdev_priv(dev);
+ NLA_PUT_U32(skb, IFLA_CAIF_IPV4_CONNID,
+ priv->conn_req.sockaddr.u.dgm.connection_id);
+ NLA_PUT_U32(skb, IFLA_CAIF_IPV6_CONNID,
+ priv->conn_req.sockaddr.u.dgm.connection_id);
+ loop = priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP;
+ NLA_PUT_U8(skb, IFLA_CAIF_LOOPBACK, loop);
+
+
+ return 0;
+nla_put_failure:
+ return -EMSGSIZE;
+
+}
+
+static void caif_netlink_parms(struct nlattr *data[],
+ struct caif_connect_request *conn_req)
+{
+ if (!data) {
+ pr_warning("CAIF: %s: no params data found\n", __func__);
+ return;
+ }
+ if (data[IFLA_CAIF_IPV4_CONNID])
+ conn_req->sockaddr.u.dgm.connection_id =
+ nla_get_u32(data[IFLA_CAIF_IPV4_CONNID]);
+ if (data[IFLA_CAIF_IPV6_CONNID])
+ conn_req->sockaddr.u.dgm.connection_id =
+ nla_get_u32(data[IFLA_CAIF_IPV6_CONNID]);
+ if (data[IFLA_CAIF_LOOPBACK]) {
+ if (nla_get_u8(data[IFLA_CAIF_LOOPBACK]))
+ conn_req->protocol = CAIFPROTO_DATAGRAM_LOOP;
+ else
+ conn_req->protocol = CAIFPROTO_DATAGRAM;
+ }
+}
+
+static int ipcaif_newlink(struct net *src_net, struct net_device *dev,
+ struct nlattr *tb[], struct nlattr *data[])
+{
+ int ret;
+ struct chnl_net *caifdev;
+ ASSERT_RTNL();
+ caifdev = netdev_priv(dev);
+ caif_netlink_parms(data, &caifdev->conn_req);
+ ret = register_netdevice(dev);
+ if (ret)
+ pr_warning("CAIF: %s(): device rtml registration failed\n",
+ __func__);
+ return ret;
+}
+
+static int ipcaif_changelink(struct net_device *dev, struct nlattr *tb[],
+ struct nlattr *data[])
+{
+ struct chnl_net *caifdev;
+ ASSERT_RTNL();
+ caifdev = netdev_priv(dev);
+ caif_netlink_parms(data, &caifdev->conn_req);
+ netdev_state_change(dev);
+ return 0;
+}
+
+static size_t ipcaif_get_size(const struct net_device *dev)
+{
+ return
+ /* IFLA_CAIF_IPV4_CONNID */
+ nla_total_size(4) +
+ /* IFLA_CAIF_IPV6_CONNID */
+ nla_total_size(4) +
+ /* IFLA_CAIF_LOOPBACK */
+ nla_total_size(2) +
+ 0;
+}
+
+static const struct nla_policy ipcaif_policy[IFLA_CAIF_MAX + 1] = {
+ [IFLA_CAIF_IPV4_CONNID] = { .type = NLA_U32 },
+ [IFLA_CAIF_IPV6_CONNID] = { .type = NLA_U32 },
+ [IFLA_CAIF_LOOPBACK] = { .type = NLA_U8 }
+};
+
+
+static struct rtnl_link_ops ipcaif_link_ops __read_mostly = {
+ .kind = "caif",
+ .priv_size = sizeof(struct chnl_net),
+ .setup = ipcaif_net_setup,
+ .maxtype = IFLA_CAIF_MAX,
+ .policy = ipcaif_policy,
+ .newlink = ipcaif_newlink,
+ .changelink = ipcaif_changelink,
+ .get_size = ipcaif_get_size,
+ .fill_info = ipcaif_fill_info,
+
+};
+
+
+static int __init chnl_init_module(void)
+{
+ return rtnl_link_register(&ipcaif_link_ops);
+}
+
+static void __exit chnl_exit_module(void)
+{
+ struct chnl_net *dev = NULL;
+ struct list_head *list_node;
+ struct list_head *_tmp;
+ rtnl_link_unregister(&ipcaif_link_ops);
+ rtnl_lock();
+ list_for_each_safe(list_node, _tmp, &chnl_net_list) {
+ dev = list_entry(list_node, struct chnl_net, list_field);
+ list_del(list_node);
+ delete_device(dev);
+ }
+ rtnl_unlock();
+}
+
+module_init(chnl_init_module);
+module_exit(chnl_exit_module);
--
1.6.3.3
^ permalink raw reply related
* [PATCH net-next-2.6 v3 08/12] net-caif: add CAIF socket implementation
From: sjur.brandeland @ 2010-02-22 22:05 UTC (permalink / raw)
To: netdev, davem, marcel
Cc: daniel.martensson, kaber, stefano.babic, randy.dunlap,
Sjur Braendeland
In-Reply-To: <1266876331-23988-8-git-send-email-sjur.brandeland@stericsson.com>
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
Changes from PATCHv2:
- Use socket level SOL_IP for socket options SO_PRIORITY and SO_BINDTODEVICE.
- Simplified CAIF Channel configuration (no long use caif_channel.h)
- Adjusted flow control default
- Bugfix: Changed shutdown to be blocking
- Bugfix: Don't use sock_alloc_send_skb, but cfpkt_create.
Implementation of CAIF sockets for protocol and address family
PF_CAIF and AF_CAIF.
CAIF socket is connection oriented implementing SOCK_SEQPACKET
interface with supporting blocking and non-blocking mode.
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
net/caif/caif_socket.c | 1462 ++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1462 insertions(+), 0 deletions(-)
diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c
new file mode 100644
index 0000000..9a14041
--- /dev/null
+++ b/net/caif/caif_socket.c
@@ -0,0 +1,1462 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland sjur.brandeland@stericsson.com
+ * 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/sched.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/poll.h>
+#include <linux/tcp.h>
+#include <linux/uaccess.h>
+#include <asm/atomic.h>
+
+#include <linux/caif/caif_socket.h>
+#include <net/caif/caif_layer.h>
+#include <net/caif/caif_dev.h>
+#include <net/caif/cfpkt.h>
+
+MODULE_LICENSE("GPL");
+
+
+#define CHNL_SKT_READ_QUEUE_HIGH 200
+#define CHNL_SKT_READ_QUEUE_LOW 100
+
+int caif_sockbuf_size = 40000;
+static struct kmem_cache *caif_sk_cachep;
+static atomic_t caif_nr_socks = ATOMIC_INIT(0);
+
+#define CONN_STATE_OPEN_BIT 1
+#define CONN_STATE_PENDING_BIT 2
+#define CONN_STATE_PEND_DESTROY_BIT 3
+#define CONN_REMOTE_SHUTDOWN_BIT 4
+
+#define TX_FLOW_ON_BIT 1
+#define RX_FLOW_ON_BIT 2
+
+#define STATE_IS_OPEN(cf_sk) test_bit(CONN_STATE_OPEN_BIT,\
+ (void *) &(cf_sk)->conn_state)
+#define STATE_IS_REMOTE_SHUTDOWN(cf_sk) test_bit(CONN_REMOTE_SHUTDOWN_BIT,\
+ (void *) &(cf_sk)->conn_state)
+#define STATE_IS_PENDING(cf_sk) test_bit(CONN_STATE_PENDING_BIT,\
+ (void *) &(cf_sk)->conn_state)
+#define STATE_IS_PENDING_DESTROY(cf_sk) test_bit(CONN_STATE_PEND_DESTROY_BIT,\
+ (void *) &(cf_sk)->conn_state)
+
+#define SET_STATE_PENDING_DESTROY(cf_sk) set_bit(CONN_STATE_PEND_DESTROY_BIT,\
+ (void *) &(cf_sk)->conn_state)
+#define SET_STATE_OPEN(cf_sk) set_bit(CONN_STATE_OPEN_BIT,\
+ (void *) &(cf_sk)->conn_state)
+#define SET_STATE_CLOSED(cf_sk) clear_bit(CONN_STATE_OPEN_BIT,\
+ (void *) &(cf_sk)->conn_state)
+#define SET_PENDING_ON(cf_sk) set_bit(CONN_STATE_PENDING_BIT,\
+ (void *) &(cf_sk)->conn_state)
+#define SET_PENDING_OFF(cf_sk) clear_bit(CONN_STATE_PENDING_BIT,\
+ (void *) &(cf_sk)->conn_state)
+#define SET_REMOTE_SHUTDOWN(cf_sk) set_bit(CONN_REMOTE_SHUTDOWN_BIT,\
+ (void *) &(cf_sk)->conn_state)
+
+#define SET_REMOTE_SHUTDOWN_OFF(dev) clear_bit(CONN_REMOTE_SHUTDOWN_BIT,\
+ (void *) &(dev)->conn_state)
+#define RX_FLOW_IS_ON(cf_sk) test_bit(RX_FLOW_ON_BIT,\
+ (void *) &(cf_sk)->flow_state)
+#define TX_FLOW_IS_ON(cf_sk) test_bit(TX_FLOW_ON_BIT,\
+ (void *) &(cf_sk)->flow_state)
+
+#define SET_RX_FLOW_OFF(cf_sk) clear_bit(RX_FLOW_ON_BIT,\
+ (void *) &(cf_sk)->flow_state)
+#define SET_RX_FLOW_ON(cf_sk) set_bit(RX_FLOW_ON_BIT,\
+ (void *) &(cf_sk)->flow_state)
+#define SET_TX_FLOW_OFF(cf_sk) clear_bit(TX_FLOW_ON_BIT,\
+ (void *) &(cf_sk)->flow_state)
+#define SET_TX_FLOW_ON(cf_sk) set_bit(TX_FLOW_ON_BIT,\
+ (void *) &(cf_sk)->flow_state)
+
+#define SKT_READ_FLAG 0x01
+#define SKT_WRITE_FLAG 0x02
+static struct dentry *debugfsdir;
+#include <linux/debugfs.h>
+
+#ifdef CONFIG_DEBUG_FS
+struct debug_fs_counter {
+ atomic_t num_open;
+ atomic_t num_close;
+ atomic_t num_init;
+ atomic_t num_init_resp;
+ atomic_t num_init_fail_resp;
+ atomic_t num_deinit;
+ atomic_t num_deinit_resp;
+ atomic_t num_remote_shutdown_ind;
+ atomic_t num_tx_flow_off_ind;
+ atomic_t num_tx_flow_on_ind;
+ atomic_t num_rx_flow_off;
+ atomic_t num_rx_flow_on;
+ atomic_t skb_in_use;
+ atomic_t skb_alloc;
+ atomic_t skb_free;
+};
+struct debug_fs_counter cnt;
+#define dbfs_atomic_inc(v) atomic_inc(v)
+#define dbfs_atomic_dec(v) atomic_dec(v)
+#else
+#define dbfs_atomic_inc(v)
+#endif
+
+/* The AF_CAIF socket */
+struct caifsock {
+ /* NOTE: sk has to be the first member */
+ struct sock sk;
+ struct layer layer;
+ char name[CAIF_LAYER_NAME_SZ];
+ u32 conn_state;
+ u32 flow_state;
+ struct cfpktq *pktq;
+ int file_mode;
+ struct caif_connect_request conn_req;
+ int read_queue_len;
+ spinlock_t read_queue_len_lock;
+ struct dentry *debugfs_socket_dir;
+};
+
+static void drain_queue(struct caifsock *cf_sk);
+
+/* Packet Receive Callback function called from CAIF Stack */
+static int caif_sktrecv_cb(struct layer *layr, struct cfpkt *pkt)
+{
+ struct caifsock *cf_sk;
+ int read_queue_high;
+ cf_sk = container_of(layr, struct caifsock, layer);
+
+ if (!STATE_IS_OPEN(cf_sk)) {
+ /*FIXME: This should be allowed finally!*/
+ pr_debug("CAIF: %s(): called after close request\n", __func__);
+ cfpkt_destroy(pkt);
+ return 0;
+ }
+ /* NOTE: This function may be called in Tasklet context! */
+
+ /* The queue has its own lock */
+ cfpkt_queue(cf_sk->pktq, pkt, 0);
+
+ spin_lock(&cf_sk->read_queue_len_lock);
+ cf_sk->read_queue_len++;
+
+ read_queue_high = (cf_sk->read_queue_len > CHNL_SKT_READ_QUEUE_HIGH);
+ spin_unlock(&cf_sk->read_queue_len_lock);
+
+ if (RX_FLOW_IS_ON(cf_sk) && read_queue_high) {
+ dbfs_atomic_inc(&cnt.num_rx_flow_off);
+ SET_RX_FLOW_OFF(cf_sk);
+
+ /* Send flow off (NOTE: must not sleep) */
+ pr_debug("CAIF: %s():"
+ " sending flow OFF (queue len = %d)\n",
+ __func__,
+ cf_sk->read_queue_len);
+ caif_assert(cf_sk->layer.dn);
+ caif_assert(cf_sk->layer.dn->ctrlcmd);
+
+ (void) cf_sk->layer.dn->modemcmd(cf_sk->layer.dn,
+ CAIF_MODEMCMD_FLOW_OFF_REQ);
+ }
+
+ /* Signal reader that data is available. */
+
+ wake_up_interruptible(cf_sk->sk.sk_sleep);
+
+ return 0;
+}
+
+/* Packet Flow Control Callback function called from CAIF */
+static void caif_sktflowctrl_cb(struct layer *layr,
+ enum caif_ctrlcmd flow,
+ int phyid)
+{
+ struct caifsock *cf_sk;
+
+ /* NOTE: This function may be called in Tasklet context! */
+ pr_debug("CAIF: %s(): flowctrl func called: %s.\n",
+ __func__,
+ 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");
+
+ cf_sk = container_of(layr, struct caifsock, layer);
+ switch (flow) {
+ case CAIF_CTRLCMD_FLOW_ON_IND:
+ dbfs_atomic_inc(&cnt.num_tx_flow_on_ind);
+ /* Signal reader that data is available. */
+ SET_TX_FLOW_ON(cf_sk);
+ wake_up_interruptible(cf_sk->sk.sk_sleep);
+ break;
+
+ case CAIF_CTRLCMD_FLOW_OFF_IND:
+ dbfs_atomic_inc(&cnt.num_tx_flow_off_ind);
+ SET_TX_FLOW_OFF(cf_sk);
+ break;
+
+ case CAIF_CTRLCMD_INIT_RSP:
+ dbfs_atomic_inc(&cnt.num_init_resp);
+ /* Signal reader that data is available. */
+ caif_assert(STATE_IS_OPEN(cf_sk));
+ SET_PENDING_OFF(cf_sk);
+ SET_TX_FLOW_ON(cf_sk);
+ wake_up_interruptible(cf_sk->sk.sk_sleep);
+ break;
+
+ case CAIF_CTRLCMD_DEINIT_RSP:
+ dbfs_atomic_inc(&cnt.num_deinit_resp);
+ caif_assert(!STATE_IS_OPEN(cf_sk));
+ SET_PENDING_OFF(cf_sk);
+ if (!STATE_IS_PENDING_DESTROY(cf_sk)) {
+ if (cf_sk->sk.sk_sleep != NULL)
+ wake_up_interruptible(cf_sk->sk.sk_sleep);
+ }
+ dbfs_atomic_inc(&cnt.num_deinit);
+ sock_put(&cf_sk->sk);
+ wake_up_interruptible(cf_sk->sk.sk_sleep);
+ break;
+
+ case CAIF_CTRLCMD_INIT_FAIL_RSP:
+ dbfs_atomic_inc(&cnt.num_init_fail_resp);
+ caif_assert(STATE_IS_OPEN(cf_sk));
+ SET_STATE_CLOSED(cf_sk);
+ SET_PENDING_OFF(cf_sk);
+ SET_TX_FLOW_OFF(cf_sk);
+ wake_up_interruptible(cf_sk->sk.sk_sleep);
+ break;
+
+ case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND:
+ dbfs_atomic_inc(&cnt.num_remote_shutdown_ind);
+ SET_REMOTE_SHUTDOWN(cf_sk);
+ SET_TX_FLOW_OFF(cf_sk);
+ drain_queue(cf_sk);
+ SET_RX_FLOW_ON(cf_sk);
+ cf_sk->file_mode = 0;
+ wake_up_interruptible(cf_sk->sk.sk_sleep);
+ break;
+
+ default:
+ pr_debug("CAIF: %s(): Unexpected flow command %d\n",
+ __func__, flow);
+ }
+}
+
+static void skb_destructor(struct sk_buff *skb)
+{
+ dbfs_atomic_inc(&cnt.skb_free);
+ dbfs_atomic_dec(&cnt.skb_in_use);
+}
+
+
+static int caif_recvmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *m, size_t buf_len, int flags)
+
+{
+ struct sock *sk = sock->sk;
+ struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
+ struct cfpkt *pkt = NULL;
+ size_t len;
+ int result;
+ struct sk_buff *skb;
+ ssize_t ret = -EIO;
+ int read_queue_low;
+
+ if (cf_sk == NULL) {
+ pr_debug("CAIF: %s(): private_data not set!\n",
+ __func__);
+ ret = -EBADFD;
+ goto read_error;
+ }
+
+ /* Don't do multiple iovec entries yet */
+ if (m->msg_iovlen != 1)
+ return -EOPNOTSUPP;
+
+ if (unlikely(!buf_len))
+ return -EINVAL;
+
+ lock_sock(&(cf_sk->sk));
+
+ caif_assert(cf_sk->pktq);
+
+ if (!STATE_IS_OPEN(cf_sk)) {
+ /* Socket is closed or closing. */
+ if (!STATE_IS_PENDING(cf_sk)) {
+ pr_debug("CAIF: %s(): socket is closed (by remote)\n",
+ __func__);
+ ret = -EPIPE;
+ } else {
+ pr_debug("CAIF: %s(): socket is closing..\n", __func__);
+ ret = -EBADF;
+ }
+ goto read_error;
+ }
+ /* Socket is open or opening. */
+ if (STATE_IS_PENDING(cf_sk)) {
+ pr_debug("CAIF: %s(): socket is opening...\n", __func__);
+
+ if (flags & MSG_DONTWAIT) {
+ /* We can't block. */
+ pr_debug("CAIF: %s():state pending and MSG_DONTWAIT\n",
+ __func__);
+ ret = -EAGAIN;
+ goto read_error;
+ }
+
+ /*
+ * Blocking mode; state is pending and we need to wait
+ * for its conclusion.
+ */
+ release_sock(&cf_sk->sk);
+
+ result =
+ wait_event_interruptible(*cf_sk->sk.sk_sleep,
+ !STATE_IS_PENDING(cf_sk));
+
+ lock_sock(&(cf_sk->sk));
+
+ if (result == -ERESTARTSYS) {
+ pr_debug("CAIF: %s(): wait_event_interruptible"
+ " woken by a signal (1)", __func__);
+ ret = -ERESTARTSYS;
+ goto read_error;
+ }
+ }
+ if (STATE_IS_REMOTE_SHUTDOWN(cf_sk)) {
+ pr_debug("CAIF: %s(): received remote_shutdown indication\n",
+ __func__);
+ ret = -ESHUTDOWN;
+ goto read_error;
+ }
+
+ /*
+ * Block if we don't have any received buffers.
+ * The queue has its own lock.
+ */
+ while ((pkt = cfpkt_qpeek(cf_sk->pktq)) == NULL) {
+
+ if (flags & MSG_DONTWAIT) {
+ pr_debug("CAIF: %s(): MSG_DONTWAIT\n", __func__);
+ ret = -EAGAIN;
+ goto read_error;
+ }
+ trace_printk("CAIF: %s() wait_event\n", __func__);
+
+ /* Let writers in. */
+ release_sock(&cf_sk->sk);
+
+ /* Block reader until data arrives or socket is closed. */
+ if (wait_event_interruptible(*cf_sk->sk.sk_sleep,
+ cfpkt_qpeek(cf_sk->pktq)
+ || STATE_IS_REMOTE_SHUTDOWN(cf_sk)
+ || !STATE_IS_OPEN(cf_sk)) ==
+ -ERESTARTSYS) {
+ pr_debug("CAIF: %s():"
+ " wait_event_interruptible woken by "
+ "a signal, signal_pending(current) = %d\n",
+ __func__,
+ signal_pending(current));
+ return -ERESTARTSYS;
+ }
+
+ trace_printk("CAIF: %s() awake\n", __func__);
+ if (STATE_IS_REMOTE_SHUTDOWN(cf_sk)) {
+ pr_debug("CAIF: %s(): "
+ "received remote_shutdown indication\n",
+ __func__);
+ ret = -ESHUTDOWN;
+ goto read_error_no_unlock;
+ }
+
+ /* I want to be alone on cf_sk (except status and queue). */
+ lock_sock(&(cf_sk->sk));
+
+ if (!STATE_IS_OPEN(cf_sk)) {
+ /* Someone closed the link, report error. */
+ pr_debug("CAIF: %s(): remote end shutdown!\n",
+ __func__);
+ ret = -EPIPE;
+ goto read_error;
+ }
+ }
+
+ /* The queue has its own lock. */
+ len = cfpkt_getlen(pkt);
+
+ /* Check max length that can be copied. */
+ if (len > buf_len) {
+ pr_debug("CAIF: %s(): user buffer too small\n", __func__);
+ ret = -EINVAL;
+ goto read_error;
+ }
+
+ /*
+ * Get packet from queue.
+ * The queue has its own lock.
+ */
+ pkt = cfpkt_dequeue(cf_sk->pktq);
+
+ spin_lock(&cf_sk->read_queue_len_lock);
+ cf_sk->read_queue_len--;
+ read_queue_low = (cf_sk->read_queue_len < CHNL_SKT_READ_QUEUE_LOW);
+ spin_unlock(&cf_sk->read_queue_len_lock);
+
+ if (!RX_FLOW_IS_ON(cf_sk) && read_queue_low) {
+ dbfs_atomic_inc(&cnt.num_rx_flow_on);
+ SET_RX_FLOW_ON(cf_sk);
+
+ /* Send flow on. */
+ pr_debug("CAIF: %s(): sending flow ON (queue len = %d)\n",
+ __func__, cf_sk->read_queue_len);
+ caif_assert(cf_sk->layer.dn);
+ caif_assert(cf_sk->layer.dn->ctrlcmd);
+ (void) cf_sk->layer.dn->modemcmd(cf_sk->layer.dn,
+ CAIF_MODEMCMD_FLOW_ON_REQ);
+
+ caif_assert(cf_sk->read_queue_len >= 0);
+ }
+
+ skb = cfpkt_tonative(pkt);
+ result = skb_copy_datagram_iovec(skb, 0, m->msg_iov, len);
+ if (result) {
+ pr_debug("CAIF: %s(): cfpkt_raw_extract failed\n", __func__);
+ cfpkt_destroy(pkt);
+ ret = -EFAULT;
+ goto read_error;
+ }
+ if (unlikely(buf_len < len)) {
+ len = buf_len;
+ m->msg_flags |= MSG_TRUNC;
+ }
+
+ /* Free packet. */
+ skb_free_datagram(sk, skb);
+
+ /* Let the others in. */
+ release_sock(&cf_sk->sk);
+ return len;
+
+read_error:
+ release_sock(&cf_sk->sk);
+read_error_no_unlock:
+ return ret;
+}
+
+/* Send a signal as a consequence of sendmsg, sendto or caif_sendmsg. */
+static int caif_sendmsg(struct kiocb *kiocb, struct socket *sock,
+ struct msghdr *msg, size_t len)
+{
+
+ struct sock *sk = sock->sk;
+ struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
+ void *payload;
+ size_t payload_size = msg->msg_iov->iov_len;
+ struct cfpkt *pkt = NULL;
+ struct payload_info info;
+ unsigned char *txbuf;
+ ssize_t ret = -EIO;
+ int result;
+ struct sk_buff *skb;
+ caif_assert(msg->msg_iovlen == 1);
+
+ if (cf_sk == NULL) {
+ pr_debug("CAIF: %s(): private_data not set!\n",
+ __func__);
+ ret = -EBADFD;
+ goto write_error_no_unlock;
+ }
+
+ if (unlikely(msg->msg_iov->iov_base == NULL)) {
+ pr_warning("CAIF: %s(): Buffer is NULL.\n", __func__);
+ ret = -EINVAL;
+ goto write_error_no_unlock;
+ }
+
+ payload = msg->msg_iov->iov_base;
+ if (payload_size > CAIF_MAX_PAYLOAD_SIZE) {
+ pr_debug("CAIF: %s(): buffer too long\n", __func__);
+ ret = -EINVAL;
+ goto write_error_no_unlock;
+ }
+ /* I want to be alone on cf_sk (except status and queue) */
+ lock_sock(&(cf_sk->sk));
+
+ caif_assert(cf_sk->pktq);
+
+ if (!STATE_IS_OPEN(cf_sk)) {
+ /* Socket is closed or closing */
+ if (!STATE_IS_PENDING(cf_sk)) {
+ pr_debug("CAIF: %s(): socket is closed (by remote)\n",
+ __func__);
+ ret = -EPIPE;
+ } else {
+ pr_debug("CAIF: %s(): socket is closing...\n",
+ __func__);
+ ret = -EBADF;
+ }
+ goto write_error;
+ }
+
+ /* Socket is open or opening */
+ if (STATE_IS_PENDING(cf_sk)) {
+ pr_debug("CAIF: %s(): socket is opening...\n", __func__);
+
+ if (msg->msg_flags & MSG_DONTWAIT) {
+ /* We can't block */
+ trace_printk("CAIF: %s():state pending:"
+ "state=MSG_DONTWAIT\n", __func__);
+ ret = -EAGAIN;
+ goto write_error;
+ }
+
+ /* Let readers in */
+ release_sock(&cf_sk->sk);
+
+ /*
+ * Blocking mode; state is pending and we need to wait
+ * for its conclusion.
+ */
+ result =
+ wait_event_interruptible(*cf_sk->sk.sk_sleep,
+ !STATE_IS_PENDING(cf_sk));
+
+ /* I want to be alone on cf_sk (except status and queue) */
+ lock_sock(&(cf_sk->sk));
+
+ if (result == -ERESTARTSYS) {
+ pr_debug("CAIF: %s(): wait_event_interruptible"
+ " woken by a signal (1)", __func__);
+ ret = -ERESTARTSYS;
+ goto write_error;
+ }
+ }
+
+ if (STATE_IS_REMOTE_SHUTDOWN(cf_sk)) {
+ pr_debug("CAIF: %s(): received remote_shutdown indication\n",
+ __func__);
+ ret = -ESHUTDOWN;
+ goto write_error;
+ }
+
+ if (!TX_FLOW_IS_ON(cf_sk)) {
+
+ /* Flow is off. Check non-block flag */
+ if (msg->msg_flags & MSG_DONTWAIT) {
+ trace_printk("CAIF: %s(): MSG_DONTWAIT and tx flow off",
+ __func__);
+ ret = -EAGAIN;
+ goto write_error;
+ }
+
+ /* release lock before waiting */
+ release_sock(&cf_sk->sk);
+
+ /* Wait until flow is on or socket is closed */
+ if (wait_event_interruptible(*cf_sk->sk.sk_sleep,
+ TX_FLOW_IS_ON(cf_sk)
+ || !STATE_IS_OPEN(cf_sk)
+ || STATE_IS_REMOTE_SHUTDOWN(cf_sk)
+ ) == -ERESTARTSYS) {
+ pr_debug("CAIF: %s():"
+ " wait_event_interruptible woken by a signal",
+ __func__);
+ ret = -ERESTARTSYS;
+ goto write_error_no_unlock;
+ }
+
+ /* I want to be alone on cf_sk (except status and queue) */
+ lock_sock(&(cf_sk->sk));
+
+ if (!STATE_IS_OPEN(cf_sk)) {
+ /* someone closed the link, report error */
+ pr_debug("CAIF: %s(): remote end shutdown!\n",
+ __func__);
+ ret = -EPIPE;
+ goto write_error;
+ }
+
+ if (STATE_IS_REMOTE_SHUTDOWN(cf_sk)) {
+ pr_debug("CAIF: %s(): "
+ "received remote_shutdown indication\n",
+ __func__);
+ ret = -ESHUTDOWN;
+ goto write_error;
+ }
+ }
+
+ pkt = cfpkt_create(payload_size);
+ skb = (struct sk_buff *)pkt;
+ skb->destructor = skb_destructor;
+ skb->sk = sk;
+ dbfs_atomic_inc(&cnt.skb_alloc);
+ dbfs_atomic_inc(&cnt.skb_in_use);
+ if (cfpkt_raw_append(pkt, (void **) &txbuf, payload_size) < 0) {
+ pr_debug("CAIF: %s(): cfpkt_raw_append failed\n", __func__);
+ cfpkt_destroy(pkt);
+ ret = -EINVAL;
+ goto write_error;
+ }
+
+ /* Copy data into buffer. */
+ if (copy_from_user(txbuf, payload, payload_size)) {
+ pr_debug("CAIF: %s(): copy_from_user returned non zero.\n",
+ __func__);
+ cfpkt_destroy(pkt);
+ ret = -EINVAL;
+ goto write_error;
+ }
+ memset(&info, 0, sizeof(info));
+
+ /* Send the packet down the stack. */
+ caif_assert(cf_sk->layer.dn);
+ caif_assert(cf_sk->layer.dn->transmit);
+
+ do {
+ ret = cf_sk->layer.dn->transmit(cf_sk->layer.dn, pkt);
+
+ if (likely((ret >= 0) || (ret != -EAGAIN)))
+ break;
+
+ /* EAGAIN - retry */
+ if (msg->msg_flags & MSG_DONTWAIT) {
+ pr_debug("CAIF: %s(): NONBLOCK and transmit failed,"
+ " error = %d\n", __func__, ret);
+ ret = -EAGAIN;
+ goto write_error;
+ }
+
+ /* Let readers in */
+ release_sock(&cf_sk->sk);
+
+ /* Wait until flow is on or socket is closed */
+ if (wait_event_interruptible(*cf_sk->sk.sk_sleep,
+ TX_FLOW_IS_ON(cf_sk)
+ || !STATE_IS_OPEN(cf_sk)
+ || STATE_IS_REMOTE_SHUTDOWN(cf_sk)
+ ) == -ERESTARTSYS) {
+ pr_debug("CAIF: %s(): wait_event_interruptible"
+ " woken by a signal", __func__);
+ ret = -ERESTARTSYS;
+ goto write_error_no_unlock;
+ }
+
+ /* I want to be alone on cf_sk (except status and queue) */
+ lock_sock(&(cf_sk->sk));
+
+ } while (ret == -EAGAIN);
+
+ if (ret < 0) {
+ cfpkt_destroy(pkt);
+ pr_debug("CAIF: %s(): transmit failed, error = %d\n",
+ __func__, ret);
+
+ goto write_error;
+ }
+
+ release_sock(&cf_sk->sk);
+ return payload_size;
+
+write_error:
+ release_sock(&cf_sk->sk);
+write_error_no_unlock:
+ return ret;
+}
+
+static unsigned int caif_poll(struct file *file, struct socket *sock,
+ poll_table *wait)
+{
+ struct sock *sk = sock->sk;
+ struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
+ u32 mask = 0;
+
+ poll_wait(file, sk->sk_sleep, wait);
+ lock_sock(&(cf_sk->sk));
+ if (cfpkt_qpeek(cf_sk->pktq) != NULL)
+ mask |= (POLLIN | POLLRDNORM);
+ if (!STATE_IS_OPEN(cf_sk))
+ mask |= POLLHUP;
+ else if (TX_FLOW_IS_ON(cf_sk))
+ mask |= (POLLOUT | POLLWRNORM);
+ release_sock(&cf_sk->sk);
+ trace_printk("CAIF: %s(): poll mask=0x%04x...\n",
+ __func__, mask);
+ return mask;
+}
+
+static void drain_queue(struct caifsock *cf_sk)
+{
+ struct cfpkt *pkt = NULL;
+
+ /* Empty the queue */
+ do {
+ /* The queue has its own lock */
+ pkt = cfpkt_dequeue(cf_sk->pktq);
+ if (!pkt)
+ break;
+ pr_debug("CAIF: %s(): freeing packet from read queue\n",
+ __func__);
+ cfpkt_destroy(pkt);
+
+ } while (1);
+
+ spin_lock(&cf_sk->read_queue_len_lock);
+ cf_sk->read_queue_len = 0;
+ spin_unlock(&cf_sk->read_queue_len_lock);
+}
+
+
+static int setsockopt(struct socket *sock,
+ int lvl, int opt, char __user *ov, unsigned int ol)
+{
+ struct sock *sk = sock->sk;
+ struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
+ int prio, linksel;
+ struct ifreq ifreq;
+ struct caif_param caif_param;
+ if (STATE_IS_OPEN(cf_sk)) {
+ pr_debug("CAIF: %s(): setsockopt "
+ "cannot be done on a connected socket\n",
+ __func__);
+ return -ENOPROTOOPT;
+ }
+ switch (opt) {
+ case CAIFSO_LINK_SELECT:
+ if (ol < sizeof(int)) {
+ pr_debug("CAIF: %s(): setsockopt"
+ " CAIFSO_CHANNEL_CONFIG bad size\n", __func__);
+ return -EINVAL;
+ }
+ if (lvl != SOL_CAIF)
+ goto bad_sol;
+ if (copy_from_user(&linksel, ov, sizeof(int)))
+ return -EINVAL;
+ lock_sock(&(cf_sk->sk));
+ cf_sk->conn_req.link_selector = linksel;
+ release_sock(&cf_sk->sk);
+ return 0;
+
+ case SO_PRIORITY:
+ if (lvl != SOL_IP)
+ goto bad_sol;
+ if (ol < sizeof(int)) {
+ pr_debug("CAIF: %s(): setsockopt"
+ " SO_PRIORITY bad size\n", __func__);
+ return -EINVAL;
+ }
+ if (copy_from_user(&prio, ov, sizeof(int)))
+ return -EINVAL;
+ lock_sock(&(cf_sk->sk));
+ cf_sk->conn_req.priority = prio;
+ pr_debug("CAIF: %s(): Setting sockopt priority=%d\n", __func__,
+ cf_sk->conn_req.priority);
+ release_sock(&cf_sk->sk);
+ return 0;
+
+ case SO_BINDTODEVICE:
+ if (lvl != SOL_IP)
+ goto bad_sol;
+ if (ol < sizeof(struct ifreq)) {
+ pr_debug("CAIF: %s(): setsockopt"
+ " SO_PRIORITY bad size\n", __func__);
+ return -EINVAL;
+ }
+ if (copy_from_user(&ifreq, ov, sizeof(ifreq)))
+ return -EFAULT;
+ lock_sock(&(cf_sk->sk));
+ strncpy(cf_sk->conn_req.link_name, ifreq.ifr_name,
+ sizeof(cf_sk->conn_req.link_name));
+ cf_sk->conn_req.link_name
+ [sizeof(cf_sk->conn_req.link_name)-1] = 0;
+ release_sock(&cf_sk->sk);
+ return 0;
+
+ case CAIFSO_REQ_PARAM:
+ if (lvl != SOL_CAIF)
+ goto bad_sol;
+ if (cf_sk->sk.sk_protocol != CAIFPROTO_UTIL)
+ return -ENOPROTOOPT;
+ if (ol < sizeof(caif_param.size))
+ goto req_param_bad_size;
+ if (copy_from_user(&caif_param, ov, sizeof(caif_param.size)))
+ goto req_param_bad_size;
+
+ if (ol < offsetof(struct caif_param, data) + caif_param.size ||
+ caif_param.size >
+ sizeof(cf_sk->conn_req.param)) {
+req_param_bad_size:
+ pr_debug("CAIF: %s(): setsockopt"
+ " CAIFSO_CHANNEL_CONFIG bad size\n", __func__);
+ return -EINVAL;
+ }
+
+ if (copy_from_user(&caif_param, ov, ol))
+ return -EINVAL;
+ lock_sock(&(cf_sk->sk));
+ cf_sk->conn_req.param = caif_param;
+ pr_debug("CAIF: %s(): Setting reqparam len=%d "
+ "data=0x%02x%02x%02x%02x\n",
+ __func__,
+ cf_sk->conn_req.param.size,
+ cf_sk->conn_req.param.data[0],
+ cf_sk->conn_req.param.data[1],
+ cf_sk->conn_req.param.data[2],
+ cf_sk->conn_req.param.data[3]);
+ release_sock(&cf_sk->sk);
+ return 0;
+
+ default:
+ pr_debug("CAIF: %s(): unhandled option %d\n", __func__, opt);
+ return -EINVAL;
+ }
+
+ return 0;
+bad_sol:
+ pr_debug("CAIF: %s(): setsockopt bad level\n", __func__);
+ return -ENOPROTOOPT;
+
+}
+
+static int getsockopt(struct socket *sock,
+ int lvl, int opt, char __user *ov, int __user *ol)
+{
+ struct sock *sk = sock->sk;
+ struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
+ struct cfpkt *pkt;
+ int len;
+ u32 val;
+ if (lvl != SOL_CAIF)
+ return -ENOPROTOOPT;
+ if (get_user(len, ol))
+ return -EFAULT;
+ if (len < 0)
+ return -EINVAL;
+ if (!STATE_IS_OPEN(cf_sk) && !STATE_IS_PENDING(cf_sk)) {
+ pr_debug("CAIF: %s():"
+ "can only be done on a sconnected socket\n",
+ __func__);
+ return -ENOPROTOOPT;
+ }
+
+ if (ov == NULL || len < sizeof(int))
+ return -EINVAL;
+ switch (opt) {
+ case CAIFSO_CHANNEL_ID:
+ if (len < sizeof(int))
+ return -EINVAL;
+ len = sizeof(val);
+ val = cf_sk->layer.id;
+ break;
+
+ case CAIFSO_NEXT_PACKET_LEN:
+ if (len < sizeof(int))
+ return -EINVAL;
+ len = sizeof(val);
+ lock_sock(&(cf_sk->sk));
+ pkt = cfpkt_qpeek(cf_sk->pktq);
+ if (pkt)
+ val = cfpkt_getlen(pkt);
+ else
+ val = 0;
+ release_sock(&(cf_sk->sk));
+ break;
+
+ case CAIFSO_MAX_PACKET_LEN:
+ if (len < sizeof(int))
+ return -EINVAL;
+ len = sizeof(val);
+ val = 4050;
+ break;
+ default:
+ pr_debug("CAIF: %s(): unhandled option %d\n", __func__, opt);
+ return -ENOPROTOOPT;
+ }
+ if (copy_to_user(ov, &val, sizeof(val)))
+ return -EINVAL;
+
+ if (put_user(len, ol))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+int caif_connect(struct socket *sock, struct sockaddr *uservaddr,
+ int sockaddr_len, int flags)
+{
+ struct caifsock *cf_sk = NULL;
+ int result = -1;
+ int mode = 0;
+ int ret = -EIO;
+ struct sock *sk = sock->sk;
+ BUG_ON(sk == NULL);
+
+ cf_sk = container_of(sk, struct caifsock, sk);
+
+ trace_printk("CAIF: %s(): cf_sk=%p OPEN=%d, TX_FLOW=%d, RX_FLOW=%d\n",
+ __func__, cf_sk,
+ STATE_IS_OPEN(cf_sk),
+ TX_FLOW_IS_ON(cf_sk), RX_FLOW_IS_ON(cf_sk));
+
+ sk->sk_state = TCP_CLOSE;
+ sock->state = SS_UNCONNECTED;
+
+ if (sock->type == SOCK_SEQPACKET) {
+ sock->state = SS_CONNECTED;
+ sk->sk_state = TCP_ESTABLISHED;
+ } else
+ goto out;
+
+ /* I want to be alone on cf_sk (except status and queue) */
+ lock_sock(&(cf_sk->sk));
+
+ if (sockaddr_len != sizeof(struct sockaddr_caif)) {
+ pr_debug("CAIF: %s(): Bad address len (%d,%d)\n",
+ __func__, sockaddr_len, sizeof(struct sockaddr_caif));
+ ret = -EINVAL;
+ goto open_error;
+ }
+
+ if (uservaddr->sa_family != AF_CAIF) {
+ pr_debug("CAIF: %s(): Bad address family (%d)\n",
+ __func__, uservaddr->sa_family);
+ ret = -EAFNOSUPPORT;
+ goto open_error;
+ }
+
+ memcpy(&cf_sk->conn_req.sockaddr, uservaddr,
+ sizeof(struct sockaddr_caif));
+
+ dbfs_atomic_inc(&cnt.num_open);
+ mode = SKT_READ_FLAG | SKT_WRITE_FLAG;
+
+ /* If socket is not open, make sure socket is in fully closed state */
+ if (!STATE_IS_OPEN(cf_sk)) {
+ /* Has link close response been received (if we ever sent it)?*/
+ if (STATE_IS_PENDING(cf_sk)) {
+ /*
+ * Still waiting for close response from remote.
+ * If opened non-blocking, report "would block"
+ */
+ if (flags & MSG_DONTWAIT) {
+ pr_debug("CAIF: %s(): MSG_DONTWAIT"
+ " && close pending\n", __func__);
+ ret = -EAGAIN;
+ goto open_error;
+ }
+
+ pr_debug("CAIF: %s(): Wait for close response"
+ " from remote...\n", __func__);
+
+ release_sock(&cf_sk->sk);
+
+ /*
+ * Blocking mode; close is pending and we need to wait
+ * for its conclusion.
+ */
+ result =
+ wait_event_interruptible(*cf_sk->sk.sk_sleep,
+ !STATE_IS_PENDING(cf_sk));
+
+ lock_sock(&(cf_sk->sk));
+ if (result == -ERESTARTSYS) {
+ pr_debug("CAIF: %s(): wait_event_interruptible"
+ "woken by a signal (1)", __func__);
+ ret = -ERESTARTSYS;
+ goto open_error;
+ }
+ }
+ }
+
+ /* socket is now either closed, pending open or open */
+ if (STATE_IS_OPEN(cf_sk) && !STATE_IS_PENDING(cf_sk)) {
+ /* Open */
+ pr_debug("CAIF: %s(): Socket is already opened (cf_sk=%p)"
+ " check access f_flags = 0x%x file_mode = 0x%x\n",
+ __func__, cf_sk, mode, cf_sk->file_mode);
+
+ if (mode & cf_sk->file_mode) {
+ pr_debug("CAIF: %s(): Access mode already in use"
+ "0x%x\n",
+ __func__, mode);
+ ret = -EBUSY;
+ goto open_error;
+ }
+ } else {
+ /* We are closed or pending open.
+ * If closed: send link setup
+ * If pending open: link setup already sent (we could have been
+ * interrupted by a signal last time)
+ */
+ if (!STATE_IS_OPEN(cf_sk)) {
+ /* First opening of file; connect lower layers: */
+ /* Drain queue (very unlikely) */
+ drain_queue(cf_sk);
+
+ cf_sk->layer.receive = caif_sktrecv_cb;
+
+ SET_STATE_OPEN(cf_sk);
+ SET_PENDING_ON(cf_sk);
+
+ /* Register this channel. */
+ result =
+ caif_connect_client(&cf_sk->conn_req,
+ &cf_sk->layer);
+ if (result < 0) {
+ pr_debug("CAIF: %s(): can't register channel\n",
+ __func__);
+ ret = -EIO;
+ SET_STATE_CLOSED(cf_sk);
+ SET_PENDING_OFF(cf_sk);
+ goto open_error;
+ }
+ dbfs_atomic_inc(&cnt.num_init);
+ }
+
+ /* If opened non-blocking, report "success".
+ */
+ if (flags & MSG_DONTWAIT) {
+ pr_debug("CAIF: %s(): MSG_DONTWAIT success\n",
+ __func__);
+ ret = 0;
+ goto open_success;
+ }
+
+ trace_printk("CAIF: %s(): Wait for connect response\n",
+ __func__);
+
+ /* release lock before waiting */
+ release_sock(&cf_sk->sk);
+
+ result =
+ wait_event_interruptible(*cf_sk->sk.sk_sleep,
+ !STATE_IS_PENDING(cf_sk));
+
+ lock_sock(&(cf_sk->sk));
+
+ if (result == -ERESTARTSYS) {
+ pr_debug("CAIF: %s(): wait_event_interruptible"
+ "woken by a signal (2)", __func__);
+ ret = -ERESTARTSYS;
+ goto open_error;
+ }
+
+ if (!STATE_IS_OPEN(cf_sk)) {
+ /* Lower layers said "no" */
+ pr_debug("CAIF: %s(): Closed received\n", __func__);
+ ret = -EPIPE;
+ goto open_error;
+ }
+
+ trace_printk("CAIF: %s(): Connect received\n", __func__);
+ }
+open_success:
+ /* Open is ok */
+ cf_sk->file_mode |= mode;
+
+ trace_printk("CAIF: %s(): Connected - file mode = %x\n",
+ __func__, cf_sk->file_mode);
+
+ release_sock(&cf_sk->sk);
+ return 0;
+open_error:
+ release_sock(&cf_sk->sk);
+out:
+ return ret;
+}
+
+
+static int caif_shutdown(struct socket *sock, int how)
+{
+ struct caifsock *cf_sk = NULL;
+ int result;
+ int tx_flow_state_was_on;
+ struct sock *sk = sock->sk;
+ if (how != SHUT_RDWR)
+ return -EOPNOTSUPP; /* FIXME: ENOTSUP in userland for POSIX */
+ cf_sk = container_of(sk, struct caifsock, sk);
+ if (cf_sk == NULL) {
+ pr_debug("CAIF: %s(): COULD NOT FIND SOCKET\n", __func__);
+ return -EBADF;
+ }
+ /* I want to be alone on cf_sk (except status queue) */
+ lock_sock(&(cf_sk->sk));
+ sock_hold(&cf_sk->sk);
+
+ /* 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.
+ */
+ if (STATE_IS_OPEN(cf_sk) && !STATE_IS_PENDING(cf_sk)) {
+
+ SET_STATE_CLOSED(cf_sk);
+ SET_PENDING_ON(cf_sk);
+ sock->state = SS_DISCONNECTING;
+ tx_flow_state_was_on = TX_FLOW_IS_ON(cf_sk);
+ SET_TX_FLOW_OFF(cf_sk);
+
+ /* Hold the socket until DEINIT_RSP is received */
+ sock_hold(&cf_sk->sk);
+ result = caif_disconnect_client(&cf_sk->layer);
+
+ if (result < 0) {
+ pr_debug("CAIF: %s(): "
+ "caif_disconnect_client() failed\n",
+ __func__);
+ SET_STATE_CLOSED(cf_sk);
+ SET_PENDING_OFF(cf_sk);
+ SET_TX_FLOW_OFF(cf_sk);
+ release_sock(&cf_sk->sk);
+ sock_put(&cf_sk->sk);
+ return -EIO;
+ }
+
+ }
+ if (STATE_IS_REMOTE_SHUTDOWN(cf_sk)) {
+ SET_PENDING_OFF(cf_sk);
+ SET_REMOTE_SHUTDOWN_OFF(cf_sk);
+ }
+ result =
+ wait_event_interruptible(*cf_sk->sk.sk_sleep,
+ !STATE_IS_PENDING(cf_sk));
+
+ if (result == -ERESTARTSYS) {
+ pr_debug("CAIF: %s(): wait_event_interruptible"
+ "woken by a signal (1)", __func__);
+ }
+
+ /*
+ * Socket is no longer in state pending close,
+ * and we can release the reference.
+ */
+
+ dbfs_atomic_inc(&cnt.num_close);
+ drain_queue(cf_sk);
+ SET_RX_FLOW_ON(cf_sk);
+ cf_sk->file_mode = 0;
+ sock_put(&cf_sk->sk);
+ release_sock(&cf_sk->sk);
+ return result;
+}
+
+static ssize_t caif_sock_no_sendpage(struct socket *sock,
+ struct page *page,
+ int offset, size_t size, int flags)
+{
+ return -EOPNOTSUPP;
+}
+
+/* This function is called as part of close. */
+static int caif_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+ struct caifsock *cf_sk = NULL;
+ int res;
+
+ caif_assert(sk != NULL);
+ cf_sk = container_of(sk, struct caifsock, sk);
+
+ res = caif_shutdown(sock, SHUT_RDWR);
+ if (res)
+ return res;
+ lock_sock(&(cf_sk->sk));
+
+ sock->sk = NULL;
+
+ /* Detach the socket from its process context by making it orphan. */
+ sock_orphan(sk);
+
+ /*
+ * Setting SHUTDOWN_MASK means that both send and receive are shutdown
+ * for the socket.
+ */
+ sk->sk_shutdown = SHUTDOWN_MASK;
+
+ /*
+ * Set the socket state to closed, the TCP_CLOSE macro is used when
+ * closing any socket.
+ */
+ sk->sk_state = TCP_CLOSE;
+
+ /* Flush out this sockets receive queue. */
+ drain_queue(cf_sk);
+
+ /* Finally release the socket. */
+ STATE_IS_PENDING_DESTROY(cf_sk);
+ release_sock(&cf_sk->sk);
+
+ sock_put(sk);
+
+ /*
+ * The rest of the cleanup will be handled from the
+ * caif_sock_destructor
+ */
+ return 0;
+}
+
+static struct proto_ops caif_ops = {
+ .family = PF_CAIF,
+ .owner = THIS_MODULE,
+ .release = caif_release,
+ .bind = sock_no_bind,
+ .connect = caif_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = sock_no_getname,
+ .poll = caif_poll,
+ .ioctl = sock_no_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = caif_shutdown,
+ .setsockopt = setsockopt,
+ .getsockopt = getsockopt,
+ .sendmsg = caif_sendmsg,
+ .recvmsg = caif_recvmsg,
+ .mmap = sock_no_mmap,
+ .sendpage = caif_sock_no_sendpage,
+};
+
+/* This function is called when a socket is finally destroyed. */
+static void caif_sock_destructor(struct sock *sk)
+{
+ struct caifsock *cf_sk = NULL;
+ cf_sk = container_of(sk, struct caifsock, sk);
+
+ /* Error checks. */
+ caif_assert(!atomic_read(&sk->sk_wmem_alloc));
+ caif_assert(sk_unhashed(sk));
+ caif_assert(!sk->sk_socket);
+
+ if (!sock_flag(sk, SOCK_DEAD)) {
+ pr_debug("CAIF: %s(): 0x%p", __func__, sk);
+ return;
+ }
+
+ lock_sock(&(cf_sk->sk));
+
+ if (STATE_IS_OPEN(cf_sk)) {
+ pr_debug("CAIF: %s(): socket is opened (cf_sk=%p)"
+ " file_mode = 0x%x\n", __func__,
+ cf_sk, cf_sk->file_mode);
+ release_sock(&cf_sk->sk);
+ return;
+ }
+ drain_queue(cf_sk);
+ kfree(cf_sk->pktq);
+
+ if (cf_sk->debugfs_socket_dir != NULL)
+ debugfs_remove_recursive(cf_sk->debugfs_socket_dir);
+
+ release_sock(&cf_sk->sk);
+ trace_printk("CAIF: %s(): caif_sock_destructor: Removing socket %s\n",
+ __func__, cf_sk->name);
+ atomic_dec(&caif_nr_socks);
+}
+
+static int caif_create(struct net *net, struct socket *sock, int protocol,
+ int kern)
+{
+ struct sock *sk = NULL;
+ struct caifsock *cf_sk = NULL;
+ int result = 0;
+ static struct proto prot = {.name = "PF_CAIF",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct caifsock),
+ };
+
+ /*
+ * The sock->type specifies the socket type to use. The CAIF socket is
+ * a packet stream in the sence that it is packet based.
+ * CAIF trusts the reliability of the link, no resending is implemented.
+ */
+ if (sock->type != SOCK_SEQPACKET)
+ return -ESOCKTNOSUPPORT;
+ prot.slab = caif_sk_cachep;
+
+ if (net != &init_net)
+ return -EAFNOSUPPORT;
+
+ if (protocol < 0 || protocol >= CAIFPROTO_MAX)
+ return -EPROTONOSUPPORT;
+ /*
+ * Set the socket state to unconnected. The socket state is really
+ * not used at all in the net/core or socket.c but the
+ * initialization makes sure that sock->state is not uninitialized.
+ */
+ sock->state = SS_UNCONNECTED;
+
+ sk = sk_alloc(net, PF_CAIF, GFP_KERNEL, &prot);
+ if (!sk)
+ return -ENOMEM;
+
+ cf_sk = container_of(sk, struct caifsock, sk);
+
+ /* Store the protocol */
+ sk->sk_protocol = (unsigned char) protocol;
+
+ spin_lock_init(&cf_sk->read_queue_len_lock);
+
+ /* Fill in some information concerning the misc socket. */
+ snprintf(cf_sk->name, sizeof(cf_sk->name), "cf_sk%d",
+ atomic_read(&caif_nr_socks));
+
+ /*
+ * Lock in order to try to stop someone from opening the socket
+ * too early.
+ */
+ lock_sock(&(cf_sk->sk));
+
+ /* Initialize the nozero default sock structure data. */
+ sock_init_data(sock, sk);
+ sock->ops = &caif_ops;
+ sk->sk_destruct = caif_sock_destructor;
+ sk->sk_sndbuf = caif_sockbuf_size;
+ sk->sk_rcvbuf = caif_sockbuf_size;
+
+ cf_sk->pktq = cfpktq_create();
+
+ if (!cf_sk->pktq) {
+ pr_err("CAIF: %s(): queue create failed.\n", __func__);
+ result = -ENOMEM;
+ release_sock(&cf_sk->sk);
+ goto err_failed;
+ }
+ cf_sk->layer.ctrlcmd = caif_sktflowctrl_cb;
+ SET_STATE_CLOSED(cf_sk);
+ SET_PENDING_OFF(cf_sk);
+ SET_TX_FLOW_OFF(cf_sk);
+ SET_RX_FLOW_ON(cf_sk);
+
+ /* Set default options on configuration */
+ cf_sk->conn_req.priority = CAIF_PRIO_NORMAL;
+ cf_sk->conn_req.link_selector = CAIF_LINK_HIGH_BANDW;
+ cf_sk->conn_req.protocol = protocol;
+ /* Increase the number of sockets created. */
+ atomic_inc(&caif_nr_socks);
+ if (!IS_ERR(debugfsdir)) {
+ cf_sk->debugfs_socket_dir =
+ debugfs_create_dir(cf_sk->name, debugfsdir);
+ debugfs_create_u32("conn_state", S_IRUSR | S_IWUSR,
+ cf_sk->debugfs_socket_dir, &cf_sk->conn_state);
+ debugfs_create_u32("flow_state", S_IRUSR | S_IWUSR,
+ cf_sk->debugfs_socket_dir, &cf_sk->flow_state);
+ debugfs_create_u32("read_queue_len", S_IRUSR | S_IWUSR,
+ cf_sk->debugfs_socket_dir,
+ (u32 *) &cf_sk->read_queue_len);
+ debugfs_create_u32("identity", S_IRUSR | S_IWUSR,
+ cf_sk->debugfs_socket_dir,
+ (u32 *) &cf_sk->layer.id);
+ }
+ release_sock(&cf_sk->sk);
+ return 0;
+err_failed:
+ sk_free(sk);
+ return result;
+}
+
+
+static struct net_proto_family caif_family_ops = {
+ .family = PF_CAIF,
+ .create = caif_create,
+ .owner = THIS_MODULE,
+};
+
+int af_caif_init(void)
+{
+ int err;
+ err = sock_register(&caif_family_ops);
+
+ if (!err)
+ return err;
+
+ return 0;
+}
+
+static int __init caif_sktinit_module(void)
+{
+ int stat;
+#ifdef CONFIG_DEBUG_FS
+ debugfsdir = debugfs_create_dir("chnl_skt", NULL);
+ if (!IS_ERR(debugfsdir)) {
+ debugfs_create_u32("skb_inuse", S_IRUSR | S_IWUSR,
+ debugfsdir,
+ (u32 *) &cnt.skb_in_use);
+ debugfs_create_u32("skb_alloc", S_IRUSR | S_IWUSR,
+ debugfsdir,
+ (u32 *) &cnt.skb_alloc);
+ debugfs_create_u32("skb_free", S_IRUSR | S_IWUSR,
+ debugfsdir,
+ (u32 *) &cnt.skb_free);
+ debugfs_create_u32("num_sockets", S_IRUSR | S_IWUSR,
+ debugfsdir,
+ (u32 *) &caif_nr_socks);
+ debugfs_create_u32("num_open", S_IRUSR | S_IWUSR,
+ debugfsdir,
+ (u32 *) &cnt.num_open);
+ debugfs_create_u32("num_close", S_IRUSR | S_IWUSR,
+ debugfsdir,
+ (u32 *) &cnt.num_close);
+ debugfs_create_u32("num_init", S_IRUSR | S_IWUSR,
+ debugfsdir,
+ (u32 *) &cnt.num_init);
+ debugfs_create_u32("num_init_resp", S_IRUSR | S_IWUSR,
+ debugfsdir,
+ (u32 *) &cnt.num_init_resp);
+ debugfs_create_u32("num_init_fail_resp", S_IRUSR | S_IWUSR,
+ debugfsdir,
+ (u32 *) &cnt.num_init_fail_resp);
+ debugfs_create_u32("num_deinit", S_IRUSR | S_IWUSR,
+ debugfsdir,
+ (u32 *) &cnt.num_deinit);
+ debugfs_create_u32("num_deinit_resp", S_IRUSR | S_IWUSR,
+ debugfsdir,
+ (u32 *) &cnt.num_deinit_resp);
+ debugfs_create_u32("num_remote_shutdown_ind",
+ S_IRUSR | S_IWUSR, debugfsdir,
+ (u32 *) &cnt.num_remote_shutdown_ind);
+ debugfs_create_u32("num_tx_flow_off_ind", S_IRUSR | S_IWUSR,
+ debugfsdir,
+ (u32 *) &cnt.num_tx_flow_off_ind);
+ debugfs_create_u32("num_tx_flow_on_ind", S_IRUSR | S_IWUSR,
+ debugfsdir,
+ (u32 *) &cnt.num_tx_flow_on_ind);
+ debugfs_create_u32("num_rx_flow_off", S_IRUSR | S_IWUSR,
+ debugfsdir,
+ (u32 *) &cnt.num_rx_flow_off);
+ debugfs_create_u32("num_rx_flow_on", S_IRUSR | S_IWUSR,
+ debugfsdir,
+ (u32 *) &cnt.num_rx_flow_on);
+ }
+#endif
+ stat = af_caif_init();
+ if (stat) {
+ pr_err("CAIF: %s(): Failed to initialize CAIF socket layer.",
+ __func__);
+ return stat;
+ }
+ return 0;
+}
+
+static void __exit caif_sktexit_module(void)
+{
+ sock_unregister(PF_CAIF);
+ if (debugfsdir != NULL)
+ debugfs_remove_recursive(debugfsdir);
+}
+
+module_init(caif_sktinit_module);
+module_exit(caif_sktexit_module);
+
--
1.6.3.3
^ permalink raw reply related
* [PATCH net-next-2.6 v3 07/12] net-caif: add CAIF device registration functionality
From: sjur.brandeland @ 2010-02-22 22:05 UTC (permalink / raw)
To: netdev, davem, marcel
Cc: daniel.martensson, kaber, stefano.babic, randy.dunlap,
Sjur Braendeland
In-Reply-To: <1266876331-23988-7-git-send-email-sjur.brandeland@stericsson.com>
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
Changes from PATCH v2:
- Use socket address types when creating channels.
Registration and deregistration of CAIF Link Layer.
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
net/caif/caif_config_util.c | 88 ++++++++++
net/caif/caif_dev.c | 394 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 482 insertions(+), 0 deletions(-)
diff --git a/net/caif/caif_config_util.c b/net/caif/caif_config_util.c
new file mode 100644
index 0000000..af7daf8
--- /dev/null
+++ b/net/caif/caif_config_util.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <net/caif/cfctrl.h>
+#include <net/caif/cfcnfg.h>
+#include <net/caif/caif_dev.h>
+int connect_req_to_link_param(struct cfcnfg *cnfg,
+ struct caif_connect_request *s,
+ struct cfctrl_link_param *l)
+{
+ struct dev_info *dev_info;
+ enum cfcnfg_phy_preference pref;
+ memset(l, 0, sizeof(*l));
+ l->priority = s->priority;
+
+ if (s->link_name[0] != '\0')
+ l->phyid = cfcnfg_get_named(cnfg, s->link_name);
+ else {
+ switch (s->link_selector) {
+ case CAIF_LINK_HIGH_BANDW:
+ pref = CFPHYPREF_HIGH_BW;
+ break;
+ case CAIF_LINK_LOW_LATENCY:
+ pref = CFPHYPREF_LOW_LAT;
+ break;
+ default:
+ return -EINVAL;
+ }
+ dev_info = cfcnfg_get_phyid(cnfg, pref);
+ if (dev_info == NULL)
+ return -ENODEV;
+ l->phyid = dev_info->id;
+ }
+ switch (s->protocol) {
+ case CAIFPROTO_AT:
+ l->linktype = CFCTRL_SRV_VEI;
+ if (s->sockaddr.u.at.type == CAIF_ATTYPE_PLAIN)
+ l->chtype = 0x02;
+ else
+ l->chtype = s->sockaddr.u.at.type;
+ l->endpoint = 0x00;
+ break;
+ case CAIFPROTO_DATAGRAM:
+ l->linktype = CFCTRL_SRV_DATAGRAM;
+ l->chtype = 0x00;
+ l->u.datagram.connid = s->sockaddr.u.dgm.connection_id;
+ break;
+ case CAIFPROTO_DATAGRAM_LOOP:
+ l->linktype = CFCTRL_SRV_DATAGRAM;
+ l->chtype = 0x03;
+ l->endpoint = 0x00;
+ l->u.datagram.connid = s->sockaddr.u.dgm.connection_id;
+ break;
+ case CAIFPROTO_RFM:
+ l->linktype = CFCTRL_SRV_RFM;
+ l->u.datagram.connid = s->sockaddr.u.rfm.connection_id;
+ strncpy(l->u.rfm.volume, s->sockaddr.u.rfm.volume,
+ sizeof(l->u.rfm.volume)-1);
+ l->u.rfm.volume[sizeof(l->u.rfm.volume)-1] = 0;
+ break;
+ case CAIFPROTO_UTIL:
+ l->linktype = CFCTRL_SRV_UTIL;
+ l->endpoint = 0x00;
+ l->chtype = 0x00;
+ strncpy(l->u.utility.name, s->sockaddr.u.util.service,
+ sizeof(l->u.utility.name)-1);
+ l->u.utility.name[sizeof(l->u.utility.name)-1] = 0;
+ caif_assert(sizeof(l->u.utility.name) > 10);
+ l->u.utility.paramlen = s->param.size;
+ 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->param.data,
+ l->u.utility.paramlen);
+
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c
new file mode 100644
index 0000000..0a642db
--- /dev/null
+++ b/net/caif/caif_dev.c
@@ -0,0 +1,394 @@
+/*
+ * CAIF Interface registration.
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ * Borrowed heavily from file: pn_dev.c. Thanks to
+ * Remi Denis-Courmont <remi.denis-courmont@nokia.com>
+ * and Sakari Ailus <sakari.ailus@nokia.com>
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/if_arp.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <net/netns/generic.h>
+#include <net/net_namespace.h>
+#include <net/pkt_sched.h>
+#include <net/caif/caif_device.h>
+#include <net/caif/caif_dev.h>
+#include <net/caif/caif_layer.h>
+#include <net/caif/cfpkt.h>
+#include <net/caif/cfcnfg.h>
+
+MODULE_LICENSE("GPL");
+#define TIMEOUT (HZ*1000)
+
+/* Used for local tracking of the CAIF net devices */
+struct caif_device_entry {
+ struct layer layer;
+ struct list_head list;
+ atomic_t in_use;
+ atomic_t state;
+ u16 phyid;
+ struct net_device *netdev;
+ wait_queue_head_t event;
+};
+
+struct caif_device_entry_list {
+ struct list_head list;
+ spinlock_t lock;
+};
+
+struct caif_net {
+ struct caif_device_entry_list caifdevs;
+};
+
+int caif_net_id;
+struct cfcnfg *cfg;
+
+struct caif_device_entry_list *caif_device_list(struct net *net)
+{
+ struct caif_net *caifn;
+ BUG_ON(!net);
+ caifn = net_generic(net, caif_net_id);
+ BUG_ON(!caifn);
+ return &caifn->caifdevs;
+}
+
+/* Allocate new CAIF device. */
+static struct caif_device_entry *caif_device_alloc(struct net_device *dev)
+{
+ struct caif_device_entry_list *caifdevs;
+ struct caif_device_entry *caifd;
+ caifdevs = caif_device_list(dev_net(dev));
+ BUG_ON(!caifdevs);
+ caifd = kzalloc(sizeof(*caifd), GFP_ATOMIC);
+ if (!caifd)
+ return NULL;
+ caifd->netdev = dev;
+ list_add(&caifd->list, &caifdevs->list);
+ init_waitqueue_head(&caifd->event);
+ return caifd;
+}
+
+static struct caif_device_entry *caif_get(struct net_device *dev)
+{
+ struct caif_device_entry_list *caifdevs =
+ caif_device_list(dev_net(dev));
+ struct caif_device_entry *caifd;
+ BUG_ON(!caifdevs);
+ list_for_each_entry(caifd, &caifdevs->list, list) {
+ if (caifd->netdev == dev)
+ return caifd;
+ }
+ return NULL;
+}
+
+static void caif_device_destroy(struct net_device *dev)
+{
+ struct caif_device_entry_list *caifdevs =
+ caif_device_list(dev_net(dev));
+ struct caif_device_entry *caifd;
+ ASSERT_RTNL();
+ if (dev->type != ARPHRD_CAIF)
+ return;
+
+ spin_lock_bh(&caifdevs->lock);
+ caifd = caif_get(dev);
+ if (caifd == NULL) {
+ spin_unlock_bh(&caifdevs->lock);
+ return;
+ }
+
+ list_del(&caifd->list);
+ spin_unlock_bh(&caifdevs->lock);
+
+ kfree(caifd);
+ return;
+}
+
+static int transmit(struct layer *layer, struct cfpkt *pkt)
+{
+ struct caif_device_entry *caifd =
+ container_of(layer, struct caif_device_entry, layer);
+ struct sk_buff *skb, *skb2;
+ int ret = -EINVAL;
+ skb = cfpkt_tonative(pkt);
+ skb->dev = caifd->netdev;
+ /*
+ * Don't allow SKB to be destroyed upon error, but signal resend
+ * notification to clients. We can't rely on the return value as
+ * congestion (NET_XMIT_CN) sometimes drops the packet, sometimes don't.
+ */
+ if (netif_queue_stopped(caifd->netdev))
+ return -EAGAIN;
+ skb2 = skb_get(skb);
+
+ ret = dev_queue_xmit(skb2);
+
+ if (!ret)
+ kfree_skb(skb);
+ else
+ return -EAGAIN;
+
+ return 0;
+}
+
+static int modemcmd(struct layer *layr, enum caif_modemcmd ctrl)
+{
+ struct caif_device_entry *caifd;
+ struct caif_dev_common *caifdev;
+ caifd = container_of(layr, struct caif_device_entry, layer);
+ caifdev = netdev_priv(caifd->netdev);
+ if (ctrl == _CAIF_MODEMCMD_PHYIF_USEFULL) {
+ atomic_set(&caifd->in_use, 1);
+ wake_up_interruptible(&caifd->event);
+
+ } else if (ctrl == _CAIF_MODEMCMD_PHYIF_USELESS) {
+ atomic_set(&caifd->in_use, 0);
+ wake_up_interruptible(&caifd->event);
+ }
+ return 0;
+}
+
+/*
+ * Stuff received packets to associated sockets.
+ * On error, returns non-zero and releases the skb.
+ */
+static int receive(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *pkttype, struct net_device *orig_dev)
+{
+ struct net *net;
+ struct cfpkt *pkt;
+ struct caif_device_entry *caifd;
+ net = dev_net(dev);
+ pkt = cfpkt_fromnative(CAIF_DIR_IN, skb);
+ caifd = caif_get(dev);
+ if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd)
+ return NET_RX_DROP;
+
+ if (caifd->layer.up->receive(caifd->layer.up, pkt))
+ return NET_RX_DROP;
+
+ return 0;
+}
+
+static struct packet_type caif_packet_type __read_mostly = {
+ .type = cpu_to_be16(ETH_P_CAIF),
+ .func = receive,
+};
+
+static void dev_flowctrl(struct net_device *dev, int on)
+{
+ struct caif_device_entry *caifd = caif_get(dev);
+ if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd)
+ return;
+
+ caifd->layer.up->ctrlcmd(caifd->layer.up,
+ on ?
+ _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND :
+ _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND,
+ caifd->layer.id);
+}
+
+/* notify Caif of device events */
+static int caif_device_notify(struct notifier_block *me, unsigned long what,
+ void *arg)
+{
+ struct net_device *dev = arg;
+ struct caif_device_entry *caifd = NULL;
+ struct caif_dev_common *caifdev;
+ int res = -EINVAL;
+ enum cfcnfg_phy_type phy_type;
+
+ if (dev->type != ARPHRD_CAIF)
+ return 0;
+
+ switch (what) {
+ case NETDEV_REGISTER:
+ pr_info("CAIF: %s():register %s\n", __func__, dev->name);
+ caifd = caif_device_alloc(dev);
+ if (caifd == NULL)
+ break;
+ caifdev = netdev_priv(dev);
+ caifdev->flowctrl = dev_flowctrl;
+ atomic_set(&caifd->state, what);
+ res = 0;
+ break;
+
+ case NETDEV_UP:
+ pr_info("CAIF: %s(): up %s\n", __func__, dev->name);
+ caifd = caif_get(dev);
+ if (caifd == NULL)
+ break;
+ caifdev = netdev_priv(dev);
+ if (atomic_read(&caifd->state) == NETDEV_UP) {
+ pr_info("CAIF: %s():%s already up\n",
+ __func__, dev->name);
+ break;
+ }
+ atomic_set(&caifd->state, what);
+ caifd->layer.transmit = transmit;
+ caifd->layer.modemcmd = modemcmd;
+
+ if (caifdev->use_frag)
+ phy_type = CFPHYTYPE_FRAG;
+ else
+ phy_type = CFPHYTYPE_CAIF;
+
+ cfcnfg_add_phy_layer(get_caif_conf(),
+ phy_type,
+ dev,
+ &caifd->layer,
+ &caifd->phyid,
+ caifdev->link_select,
+ caifdev->use_fcs,
+ caifdev->use_stx);
+ strncpy(caifd->layer.name, dev->name,
+ sizeof(caifd->layer.name) - 1);
+ caifd->layer.name[sizeof(caifd->layer.name) - 1] = 0;
+ break;
+
+ case NETDEV_GOING_DOWN:
+ caifd = caif_get(dev);
+ if (caifd == NULL)
+ break;
+ pr_info("CAIF: %s():going down %s\n", __func__, dev->name);
+ atomic_set(&caifd->state, what);
+ if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd)
+ return -EINVAL;
+ caifd->layer.up->ctrlcmd(caifd->layer.up,
+ _CAIF_CTRLCMD_PHYIF_DOWN_IND,
+ caifd->layer.id);
+ res = wait_event_interruptible_timeout(caifd->event,
+ atomic_read(&caifd->in_use) == 0,
+ TIMEOUT);
+ break;
+
+ case NETDEV_DOWN:
+ caifd = caif_get(dev);
+ if (caifd == NULL)
+ break;
+ pr_info("CAIF: %s(): down %s\n", __func__, dev->name);
+ if (atomic_read(&caifd->in_use))
+ pr_warning("CAIF: %s(): "
+ "Unregistering an active CAIF device: %s\n",
+ __func__, dev->name);
+ cfcnfg_del_phy_layer(get_caif_conf(), &caifd->layer);
+ atomic_set(&caifd->state, what);
+ break;
+
+ case NETDEV_UNREGISTER:
+ caifd = caif_get(dev);
+ pr_info("CAIF: %s(): unregister %s\n", __func__, dev->name);
+ atomic_set(&caifd->state, what);
+ caif_device_destroy(dev);
+ break;
+ }
+ return 0;
+}
+
+static struct notifier_block caif_device_notifier = {
+ .notifier_call = caif_device_notify,
+ .priority = 0,
+};
+
+
+struct cfcnfg *get_caif_conf(void)
+{
+ return cfg;
+}
+EXPORT_SYMBOL(get_caif_conf);
+
+int caif_connect_client(struct caif_connect_request *conn_req,
+ struct layer *client_layer)
+{
+ struct cfctrl_link_param param;
+ if (connect_req_to_link_param(get_caif_conf(), conn_req, ¶m) == 0)
+ /* Hook up the adaptation layer. */
+ return cfcnfg_add_adaptation_layer(get_caif_conf(),
+ ¶m, client_layer);
+
+ return -EINVAL;
+
+ caif_assert(0);
+}
+EXPORT_SYMBOL(caif_connect_client);
+
+int caif_disconnect_client(struct layer *adap_layer)
+{
+ return cfcnfg_del_adapt_layer(get_caif_conf(), adap_layer);
+}
+EXPORT_SYMBOL(caif_disconnect_client);
+
+/* Per-namespace Caif devices handling */
+static int caif_init_net(struct net *net)
+{
+ struct caif_net *caifn = net_generic(net, caif_net_id);
+ INIT_LIST_HEAD(&caifn->caifdevs.list);
+ spin_lock_init(&caifn->caifdevs.lock);
+ return 0;
+}
+
+static void caif_exit_net(struct net *net)
+{
+ struct net_device *dev;
+ int res;
+ rtnl_lock();
+ for_each_netdev(net, dev) {
+ if (dev->type != ARPHRD_CAIF)
+ continue;
+ res = dev_close(dev);
+ caif_device_destroy(dev);
+ }
+ rtnl_unlock();
+}
+
+static struct pernet_operations caif_net_ops = {
+ .init = caif_init_net,
+ .exit = caif_exit_net,
+ .id = &caif_net_id,
+ .size = sizeof(struct caif_net),
+};
+
+/* Initialize Caif devices list */
+int __init caif_device_init(void)
+{
+ int result;
+ cfg = cfcnfg_create();
+ if (!cfg) {
+ pr_warning("CAIF: %s(): can't create cfcnfg.\n", __func__);
+ goto err_cfcnfg_create_failed;
+ }
+ result = register_pernet_device(&caif_net_ops);
+
+ if (result) {
+ kfree(cfg);
+ cfg = NULL;
+ return result;
+ }
+ dev_add_pack(&caif_packet_type);
+ register_netdevice_notifier(&caif_device_notifier);
+
+ return result;
+err_cfcnfg_create_failed:
+ return -ENODEV;
+}
+
+void __exit caif_device_exit(void)
+{
+ dev_remove_pack(&caif_packet_type);
+ unregister_pernet_device(&caif_net_ops);
+ unregister_netdevice_notifier(&caif_device_notifier);
+ cfcnfg_remove(cfg);
+}
+
+module_init(caif_device_init);
+module_exit(caif_device_exit);
--
1.6.3.3
^ permalink raw reply related
* [PATCH net-next-2.6 v3 06/12] net-caif: add CAIF generic caif support functions
From: sjur.brandeland @ 2010-02-22 22:05 UTC (permalink / raw)
To: netdev, davem, marcel
Cc: daniel.martensson, kaber, stefano.babic, randy.dunlap,
Sjur Braendeland
In-Reply-To: <1266876331-23988-6-git-send-email-sjur.brandeland@stericsson.com>
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
Changes from PATCHv2:
- Bugfix in related to SO_BINDTODEVICE.
Support functions for the caif protocol stack:
cfcnfg.c - CAIF Configuration Module used for
adding and removing drivers and connection
cfpkt_skbuff.c - CAIF Packet layer (SKB helper functions)
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
net/caif/cfcnfg.c | 529 +++++++++++++++++++++++++++++++++++++++++
net/caif/cfpkt_skbuff.c | 595 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 1124 insertions(+), 0 deletions(-)
diff --git a/net/caif/cfcnfg.c b/net/caif/cfcnfg.c
new file mode 100644
index 0000000..4f86ae3
--- /dev/null
+++ b/net/caif/cfcnfg.c
@@ -0,0 +1,529 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <net/caif/caif_layer.h>
+#include <net/caif/cfpkt.h>
+#include <net/caif/cfcnfg.h>
+#include <net/caif/cfctrl.h>
+#include <net/caif/cfmuxl.h>
+#include <net/caif/cffrml.h>
+#include <net/caif/cfserl.h>
+#include <net/caif/cfsrvl.h>
+
+#include <linux/module.h>
+#include <asm/atomic.h>
+
+#define MAX_PHY_LAYERS 7
+#define PHY_NAME_LEN 20
+
+#define container_obj(layr) container_of(layr, struct cfcnfg, layer)
+
+/* Information about CAIF physical interfaces held by Config Module in order
+ * to manage physical interfaces
+ */
+struct cfcnfg_phyinfo {
+ /* Pointer to the layer below the MUX (framing layer) */
+ struct layer *frm_layer;
+ /* Pointer to the lowest actual physical layer */
+ struct layer *phy_layer;
+ /* Unique identifier of the physical interface */
+ unsigned int id;
+ /* Preference of the physical in interface */
+ enum cfcnfg_phy_preference pref;
+
+ /* Reference count, number of channels using the device */
+ int phy_ref_count;
+
+ /* Information about the physical device */
+ struct dev_info dev_info;
+};
+
+struct cfcnfg {
+ struct layer layer;
+ struct layer *ctrl;
+ struct layer *mux;
+ u8 last_phyid;
+ struct cfcnfg_phyinfo phy_layers[MAX_PHY_LAYERS];
+};
+
+static void cncfg_linkup_rsp(struct layer *layer, u8 linkid,
+ enum cfctrl_srv serv, u8 phyid,
+ struct layer *adapt_layer);
+static void cncfg_linkdestroy_rsp(struct layer *layer, u8 linkid,
+ struct layer *client_layer);
+static void cncfg_reject_rsp(struct layer *layer, u8 linkid,
+ struct layer *adapt_layer);
+static void cfctrl_resp_func(void);
+static void cfctrl_enum_resp(void);
+
+struct cfcnfg *cfcnfg_create()
+{
+ struct cfcnfg *this;
+ struct cfctrl_rsp *resp;
+ /* Initiate this layer */
+ this = kmalloc(sizeof(struct cfcnfg), GFP_ATOMIC);
+ if (!this) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return NULL;
+ }
+ memset(this, 0, sizeof(struct cfcnfg));
+ this->mux = cfmuxl_create();
+ if (!this->mux)
+ goto out_of_mem;
+ this->ctrl = cfctrl_create();
+ if (!this->ctrl)
+ goto out_of_mem;
+ /* Initiate response functions */
+ resp = cfctrl_get_respfuncs(this->ctrl);
+ 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;
+
+ this->last_phyid = 1;
+
+ cfmuxl_set_uplayer(this->mux, this->ctrl, 0);
+ layer_set_dn(this->ctrl, this->mux);
+ layer_set_up(this->ctrl, this);
+ return this;
+out_of_mem:
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ kfree(this->mux);
+ kfree(this->ctrl);
+ kfree(this);
+ return NULL;
+}
+EXPORT_SYMBOL(cfcnfg_create);
+
+void cfcnfg_remove(struct cfcnfg *cfg)
+{
+ if (cfg) {
+ kfree(cfg->mux);
+ kfree(cfg->ctrl);
+ kfree(cfg);
+ }
+}
+
+static void cfctrl_resp_func(void)
+{
+}
+
+static void cfctrl_enum_resp(void)
+{
+}
+
+struct dev_info *cfcnfg_get_phyid(struct cfcnfg *cnfg,
+ enum cfcnfg_phy_preference 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) {
+ caif_assert(cnfg->phy_layers != NULL);
+ caif_assert(cnfg->phy_layers[i].id == i);
+ return &cnfg->phy_layers[i].dev_info;
+ }
+ }
+ /* Otherwise just return something */
+ for (i = 1; i < MAX_PHY_LAYERS; i++) {
+ if (cnfg->phy_layers[i].id == i) {
+ caif_assert(cnfg->phy_layers != NULL);
+ caif_assert(cnfg->phy_layers[i].id == i);
+ return &cnfg->phy_layers[i].dev_info;
+ }
+ }
+
+ return NULL;
+}
+
+static struct cfcnfg_phyinfo *cfcnfg_get_phyinfo(struct cfcnfg *cnfg,
+ u8 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(struct cfcnfg *cnfg, char *name)
+{
+ int i;
+
+ /* Try to match with specified name */
+ for (i = 0; i < MAX_PHY_LAYERS; i++) {
+ if (cnfg->phy_layers[i].frm_layer != NULL
+ && strcmp(cnfg->phy_layers[i].phy_layer->name,
+ name) == 0)
+ return cnfg->phy_layers[i].frm_layer->id;
+ }
+ return 0;
+}
+
+/*
+ * NOTE: What happens on destroy failure:
+ * 1a) No response - Too early
+ * This will not happen 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 request
+ * 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
+ */
+
+int cfcnfg_del_adapt_layer(struct cfcnfg *cnfg, struct layer *adap_layer)
+{
+ u8 channel_id = 0;
+ int ret = 0;
+ struct cfcnfg_phyinfo *phyinfo = NULL;
+ u8 phyid = 0;
+
+ caif_assert(adap_layer != NULL);
+ channel_id = adap_layer->id;
+ if (channel_id == 0) {
+ pr_err("CAIF: %s():adap_layer->id is 0\n", __func__);
+ ret = -ENOTCONN;
+ goto end;
+ }
+
+ if (adap_layer->dn == NULL) {
+ pr_err("CAIF: %s():adap_layer->dn is NULL\n", __func__);
+ ret = -ENODEV;
+ goto end;
+ }
+
+ if (adap_layer->dn != NULL)
+ phyid = cfsrvl_getphyid(adap_layer->dn);
+
+ phyinfo = cfcnfg_get_phyinfo(cnfg, phyid);
+ if (phyinfo == NULL) {
+ pr_warning("CAIF: %s(): No interface to send disconnect to\n",
+ __func__);
+ ret = -ENODEV;
+ goto end;
+ }
+
+ if (phyinfo->id != phyid
+ || phyinfo->phy_layer->id != phyid
+ || phyinfo->frm_layer->id != phyid) {
+
+ pr_err("CAIF: %s(): Inconsistency in phy registration\n",
+ __func__);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ ret = cfctrl_linkdown_req(cnfg->ctrl, channel_id, adap_layer);
+
+end:
+ if (phyinfo != NULL && --phyinfo->phy_ref_count == 0 &&
+ phyinfo->phy_layer != NULL &&
+ phyinfo->phy_layer->modemcmd != NULL) {
+ phyinfo->phy_layer->modemcmd(phyinfo->phy_layer,
+ _CAIF_MODEMCMD_PHYIF_USELESS);
+ }
+ return ret;
+
+}
+EXPORT_SYMBOL(cfcnfg_del_adapt_layer);
+
+static void cncfg_linkdestroy_rsp(struct layer *layer, u8 linkid,
+ struct layer *client_layer)
+{
+ struct cfcnfg *cnfg = container_obj(layer);
+ struct layer *servl;
+
+ /*
+ * 1) Remove service from the MUX layer. The MUX must
+ * guarante that no more payload sent "upwards" (receive)
+ */
+ servl = cfmuxl_remove_uplayer(cnfg->mux, linkid);
+
+ if (servl == NULL) {
+ pr_err("CAIF: %s(): PROTOCOL ERROR "
+ "- Error removing service_layer Linkid(%d)",
+ __func__, linkid);
+ return;
+ }
+ caif_assert(linkid == servl->id);
+
+ if (servl != client_layer && servl->up != client_layer) {
+ pr_err("CAIF: %s(): Error removing service_layer "
+ "Linkid(%d) %p %p",
+ __func__, linkid, (void *) servl,
+ (void *) client_layer);
+ return;
+ }
+
+ /*
+ * 2) DEINIT_RSP must guarantee that no more packets are transmitted
+ * from client (adap_layer) when it returns.
+ */
+
+ if (servl->ctrlcmd == NULL) {
+ pr_err("CAIF: %s(): Error servl->ctrlcmd == NULL", __func__);
+ return;
+ }
+
+ servl->ctrlcmd(servl, CAIF_CTRLCMD_DEINIT_RSP, 0);
+
+ /* 3) It is now safe to destroy the service layer. */
+ cfservl_destroy(servl);
+}
+
+/*
+ * NOTE: What happens on linksetup failure:
+ * 1a) No response - Too early
+ * This will not happen 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
+ * Handled in cnfg_reject_rsp
+ * 3) Not able to match on request
+ * 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
+ */
+
+int
+cfcnfg_add_adaptation_layer(struct cfcnfg *cnfg,
+ struct cfctrl_link_param *param,
+ struct layer *adap_layer)
+{
+ struct layer *frml;
+ if (adap_layer == NULL) {
+ pr_err("CAIF: %s(): adap_layer is zero", __func__);
+ return -EINVAL;
+ }
+ if (adap_layer->receive == NULL) {
+ pr_err("CAIF: %s(): adap_layer->receive is NULL", __func__);
+ return -EINVAL;
+ }
+ if (adap_layer->ctrlcmd == NULL) {
+ pr_err("CAIF: %s(): adap_layer->ctrlcmd == NULL", __func__);
+ return -EINVAL;
+ }
+ frml = cnfg->phy_layers[param->phyid].frm_layer;
+ if (frml == NULL) {
+ pr_err("CAIF: %s(): Specified PHY type does not exist!",
+ __func__);
+ return -ENODEV;
+ }
+ caif_assert(param->phyid == cnfg->phy_layers[param->phyid].id);
+ caif_assert(cnfg->phy_layers[param->phyid].frm_layer->id ==
+ param->phyid);
+ caif_assert(cnfg->phy_layers[param->phyid].phy_layer->id ==
+ param->phyid);
+ /* FIXME: ENUMERATE INITIALLY WHEN ACTIVATING PHYSICAL INTERFACE */
+ cfctrl_enum_req(cnfg->ctrl, param->phyid);
+ cfctrl_linkup_request(cnfg->ctrl, param, adap_layer);
+ return 0;
+}
+EXPORT_SYMBOL(cfcnfg_add_adaptation_layer);
+
+static void cncfg_reject_rsp(struct layer *layer, u8 linkid,
+ struct layer *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(struct layer *layer, u8 linkid, enum cfctrl_srv serv,
+ u8 phyid, struct layer *adapt_layer)
+{
+ struct cfcnfg *cnfg = container_obj(layer);
+ struct layer *servicel = NULL;
+ struct cfcnfg_phyinfo *phyinfo;
+ if (adapt_layer == NULL) {
+ pr_err("CAIF: %s(): PROTOCOL ERROR "
+ "- LinkUp Request/Response did not match\n", __func__);
+ return;
+ }
+
+ caif_assert(cnfg != NULL);
+ caif_assert(phyid != 0);
+ phyinfo = &cnfg->phy_layers[phyid];
+ caif_assert(phyinfo != NULL);
+ caif_assert(phyinfo->id == phyid);
+ caif_assert(phyinfo->phy_layer != NULL);
+ caif_assert(phyinfo->phy_layer->id == phyid);
+
+ if (phyinfo != NULL &&
+ phyinfo->phy_ref_count++ == 0 &&
+ phyinfo->phy_layer != NULL &&
+ phyinfo->phy_layer->modemcmd != NULL) {
+ caif_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, &phyinfo->dev_info);
+ break;
+ case CFCTRL_SRV_DATAGRAM:
+ servicel = cfdgml_create(linkid, &phyinfo->dev_info);
+ break;
+ case CFCTRL_SRV_RFM:
+ servicel = cfrfml_create(linkid, &phyinfo->dev_info);
+ break;
+ case CFCTRL_SRV_UTIL:
+ servicel = cfutill_create(linkid, &phyinfo->dev_info);
+ break;
+ case CFCTRL_SRV_VIDEO:
+ servicel = cfvidl_create(linkid, &phyinfo->dev_info);
+ break;
+ case CFCTRL_SRV_DBG:
+ servicel = cfdbgl_create(linkid, &phyinfo->dev_info);
+ break;
+ default:
+ pr_err("CAIF: %s(): Protocol error. "
+ "Link setup response - unknown channel type\n",
+ __func__);
+ return;
+ }
+ if (!servicel) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return;
+ }
+ layer_set_dn(servicel, cnfg->mux);
+ cfmuxl_set_uplayer(cnfg->mux, servicel, linkid);
+ layer_set_up(servicel, adapt_layer);
+ layer_set_dn(adapt_layer, servicel);
+ servicel->ctrlcmd(servicel, CAIF_CTRLCMD_INIT_RSP, 0);
+}
+
+void
+cfcnfg_add_phy_layer(struct cfcnfg *cnfg, enum cfcnfg_phy_type phy_type,
+ void *dev, struct layer *phy_layer, u16 *phyid,
+ enum cfcnfg_phy_preference pref,
+ bool fcs, bool stx)
+{
+ struct layer *frml;
+ struct layer *phy_driver = NULL;
+ int i;
+
+
+ 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) {
+ pr_err("CAIF: %s(): No Available PHY ID\n", __func__);
+ return;
+ }
+
+ switch (phy_type) {
+ case CFPHYTYPE_FRAG:
+ phy_driver =
+ cfserl_create(CFPHYTYPE_FRAG, *phyid, stx);
+ if (!phy_driver) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return;
+ }
+
+ break;
+ case CFPHYTYPE_CAIF:
+ phy_driver = NULL;
+ break;
+ default:
+ pr_err("CAIF: %s(): %d", __func__, phy_type);
+ return;
+ break;
+ }
+
+ phy_layer->id = *phyid;
+ cnfg->phy_layers[*phyid].pref = pref;
+ cnfg->phy_layers[*phyid].id = *phyid;
+ cnfg->phy_layers[*phyid].dev_info.id = *phyid;
+ cnfg->phy_layers[*phyid].dev_info.dev = dev;
+ 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, fcs);
+ if (!frml) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return;
+ }
+ cnfg->phy_layers[*phyid].frm_layer = frml;
+ cfmuxl_set_dnlayer(cnfg->mux, frml, *phyid);
+ layer_set_up(frml, cnfg->mux);
+
+ if (phy_driver != NULL) {
+ phy_driver->id = *phyid;
+ 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);
+ } else {
+ layer_set_dn(frml, phy_layer);
+ layer_set_up(phy_layer, frml);
+ }
+}
+EXPORT_SYMBOL(cfcnfg_add_phy_layer);
+
+int cfcnfg_del_phy_layer(struct cfcnfg *cnfg, struct layer *phy_layer)
+{
+ struct layer *frml, *frml_dn;
+ u16 phyid;
+ phyid = phy_layer->id;
+ caif_assert(phyid == cnfg->phy_layers[phyid].id);
+ caif_assert(phy_layer == cnfg->phy_layers[phyid].phy_layer);
+ caif_assert(phy_layer->id == phyid);
+ caif_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);
+ frml_dn = frml->dn;
+ cffrml_set_uplayer(frml, NULL);
+ cffrml_set_dnlayer(frml, NULL);
+ cffrml_destroy(frml);
+
+ if (phy_layer != frml_dn) {
+ layer_set_up(frml_dn, NULL);
+ layer_set_dn(frml_dn, NULL);
+ kfree(frml_dn);
+ }
+ layer_set_up(phy_layer, NULL);
+ return 0;
+}
+EXPORT_SYMBOL(cfcnfg_del_phy_layer);
diff --git a/net/caif/cfpkt_skbuff.c b/net/caif/cfpkt_skbuff.c
new file mode 100644
index 0000000..92e963f
--- /dev/null
+++ b/net/caif/cfpkt_skbuff.c
@@ -0,0 +1,595 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/string.h>
+#include <linux/skbuff.h>
+#include <linux/hardirq.h>
+#include <net/caif/cfpkt.h>
+
+#define PKT_PREFIX CAIF_NEEDED_HEADROOM
+#define PKT_POSTFIX CAIF_NEEDED_TAILROOM
+#define PKT_LEN_WHEN_EXTENDING 128
+#define PKT_ERROR(pkt, errmsg) do { \
+ cfpkt_priv(pkt)->erronous = true; \
+ skb_reset_tail_pointer(&pkt->skb); \
+ pr_warning("CAIF: " errmsg);\
+ } while (0)
+
+struct cfpktq {
+ struct sk_buff_head head;
+ atomic_t count;
+ spinlock_t lock;
+};
+
+/*
+ * net/caif/ is generic and does not
+ * understand SKB, so we do this typecast
+ */
+struct cfpkt {
+ struct sk_buff skb;
+};
+
+/* Private data inside SKB */
+struct cfpkt_priv_data {
+ struct dev_info dev_info;
+ bool erronous;
+};
+
+inline struct cfpkt_priv_data *cfpkt_priv(struct cfpkt *pkt)
+{
+ return (struct cfpkt_priv_data *) pkt->skb.cb;
+}
+
+inline bool is_erronous(struct cfpkt *pkt)
+{
+ return cfpkt_priv(pkt)->erronous;
+}
+
+inline struct sk_buff *pkt_to_skb(struct cfpkt *pkt)
+{
+ return &pkt->skb;
+}
+
+inline struct cfpkt *skb_to_pkt(struct sk_buff *skb)
+{
+ return (struct cfpkt *) skb;
+}
+
+atomic_t cfpkt_packet_count;
+EXPORT_SYMBOL(cfpkt_packet_count);
+
+struct cfpkt *cfpkt_fromnative(enum caif_direction dir, void *nativepkt)
+{
+ struct cfpkt *pkt = skb_to_pkt(nativepkt);
+ cfpkt_priv(pkt)->erronous = false;
+ atomic_inc(&cfpkt_packet_count);
+ return pkt;
+}
+EXPORT_SYMBOL(cfpkt_fromnative);
+
+void *cfpkt_tonative(struct cfpkt *pkt)
+{
+ return (void *) pkt;
+}
+EXPORT_SYMBOL(cfpkt_tonative);
+
+struct cfpkt *cfpkt_create_pfx(u16 len, u16 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);
+ atomic_inc(&cfpkt_packet_count);
+ return skb_to_pkt(skb);
+}
+
+inline struct cfpkt *cfpkt_create(u16 len)
+{
+ return cfpkt_create_pfx(len + PKT_POSTFIX, PKT_PREFIX);
+}
+EXPORT_SYMBOL(cfpkt_create);
+
+void cfpkt_destroy(struct cfpkt *pkt)
+{
+ struct sk_buff *skb = pkt_to_skb(pkt);
+ atomic_dec(&cfpkt_packet_count);
+ caif_assert(atomic_read(&cfpkt_packet_count) >= 0);
+ kfree_skb(skb);
+}
+EXPORT_SYMBOL(cfpkt_destroy);
+
+inline bool cfpkt_more(struct cfpkt *pkt)
+{
+ struct sk_buff *skb = pkt_to_skb(pkt);
+ return skb->len > 0;
+}
+EXPORT_SYMBOL(cfpkt_more);
+
+int cfpkt_peek_head(struct cfpkt *pkt, void *data, u16 len)
+{
+ struct sk_buff *skb = pkt_to_skb(pkt);
+ if (skb->tail - skb->data >= len) {
+ memcpy(data, skb->data, len);
+ return 0;
+ }
+ return !cfpkt_extr_head(pkt, data, len) &&
+ !cfpkt_add_head(pkt, data, len);
+}
+EXPORT_SYMBOL(cfpkt_peek_head);
+
+int cfpkt_extr_head(struct cfpkt *pkt, void *data, u16 len)
+{
+ struct sk_buff *skb = pkt_to_skb(pkt);
+ u8 *from;
+ if (unlikely(is_erronous(pkt)))
+ return -EPROTO;
+
+ if (unlikely(len > skb->len)) {
+ PKT_ERROR(pkt, "cfpkt_extr_head read beyond end of packet\n");
+ return -EPROTO;
+ }
+
+ if (unlikely(len > skb_headlen(skb))) {
+ if (unlikely(skb_linearize(skb) != 0)) {
+ PKT_ERROR(pkt, "cfpkt_extr_head linearize failed\n");
+ return -EPROTO;
+ }
+ }
+ from = skb_pull(skb, len);
+ from -= len;
+ memcpy(data, from, len);
+ return 0;
+}
+EXPORT_SYMBOL(cfpkt_extr_head);
+
+int cfpkt_extr_trail(struct cfpkt *pkt, void *dta, u16 len)
+{
+ struct sk_buff *skb = pkt_to_skb(pkt);
+ u8 *data = dta;
+ u8 *from;
+ if (unlikely(is_erronous(pkt)))
+ return -EPROTO;
+
+ if (unlikely(skb_linearize(skb) != 0)) {
+ PKT_ERROR(pkt, "cfpkt_extr_trail linearize failed\n");
+ return -EPROTO;
+ }
+ if (unlikely(skb->data + len > skb_tail_pointer(skb))) {
+ PKT_ERROR(pkt, "cfpkt_extr_trail read beyond end of packet\n");
+ return -EPROTO;
+ }
+ from = skb_tail_pointer(skb) - len;
+ skb_trim(skb, skb->len - len);
+ memcpy(data, from, len);
+ return 0;
+}
+EXPORT_SYMBOL(cfpkt_extr_trail);
+
+int cfpkt_pad_trail(struct cfpkt *pkt, u16 len)
+{
+ return cfpkt_add_body(pkt, NULL, len);
+}
+EXPORT_SYMBOL(cfpkt_pad_trail);
+
+int cfpkt_add_body(struct cfpkt *pkt, const void *data, u16 len)
+{
+ struct sk_buff *skb = pkt_to_skb(pkt);
+ struct sk_buff *lastskb;
+ u8 *to;
+ u16 addlen = 0;
+
+
+ if (unlikely(is_erronous(pkt)))
+ return -EPROTO;
+
+ lastskb = skb;
+
+ /* Check whether 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;
+ }
+
+ /* Check whether 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)) {
+ PKT_ERROR(pkt, "cfpkt_add_body: cow failed\n");
+ return -EPROTO;
+ }
+ /*
+ * 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) {
+ pr_warning("CAIF: %s(): Packet is non-linear\n",
+ __func__);
+ 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);
+ return 0;
+}
+EXPORT_SYMBOL(cfpkt_add_body);
+
+inline int cfpkt_addbdy(struct cfpkt *pkt, u8 data)
+{
+ return cfpkt_add_body(pkt, &data, 1);
+}
+EXPORT_SYMBOL(cfpkt_addbdy);
+
+int cfpkt_add_head(struct cfpkt *pkt, const void *data2, u16 len)
+{
+ struct sk_buff *skb = pkt_to_skb(pkt);
+ struct sk_buff *lastskb;
+ u8 *to;
+ const u8 *data = data2;
+ if (unlikely(is_erronous(pkt)))
+ return -EPROTO;
+ if (unlikely(skb_headroom(skb) < len)) {
+ PKT_ERROR(pkt, "cfpkt_add_head: no headroom\n");
+ return -EPROTO;
+ }
+
+ /* Make sure data is writable */
+ if (unlikely(skb_cow_data(skb, 0, &lastskb) < 0)) {
+ PKT_ERROR(pkt, "cfpkt_add_head: cow failed\n");
+ return -EPROTO;
+ }
+
+ to = skb_push(skb, len);
+ memcpy(to, data, len);
+ return 0;
+}
+EXPORT_SYMBOL(cfpkt_add_head);
+
+inline int cfpkt_add_trail(struct cfpkt *pkt, const void *data, u16 len)
+{
+ return cfpkt_add_body(pkt, data, len);
+}
+EXPORT_SYMBOL(cfpkt_add_trail);
+
+inline u16 cfpkt_getlen(struct cfpkt *pkt)
+{
+ struct sk_buff *skb = pkt_to_skb(pkt);
+ return skb->len;
+}
+EXPORT_SYMBOL(cfpkt_getlen);
+
+inline u16 cfpkt_iterate(struct cfpkt *pkt,
+ u16 (*iter_func)(u16, void *, __u16),
+ u16 data)
+{
+ /*
+ * Don't care about the performance hit of linearizing,
+ * Checksum should not be used on high-speed interfaces anyway.
+ */
+ if (unlikely(is_erronous(pkt)))
+ return -EPROTO;
+ if (unlikely(skb_linearize(&pkt->skb) != 0)) {
+ PKT_ERROR(pkt, "cfpkt_iterate: linearize failed\n");
+ return -EPROTO;
+ }
+ return iter_func(data, pkt->skb.data, cfpkt_getlen(pkt));
+}
+EXPORT_SYMBOL(cfpkt_iterate);
+
+int cfpkt_setlen(struct cfpkt *pkt, u16 len)
+{
+ struct sk_buff *skb = pkt_to_skb(pkt);
+
+
+ if (unlikely(is_erronous(pkt)))
+ return -EPROTO;
+
+ if (likely(len <= skb->len)) {
+ if (unlikely(skb->data_len))
+ ___pskb_trim(skb, len);
+ else
+ skb_trim(skb, len);
+
+ return cfpkt_getlen(pkt);
+ }
+
+ /* Need to expand SKB */
+ if (unlikely(!cfpkt_pad_trail(pkt, len - skb->len)))
+ PKT_ERROR(pkt, "cfpkt_setlen: skb_pad_trail failed\n");
+
+ return cfpkt_getlen(pkt);
+}
+EXPORT_SYMBOL(cfpkt_setlen);
+
+struct cfpkt *cfpkt_create_uplink(const unsigned char *data, unsigned int len)
+{
+ struct cfpkt *pkt = cfpkt_create_pfx(len + PKT_POSTFIX, PKT_PREFIX);
+ if (unlikely(data != NULL))
+ cfpkt_add_body(pkt, data, len);
+ return pkt;
+}
+EXPORT_SYMBOL(cfpkt_create_uplink);
+
+
+struct cfpkt *cfpkt_append(struct cfpkt *dstpkt,
+ struct cfpkt *addpkt,
+ u16 expectlen)
+{
+ struct sk_buff *dst = pkt_to_skb(dstpkt);
+ struct sk_buff *add = pkt_to_skb(addpkt);
+ u16 addlen = add->tail - add->data;
+ u16 neededtailspace;
+ struct sk_buff *tmp;
+ u16 dstlen;
+ u16 createlen;
+ if (unlikely(is_erronous(dstpkt) || is_erronous(addpkt))) {
+ cfpkt_destroy(addpkt);
+ return dstpkt;
+ }
+ if (expectlen > addlen)
+ neededtailspace = expectlen;
+ else
+ neededtailspace = addlen;
+
+ if (dst->tail + neededtailspace > dst->end) {
+ /* Create a dumplicate of 'dst' with more tail space */
+ dstlen = dst->tail - dst->data;
+ createlen = dstlen + neededtailspace;
+ tmp = pkt_to_skb(
+ cfpkt_create(createlen + PKT_PREFIX + PKT_POSTFIX));
+ if (!tmp)
+ return NULL;
+ tmp->tail = tmp->data + dstlen;
+ tmp->len = dstlen;
+ memcpy(tmp->data, dst->data, dstlen);
+ cfpkt_destroy(dstpkt);
+ dst = tmp;
+ }
+ memcpy(dst->tail, add->data, add->tail - add->data);
+ cfpkt_destroy(addpkt);
+ dst->tail += addlen;
+ dst->len += addlen;
+ return skb_to_pkt(dst);
+}
+EXPORT_SYMBOL(cfpkt_append);
+
+struct cfpkt *cfpkt_split(struct cfpkt *pkt, u16 pos)
+{
+ struct sk_buff *skb2;
+ struct sk_buff *skb = pkt_to_skb(pkt);
+ u8 *split = skb->data + pos;
+ u16 len2nd = skb->tail - split;
+
+ if (unlikely(is_erronous(pkt)))
+ return NULL;
+
+ if (skb->data + pos > skb->tail) {
+ PKT_ERROR(pkt,
+ "cfpkt_split: trying to split beyond end of packet");
+ return NULL;
+ }
+
+ /* Create a new packet for the second part of the data */
+ skb2 = pkt_to_skb(
+ cfpkt_create_pfx(len2nd + PKT_PREFIX + PKT_POSTFIX,
+ PKT_PREFIX));
+
+ if (skb2 == NULL)
+ return NULL;
+
+ /* Reduce the length of the original packet */
+ skb->tail = split;
+ skb->len = pos;
+
+ memcpy(skb2->data, split, len2nd);
+ skb2->tail += len2nd;
+ skb2->len += len2nd;
+ return skb_to_pkt(skb2);
+}
+EXPORT_SYMBOL(cfpkt_split);
+
+
+char *cfpkt_log_pkt(struct cfpkt *pkt, char *buf, int buflen)
+{
+ struct sk_buff *skb = pkt_to_skb(pkt);
+ char *p = buf;
+ int i;
+
+ /*
+ * Sanity check buffer length, it needs to be at least as large as
+ * the header info: ~=50+ bytes
+ */
+ if (buflen < 50)
+ return NULL;
+
+ snprintf(buf, buflen, "%s: pkt:%p len:%d(%d+%d) {%d,%d} data: [",
+ is_erronous(pkt) ? "ERRONOUS-SKB" :
+ (skb->data_len != 0 ? "COMPLEX-SKB" : "SKB"),
+ skb,
+ skb->len,
+ skb->tail - skb->data,
+ skb->data_len,
+ skb->data - skb->head, skb->tail - skb->head);
+ p = buf + strlen(buf);
+
+ for (i = 0; i < skb->tail - skb->data && i < 300; 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;
+}
+EXPORT_SYMBOL(cfpkt_log_pkt);
+
+int cfpkt_raw_append(struct cfpkt *pkt, void **buf, unsigned int buflen)
+{
+ struct sk_buff *skb = pkt_to_skb(pkt);
+ struct sk_buff *lastskb;
+
+ caif_assert(buf != NULL);
+ if (unlikely(is_erronous(pkt)))
+ return -EPROTO;
+ /* Make sure SKB is writable */
+ if (unlikely(skb_cow_data(skb, 0, &lastskb) < 0)) {
+ PKT_ERROR(pkt, "cfpkt_raw_append: skb_cow_data failed\n");
+ return -EPROTO;
+ }
+
+ if (unlikely(skb_linearize(skb) != 0)) {
+ PKT_ERROR(pkt, "cfpkt_raw_append: linearize failed\n");
+ return -EPROTO;
+ }
+
+ if (unlikely(skb_tailroom(skb) < buflen)) {
+ PKT_ERROR(pkt, "cfpkt_raw_append: buffer too short - failed\n");
+ return -EPROTO;
+ }
+
+ *buf = skb_put(skb, buflen);
+ return 1;
+}
+EXPORT_SYMBOL(cfpkt_raw_append);
+
+int cfpkt_raw_extract(struct cfpkt *pkt, void **buf, unsigned int buflen)
+{
+ struct sk_buff *skb = pkt_to_skb(pkt);
+
+ caif_assert(buf != NULL);
+ if (unlikely(is_erronous(pkt)))
+ return -EPROTO;
+
+ if (unlikely(buflen > skb->len)) {
+ PKT_ERROR(pkt, "cfpkt_raw_extract: buflen too large "
+ "- failed\n");
+ return -EPROTO;
+ }
+
+ if (unlikely(buflen > skb_headlen(skb))) {
+ if (unlikely(skb_linearize(skb) != 0)) {
+ PKT_ERROR(pkt, "cfpkt_raw_extract: linearize failed\n");
+ return -EPROTO;
+ }
+ }
+
+ *buf = skb->data;
+ skb_pull(skb, buflen);
+
+ return 1;
+}
+EXPORT_SYMBOL(cfpkt_raw_extract);
+
+inline bool cfpkt_erroneous(struct cfpkt *pkt)
+{
+ return cfpkt_priv(pkt)->erronous;
+}
+EXPORT_SYMBOL(cfpkt_erroneous);
+
+struct cfpkt *cfpkt_create_pkt(enum caif_direction dir,
+ const unsigned char *data, unsigned int len)
+{
+ struct cfpkt *pkt;
+ if (dir == CAIF_DIR_OUT)
+ pkt = cfpkt_create_pfx(len + PKT_POSTFIX, PKT_PREFIX);
+ else
+ pkt = cfpkt_create_pfx(len, 0);
+ if (unlikely(!pkt))
+ return NULL;
+ if (unlikely(data))
+ cfpkt_add_body(pkt, data, len);
+ cfpkt_priv(pkt)->erronous = false;
+ return pkt;
+}
+EXPORT_SYMBOL(cfpkt_create_pkt);
+
+struct cfpktq *cfpktq_create()
+{
+ struct cfpktq *q = kmalloc(sizeof(struct cfpktq), GFP_ATOMIC);
+ if (!q)
+ return NULL;
+ skb_queue_head_init(&q->head);
+ atomic_set(&q->count, 0);
+ spin_lock_init(&q->lock);
+ return q;
+}
+EXPORT_SYMBOL(cfpktq_create);
+
+void cfpkt_queue(struct cfpktq *pktq, struct cfpkt *pkt, unsigned short prio)
+{
+ atomic_inc(&pktq->count);
+ spin_lock(&pktq->lock);
+ skb_queue_tail(&pktq->head, pkt_to_skb(pkt));
+ spin_unlock(&pktq->lock);
+
+}
+EXPORT_SYMBOL(cfpkt_queue);
+
+struct cfpkt *cfpkt_qpeek(struct cfpktq *pktq)
+{
+ struct cfpkt *tmp;
+ spin_lock(&pktq->lock);
+ tmp = skb_to_pkt(skb_peek(&pktq->head));
+ spin_unlock(&pktq->lock);
+ return tmp;
+}
+EXPORT_SYMBOL(cfpkt_qpeek);
+
+struct cfpkt *cfpkt_dequeue(struct cfpktq *pktq)
+{
+ struct cfpkt *pkt;
+ spin_lock(&pktq->lock);
+ pkt = skb_to_pkt(skb_dequeue(&pktq->head));
+ if (pkt) {
+ atomic_dec(&pktq->count);
+ caif_assert(atomic_read(&pktq->count) >= 0);
+ }
+ spin_unlock(&pktq->lock);
+ return pkt;
+}
+EXPORT_SYMBOL(cfpkt_dequeue);
+
+int cfpkt_qcount(struct cfpktq *pktq)
+{
+ return atomic_read(&pktq->count);
+}
+EXPORT_SYMBOL(cfpkt_qcount);
+
+struct cfpkt *cfpkt_clone_release(struct cfpkt *pkt)
+{
+ struct cfpkt *clone;
+ clone = skb_to_pkt(skb_clone(pkt_to_skb(pkt), GFP_ATOMIC));
+ /* Free original packet. */
+ cfpkt_destroy(pkt);
+ if (!clone)
+ return NULL;
+ atomic_inc(&cfpkt_packet_count);
+ return clone;
+}
+EXPORT_SYMBOL(cfpkt_clone_release);
+
+struct payload_info *cfpkt_info(struct cfpkt *pkt)
+{
+ return (struct payload_info *)&pkt_to_skb(pkt)->cb;
+}
+EXPORT_SYMBOL(cfpkt_info);
--
1.6.3.3
^ permalink raw reply related
* [PATCH net-next-2.6 v3 05/12] net-caif: add CAIF core protocol stack
From: sjur.brandeland @ 2010-02-22 22:05 UTC (permalink / raw)
To: netdev, davem, marcel
Cc: daniel.martensson, kaber, stefano.babic, randy.dunlap,
Sjur Braendeland
In-Reply-To: <1266876331-23988-5-git-send-email-sjur.brandeland@stericsson.com>
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
Changes from PATCHv2:
- Minor change in loopback allocation of channel-ids.
CAIF generic protocol implementation. This layer is
somewhat generic in order to be able to use and test it outside
the Linux Kernel.
cfctrl.c - CAIF control protocol layer
cfdbgl.c - CAIF debug protocol layer
cfdgml.c - CAIF datagram protocol layer
cffrml.c - CAIF framing protocol layer
cfmuxl.c - CAIF mux protocol layer
cfrfml.c - CAIF remote file manager protocol layer
cfserl.c - CAIF serial (fragmentation) protocol layer
cfsrvl.c - CAIF generic service layer functions
cfutill.c - CAIF utility protocol layer
cfveil.c - CAIF AT protocol layer
cfvidl.c - CAIF video protocol layer
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
net/caif/cfctrl.c | 710 ++++++++++++++++++++++++++++++++++++++++++++++++++++
net/caif/cfdbgl.c | 40 +++
net/caif/cfdgml.c | 108 ++++++++
net/caif/cffrml.c | 151 +++++++++++
net/caif/cfmuxl.c | 246 ++++++++++++++++++
net/caif/cfrfml.c | 106 ++++++++
net/caif/cfserl.c | 199 +++++++++++++++
net/caif/cfsrvl.c | 185 ++++++++++++++
net/caif/cfutill.c | 115 +++++++++
net/caif/cfveil.c | 107 ++++++++
net/caif/cfvidl.c | 65 +++++
11 files changed, 2032 insertions(+), 0 deletions(-)
diff --git a/net/caif/cfctrl.c b/net/caif/cfctrl.c
new file mode 100644
index 0000000..c8c3f98
--- /dev/null
+++ b/net/caif/cfctrl.c
@@ -0,0 +1,710 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/stddef.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <net/caif/caif_layer.h>
+#include <net/caif/cfpkt.h>
+#include <net/caif/cfctrl.h>
+
+#define container_obj(layr) container_of(layr, struct cfctrl, serv.layer)
+#define UTILITY_NAME_LENGTH 16
+#define CFPKT_CTRL_PKT_LEN 20
+
+
+#ifdef CAIF_NO_LOOP
+static inline int handle_loop(struct cfctrl *ctrl,
+ int cmd, struct cfpkt *pkt){
+ return CAIF_FAILURE;
+}
+#else
+static int handle_loop(struct cfctrl *ctrl,
+ int cmd, struct cfpkt *pkt);
+#endif
+static int cfctrl_recv(struct layer *layr, struct cfpkt *pkt);
+static void cfctrl_ctrlcmd(struct layer *layr, enum caif_ctrlcmd ctrl,
+ int phyid);
+
+
+struct layer *cfctrl_create()
+{
+ struct cfctrl *this =
+ kmalloc(sizeof(struct cfctrl), GFP_ATOMIC);
+ if (!this) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return NULL;
+ }
+ caif_assert(offsetof(struct cfctrl, serv.layer) == 0);
+ memset(this, 0, sizeof(*this));
+ spin_lock_init(&this->info_list_lock);
+ atomic_set(&this->req_seq_no, 1);
+ atomic_set(&this->rsp_seq_no, 1);
+ this->serv.dev_info.id = 0xff;
+ this->serv.layer.id = 0;
+ this->serv.layer.receive = cfctrl_recv;
+ sprintf(this->serv.layer.name, "ctrl");
+ this->serv.layer.ctrlcmd = cfctrl_ctrlcmd;
+ spin_lock_init(&this->loop_linkid_lock);
+ this->loop_linkid = 1;
+ return &this->serv.layer;
+}
+
+bool param_eq(struct cfctrl_link_param *p1, struct cfctrl_link_param *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;
+ spin_lock(&ctrl->info_list_lock);
+ req->next = NULL;
+ atomic_inc(&ctrl->req_seq_no);
+ req->sequence_no = atomic_read(&ctrl->req_seq_no);
+ if (ctrl->first_req == NULL) {
+ ctrl->first_req = req;
+ spin_unlock(&ctrl->info_list_lock);
+ return;
+ }
+ p = ctrl->first_req;
+ while (p->next != NULL)
+ p = p->next;
+ p->next = req;
+ spin_unlock(&ctrl->info_list_lock);
+}
+
+static void cfctrl_insert_req2(struct cfctrl *ctrl, enum cfctrl_cmd cmd,
+ u8 linkid, struct layer *user_layer)
+{
+ struct cfctrl_request_info *req = kmalloc(sizeof(*req), GFP_KERNEL);
+ if (!req) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return;
+ }
+ 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;
+
+ spin_lock(&ctrl->info_list_lock);
+ if (ctrl->first_req == NULL) {
+ spin_unlock(&ctrl->info_list_lock);
+ return NULL;
+ }
+
+ if (cfctrl_req_eq(req, ctrl->first_req)) {
+ ret = ctrl->first_req;
+ atomic_set(&ctrl->rsp_seq_no,
+ ctrl->first_req->sequence_no);
+ ctrl->first_req = ctrl->first_req->next;
+ spin_unlock(&ctrl->info_list_lock);
+ return ret;
+ }
+
+ pr_warning("CAIF: %s(): Requests are not received in order/matching\n",
+ __func__);
+
+ p = ctrl->first_req;
+
+ while (p->next != NULL) {
+ if (cfctrl_req_eq(req, p->next)) {
+ ret = p->next;
+ atomic_set(&ctrl->rsp_seq_no,
+ p->next->sequence_no);
+ p = p->next;
+ spin_unlock(&ctrl->info_list_lock);
+ return ret;
+ }
+ p = p->next;
+ }
+ spin_unlock(&ctrl->info_list_lock);
+ 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;
+
+ spin_lock(&ctrl->info_list_lock);
+ if (ctrl->first_req == NULL) {
+ spin_unlock(&ctrl->info_list_lock);
+ return;
+ }
+
+ if (ctrl->first_req->sequence_no <
+ atomic_read(&ctrl->req_seq_no)) {
+ del = ctrl->first_req;
+ ctrl->first_req = ctrl->first_req->next;
+ kfree(del);
+ }
+ p = ctrl->first_req;
+ while (p->next != NULL) {
+ if (p->next->sequence_no <
+ atomic_read(&ctrl->rsp_seq_no)) {
+ del = p->next;
+ p = p->next;
+ atomic_set(&ctrl->rsp_seq_no,
+ ctrl->first_req->sequence_no);
+ kfree(del);
+ }
+ p = p->next;
+ }
+ spin_unlock(&ctrl->info_list_lock);
+}
+
+struct cfctrl_rsp *cfctrl_get_respfuncs(struct layer *layer)
+{
+ struct cfctrl *this = container_obj(layer);
+ return &this->res;
+}
+
+void cfctrl_set_dnlayer(struct layer *this, struct layer *dn)
+{
+ this->dn = dn;
+}
+
+void cfctrl_set_uplayer(struct layer *this, struct layer *up)
+{
+ this->up = up;
+}
+
+void init_info(struct payload_info *info, struct cfctrl *cfctrl)
+{
+ info->hdr_len = 0;
+ info->channel_id = cfctrl->serv.layer.id;
+ info->dev_info = &cfctrl->serv.dev_info;
+}
+
+void cfctrl_enum_req(struct layer *layer, u8 physlinkid)
+{
+ struct cfctrl *cfctrl = container_obj(layer);
+ int ret;
+ struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
+ if (!pkt) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return;
+ }
+ caif_assert(offsetof(struct cfctrl, serv.layer) == 0);
+ init_info(cfpkt_info(pkt), cfctrl);
+ cfpkt_info(pkt)->dev_info->id = physlinkid;
+ cfctrl->serv.dev_info.id = physlinkid;
+ cfpkt_addbdy(pkt, CFCTRL_CMD_ENUM);
+ cfpkt_addbdy(pkt, physlinkid);
+ ret =
+ cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
+ if (ret < 0) {
+ pr_err("CAIF: %s(): Could not transmit enum message\n",
+ __func__);
+ cfpkt_destroy(pkt);
+ }
+}
+
+void cfctrl_linkup_request(struct layer *layer, struct cfctrl_link_param *param,
+ struct layer *user_layer)
+{
+ struct cfctrl *cfctrl = container_obj(layer);
+ u32 tmp32;
+ u16 tmp16;
+ u8 tmp8;
+ struct cfctrl_request_info *req;
+ int ret;
+ char utility_name[16];
+ struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
+ if (!pkt) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return;
+ }
+ 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);
+
+ switch (param->linktype) {
+ case CFCTRL_SRV_VEI:
+ break;
+ case CFCTRL_SRV_VIDEO:
+ cfpkt_addbdy(pkt, (u8) param->u.video.connid);
+ break;
+ case CFCTRL_SRV_DBG:
+ break;
+ case CFCTRL_SRV_DATAGRAM:
+ tmp32 = 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 = 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 = cpu_to_le16(param->u.utility.fifosize_kb);
+ cfpkt_add_body(pkt, &tmp16, 2);
+ tmp16 = 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);
+ break;
+ default:
+ pr_warning("CAIF: %s():Request setup of bad link type = %d\n",
+ __func__, param->linktype);
+ }
+ req = kmalloc(sizeof(*req), GFP_KERNEL);
+ if (!req) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return;
+ }
+ memset(req, 0, sizeof(*req));
+ req->client_layer = user_layer;
+ req->cmd = CFCTRL_CMD_LINK_SETUP;
+ req->param = *param;
+ cfctrl_insert_req(cfctrl, req);
+ init_info(cfpkt_info(pkt), cfctrl);
+ cfpkt_info(pkt)->dev_info->id = param->phyid;
+ ret =
+ cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
+ if (ret < 0) {
+ pr_err("CAIF: %s(): Could not transmit linksetup request\n",
+ __func__);
+ cfpkt_destroy(pkt);
+ }
+}
+
+int cfctrl_linkdown_req(struct layer *layer, u8 channelid,
+ struct layer *client)
+{
+ int ret;
+ struct cfctrl *cfctrl = container_obj(layer);
+ struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
+ if (!pkt) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return -ENOMEM;
+ }
+ cfctrl_insert_req2(cfctrl, CFCTRL_CMD_LINK_DESTROY, channelid, client);
+ cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_DESTROY);
+ cfpkt_addbdy(pkt, channelid);
+ init_info(cfpkt_info(pkt), cfctrl);
+ ret =
+ cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
+ if (ret < 0) {
+ pr_err("CAIF: %s(): Could not transmit link-down request\n",
+ __func__);
+ cfpkt_destroy(pkt);
+ }
+ return ret;
+}
+
+void cfctrl_sleep_req(struct layer *layer)
+{
+ int ret;
+ struct cfctrl *cfctrl = container_obj(layer);
+ struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
+ if (!pkt) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return;
+ }
+ cfpkt_addbdy(pkt, CFCTRL_CMD_SLEEP);
+ init_info(cfpkt_info(pkt), cfctrl);
+ ret =
+ cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
+ if (ret < 0)
+ cfpkt_destroy(pkt);
+}
+
+void cfctrl_wake_req(struct layer *layer)
+{
+ int ret;
+ struct cfctrl *cfctrl = container_obj(layer);
+ struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
+ if (!pkt) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return;
+ }
+ cfpkt_addbdy(pkt, CFCTRL_CMD_WAKE);
+ init_info(cfpkt_info(pkt), cfctrl);
+ ret =
+ cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
+ if (ret < 0)
+ cfpkt_destroy(pkt);
+}
+
+void cfctrl_getstartreason_req(struct layer *layer)
+{
+ int ret;
+ struct cfctrl *cfctrl = container_obj(layer);
+ struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
+ if (!pkt) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return;
+ }
+ cfpkt_addbdy(pkt, CFCTRL_CMD_START_REASON);
+ init_info(cfpkt_info(pkt), cfctrl);
+ ret =
+ cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
+ if (ret < 0)
+ cfpkt_destroy(pkt);
+}
+
+void cfctrl_setmode_req(struct layer *layer, u8 mode)
+{
+ int ret;
+ struct cfctrl *cfctrl = container_obj(layer);
+ struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
+ if (!pkt) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return;
+ }
+ cfpkt_addbdy(pkt, CFCTRL_CMD_RADIO_SET);
+ cfpkt_addbdy(pkt, mode);
+ init_info(cfpkt_info(pkt), cfctrl);
+ ret =
+ cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
+ if (ret < 0)
+ cfpkt_destroy(pkt);
+}
+
+static int cfctrl_recv(struct layer *layer, struct cfpkt *pkt)
+{
+ u8 cmdrsp;
+ u8 cmd;
+ int ret = -1;
+ u16 tmp16;
+ u8 len;
+ u8 param[255];
+ u8 linkid;
+ struct cfctrl *cfctrl = container_obj(layer);
+ struct cfctrl_request_info rsp, *req;
+
+
+ cfpkt_extr_head(pkt, &cmdrsp, 1);
+ cmd = cmdrsp & CFCTRL_CMD_MASK;
+ if (cmd != CFCTRL_CMD_LINK_ERR
+ && CFCTRL_RSP_BIT != (CFCTRL_RSP_BIT & cmdrsp)) {
+ if (handle_loop(cfctrl, cmd, pkt) == CAIF_FAILURE) {
+ pr_info("CAIF: %s() CAIF Protocol error:"
+ "Response bit not set\n", __func__);
+ goto error;
+ }
+ }
+
+ switch (cmd) {
+ case CFCTRL_CMD_LINK_SETUP:
+ {
+ enum cfctrl_srv serv;
+ enum cfctrl_srv servtype;
+ u8 endpoint;
+ u8 physlinkid;
+ u8 prio;
+ u8 tmp;
+ u32 tmp32;
+ u8 *cp;
+ int i;
+ struct cfctrl_link_param 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 =
+ le32_to_cpu(tmp32);
+ /* Link ID */
+ cfpkt_extr_head(pkt, &linkid, 1);
+ break;
+ case CFCTRL_SRV_RFM:
+ /* Construct a frame, convert
+ * DatagramConnectionID
+ * to network format long and copy it out...
+ */
+ cfpkt_extr_head(pkt, &tmp32, 4);
+ linkparam.u.rfm.connid =
+ le32_to_cpu(tmp32);
+ cp = (u8 *) 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, convert
+ * DatagramConnectionID
+ * to network format long and copy it out...
+ */
+ /* Fifosize KB */
+ cfpkt_extr_head(pkt, &tmp16, 2);
+ linkparam.u.utility.fifosize_kb =
+ le16_to_cpu(tmp16);
+ /* Fifosize bufs */
+ cfpkt_extr_head(pkt, &tmp16, 2);
+ linkparam.u.utility.fifosize_bufs =
+ le16_to_cpu(tmp16);
+ /* name */
+ cp = (u8 *) linkparam.u.utility.name;
+ caif_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:
+ pr_warning("CAIF: %s(): Request setup "
+ "- invalid link type (%d)",
+ __func__, serv);
+ goto error;
+ }
+ if (cfpkt_erroneous(pkt)) {
+ pr_err("CAIF: %s(): Packet is erroneous!",
+ __func__);
+ goto error;
+ }
+ rsp.cmd = cmd;
+ rsp.param = linkparam;
+ req = cfctrl_remove_req(cfctrl, &rsp);
+
+ if (CFCTRL_ERR_BIT == (CFCTRL_ERR_BIT & cmdrsp)) {
+ pr_err("CAIF: %s(): Invalid O/E bit "
+ "on CAIF control channel", __func__);
+ 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)
+ kfree(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)
+ kfree(req);
+ break;
+ case CFCTRL_CMD_LINK_ERR:
+ pr_err("CAIF: %s(): Frame Error Indication received \n",
+ __func__);
+ 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:
+ pr_err("CAIF: %s(): Unrecognized Control Frame\n", __func__);
+ goto error;
+ break;
+ }
+ ret = 0;
+error:
+ cfpkt_destroy(pkt);
+ return ret;
+}
+
+static void cfctrl_ctrlcmd(struct layer *layr, enum caif_ctrlcmd ctrl,
+ int phyid)
+{
+ struct cfctrl *this = container_obj(layr);
+ switch (ctrl) {
+ case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND:
+ case CAIF_CTRLCMD_FLOW_OFF_IND:
+ spin_lock(&this->info_list_lock);
+ if (this->first_req != NULL) {
+ pr_warning("CAIF: %s(): Received flow off in "
+ "control layer", __func__);
+ }
+ spin_unlock(&this->info_list_lock);
+ break;
+ default:
+ break;
+ }
+}
+
+#ifndef CAIF_NO_LOOP
+int handle_loop(struct cfctrl *ctrl, int cmd, struct cfpkt *pkt)
+{
+ static int last_linkid;
+ u8 linkid, linktype, tmp;
+ switch (cmd) {
+ case CFCTRL_CMD_LINK_SETUP:
+ spin_lock(&ctrl->loop_linkid_lock);
+ for (linkid = last_linkid + 1; linkid < 255; linkid++)
+ if (!ctrl->loop_linkused[linkid])
+ goto found;
+ for (linkid = last_linkid - 1; linkid > 0; linkid--)
+ if (!ctrl->loop_linkused[linkid])
+ goto found;
+ return -EINVAL;
+found:
+ if (!ctrl->loop_linkused[linkid])
+ ctrl->loop_linkused[linkid] = 1;
+
+ last_linkid = linkid;
+
+ cfpkt_add_trail(pkt, &linkid, 1);
+ spin_unlock(&ctrl->loop_linkid_lock);
+ cfpkt_peek_head(pkt, &linktype, 1);
+ if (linktype == CFCTRL_SRV_UTIL) {
+ tmp = 0x01;
+ cfpkt_add_trail(pkt, &tmp, 1);
+ cfpkt_add_trail(pkt, &tmp, 1);
+ }
+ break;
+
+ case CFCTRL_CMD_LINK_DESTROY:
+ spin_lock(&ctrl->loop_linkid_lock);
+ cfpkt_peek_head(pkt, &linkid, 1);
+ ctrl->loop_linkused[linkid] = 0;
+ spin_unlock(&ctrl->loop_linkid_lock);
+ break;
+ default:
+ break;
+ }
+ return CAIF_SUCCESS;
+}
+#endif
diff --git a/net/caif/cfdbgl.c b/net/caif/cfdbgl.c
new file mode 100644
index 0000000..fbe316b
--- /dev/null
+++ b/net/caif/cfdbgl.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/stddef.h>
+#include <linux/slab.h>
+#include <net/caif/caif_layer.h>
+#include <net/caif/cfsrvl.h>
+#include <net/caif/cfpkt.h>
+
+static int cfdbgl_receive(struct layer *layr, struct cfpkt *pkt);
+static int cfdbgl_transmit(struct layer *layr, struct cfpkt *pkt);
+
+struct layer *cfdbgl_create(u8 channel_id, struct dev_info *dev_info)
+{
+ struct cfsrvl *dbg = kmalloc(sizeof(struct cfsrvl), GFP_ATOMIC);
+ if (!dbg) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return NULL;
+ }
+ caif_assert(offsetof(struct cfsrvl, layer) == 0);
+ memset(dbg, 0, sizeof(struct cfsrvl));
+ cfsrvl_init(dbg, channel_id, dev_info);
+ dbg->layer.receive = cfdbgl_receive;
+ dbg->layer.transmit = cfdbgl_transmit;
+ snprintf(dbg->layer.name, CAIF_LAYER_NAME_SZ - 1, "dbg%d", channel_id);
+ return &dbg->layer;
+}
+
+static int cfdbgl_receive(struct layer *layr, struct cfpkt *pkt)
+{
+ return layr->up->receive(layr->up, pkt);
+}
+
+static int cfdbgl_transmit(struct layer *layr, struct cfpkt *pkt)
+{
+ return layr->dn->transmit(layr->dn, pkt);
+}
diff --git a/net/caif/cfdgml.c b/net/caif/cfdgml.c
new file mode 100644
index 0000000..fecac32
--- /dev/null
+++ b/net/caif/cfdgml.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/stddef.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <net/caif/caif_layer.h>
+#include <net/caif/cfsrvl.h>
+#include <net/caif/cfpkt.h>
+
+#define container_obj(layr) ((struct cfsrvl *) 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(struct layer *layr, struct cfpkt *pkt);
+static int cfdgml_transmit(struct layer *layr, struct cfpkt *pkt);
+
+struct layer *cfdgml_create(u8 channel_id, struct dev_info *dev_info)
+{
+ struct cfsrvl *dgm = kmalloc(sizeof(struct cfsrvl), GFP_ATOMIC);
+ if (!dgm) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return NULL;
+ }
+ caif_assert(offsetof(struct cfsrvl, layer) == 0);
+ memset(dgm, 0, sizeof(struct cfsrvl));
+ cfsrvl_init(dgm, channel_id, dev_info);
+ dgm->layer.receive = cfdgml_receive;
+ dgm->layer.transmit = cfdgml_transmit;
+ snprintf(dgm->layer.name, CAIF_LAYER_NAME_SZ - 1, "dgm%d", channel_id);
+ dgm->layer.name[CAIF_LAYER_NAME_SZ - 1] = '\0';
+ return &dgm->layer;
+}
+
+static int cfdgml_receive(struct layer *layr, struct cfpkt *pkt)
+{
+ u8 cmd = -1;
+ u8 dgmhdr[3];
+ int ret;
+ caif_assert(layr->up != NULL);
+ caif_assert(layr->receive != NULL);
+ caif_assert(layr->ctrlcmd != NULL);
+
+ if (cfpkt_extr_head(pkt, &cmd, 1) < 0) {
+ pr_err("CAIF: %s(): Packet is erroneous!\n", __func__);
+ cfpkt_destroy(pkt);
+ return -EPROTO;
+ }
+
+ if ((cmd & DGM_CMD_BIT) == 0) {
+ if (cfpkt_extr_head(pkt, &dgmhdr, 3) < 0) {
+ pr_err("CAIF: %s(): Packet is erroneous!\n", __func__);
+ cfpkt_destroy(pkt);
+ return -EPROTO;
+ }
+ ret = layr->up->receive(layr->up, pkt);
+ return ret;
+ }
+
+ switch (cmd) {
+ case DGM_FLOW_OFF: /* FLOW OFF */
+ layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_OFF_IND, 0);
+ cfpkt_destroy(pkt);
+ return 0;
+ case DGM_FLOW_ON: /* FLOW ON */
+ layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_ON_IND, 0);
+ cfpkt_destroy(pkt);
+ return 0;
+ default:
+ cfpkt_destroy(pkt);
+ pr_info("CAIF: %s(): Unknown datagram control %d (0x%x)\n",
+ __func__, cmd, cmd);
+ return -EPROTO;
+ }
+}
+
+static int cfdgml_transmit(struct layer *layr, struct cfpkt *pkt)
+{
+ u32 zero = 0;
+ struct payload_info *info;
+ struct cfsrvl *service = container_obj(layr);
+ int ret;
+ if (!cfsrvl_ready(service, &ret))
+ return ret;
+
+ cfpkt_add_head(pkt, &zero, 4);
+
+ /* Add info for MUX-layer to route the packet out. */
+ info = cfpkt_info(pkt);
+ info->channel_id = service->layer.id;
+ /* To optimize alignment, we add up the size of CAIF header
+ * before payload.
+ */
+ info->hdr_len = 4;
+ info->dev_info = &service->dev_info;
+ ret = layr->dn->transmit(layr->dn, pkt);
+ if (ret < 0) {
+ u32 tmp32;
+ cfpkt_extr_head(pkt, &tmp32, 4);
+ }
+ return ret;
+}
diff --git a/net/caif/cffrml.c b/net/caif/cffrml.c
new file mode 100644
index 0000000..f3f5bfa
--- /dev/null
+++ b/net/caif/cffrml.c
@@ -0,0 +1,151 @@
+/*
+ * CAIF Framing Layer.
+ *
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/stddef.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/crc-ccitt.h>
+#include <net/caif/caif_layer.h>
+#include <net/caif/cfpkt.h>
+#include <net/caif/cffrml.h>
+
+#define container_obj(layr) container_of(layr, struct cffrml, layer)
+
+struct cffrml {
+ struct layer layer;
+ bool dofcs; /* !< FCS active */
+};
+
+static int cffrml_receive(struct layer *layr, struct cfpkt *pkt);
+static int cffrml_transmit(struct layer *layr, struct cfpkt *pkt);
+static void cffrml_ctrlcmd(struct layer *layr, enum caif_ctrlcmd ctrl,
+ int phyid);
+
+u32 cffrml_rcv_error;
+u32 cffrml_rcv_checsum_error;
+struct layer *cffrml_create(u16 phyid, bool use_fcs)
+{
+ struct cffrml *this = kmalloc(sizeof(struct cffrml), GFP_ATOMIC);
+ if (!this) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return NULL;
+ }
+ caif_assert(offsetof(struct cffrml, layer) == 0);
+
+ memset(this, 0, sizeof(struct layer));
+ this->layer.receive = cffrml_receive;
+ this->layer.transmit = cffrml_transmit;
+ this->layer.ctrlcmd = cffrml_ctrlcmd;
+ snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "frm%d", phyid);
+ this->dofcs = use_fcs;
+ this->layer.id = phyid;
+ return (struct layer *) this;
+}
+
+void cffrml_set_uplayer(struct layer *this, struct layer *up)
+{
+ this->up = up;
+}
+
+void cffrml_set_dnlayer(struct layer *this, struct layer *dn)
+{
+ this->dn = dn;
+}
+
+static u16 cffrml_checksum(u16 chks, void *buf, __u16 len)
+{
+ /* FIXME: FCS should be moved to glue in order to use OS-Specific
+ * solutions
+ */
+ return crc_ccitt(chks, buf, len);
+}
+
+static int cffrml_receive(struct layer *layr, struct cfpkt *pkt)
+{
+ u16 tmp;
+ u16 len;
+ u16 hdrchks;
+ u16 pktchks;
+ struct cffrml *this;
+ this = container_obj(layr);
+
+ cfpkt_extr_head(pkt, &tmp, 2);
+ len = 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;
+ pr_err("CAIF: %s():Framing length error (%d)\n", __func__, len);
+ cfpkt_destroy(pkt);
+ return -EPROTO;
+ }
+ /*
+ * Don't do extract if FCS is false, rather do setlen - then we don't
+ * get a cache-miss.
+ */
+ if (this->dofcs) {
+ cfpkt_extr_trail(pkt, &tmp, 2);
+ hdrchks = 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;
+ pr_info("CAIF: %s(): Frame checksum error "
+ "(0x%x != 0x%x)\n", __func__, hdrchks, pktchks);
+ return -EILSEQ;
+ }
+ }
+ if (cfpkt_erroneous(pkt)) {
+ ++cffrml_rcv_error;
+ pr_err("CAIF: %s(): Packet is erroneous!\n", __func__);
+ cfpkt_destroy(pkt);
+ return -EPROTO;
+ }
+ return layr->up->receive(layr->up, pkt);
+}
+
+static int cffrml_transmit(struct layer *layr, struct cfpkt *pkt)
+{
+ int tmp;
+ u16 chks;
+ u16 len;
+ int ret;
+ struct cffrml *this = container_obj(layr);
+ if (this->dofcs) {
+ chks = cfpkt_iterate(pkt, cffrml_checksum, 0xffff);
+ tmp = cpu_to_le16(chks);
+ cfpkt_add_trail(pkt, &tmp, 2);
+ } else {
+ cfpkt_pad_trail(pkt, 2);
+ }
+ len = cfpkt_getlen(pkt);
+ tmp = cpu_to_le16(len);
+ cfpkt_add_head(pkt, &tmp, 2);
+ cfpkt_info(pkt)->hdr_len += 2;
+ if (cfpkt_erroneous(pkt)) {
+ pr_err("CAIF: %s(): Packet is erroneous!\n", __func__);
+ return -EPROTO;
+ }
+ ret = layr->dn->transmit(layr->dn, pkt);
+ if (ret < 0) {
+ /* Remove header on faulty packet. */
+ cfpkt_extr_head(pkt, &tmp, 2);
+ }
+ return ret;
+}
+
+static void cffrml_ctrlcmd(struct layer *layr, enum caif_ctrlcmd ctrl,
+ int phyid)
+{
+ if (layr->up->ctrlcmd)
+ layr->up->ctrlcmd(layr->up, ctrl, layr->id);
+}
diff --git a/net/caif/cfmuxl.c b/net/caif/cfmuxl.c
new file mode 100644
index 0000000..1ad7d0c
--- /dev/null
+++ b/net/caif/cfmuxl.c
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+#include <linux/stddef.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <net/caif/cfpkt.h>
+#include <net/caif/cfmuxl.h>
+#include <net/caif/cfsrvl.h>
+#include <net/caif/cffrml.h>
+
+#define container_obj(layr) container_of(layr, struct cfmuxl, layer)
+
+#define CAIF_CTRL_CHANNEL 0
+#define UP_CACHE_SIZE 8
+#define DN_CACHE_SIZE 8
+
+struct cfmuxl {
+ struct layer layer;
+ struct list_head srvl_list;
+ struct list_head frml_list;
+ struct layer *up_cache[UP_CACHE_SIZE];
+ struct layer *dn_cache[DN_CACHE_SIZE];
+ /*
+ * Set when inserting or removing downwards layers.
+ */
+ spinlock_t transmit_lock;
+
+ /*
+ * Set when inserting or removing upwards layers.
+ */
+ spinlock_t receive_lock;
+
+};
+
+static int cfmuxl_receive(struct layer *layr, struct cfpkt *pkt);
+static int cfmuxl_transmit(struct layer *layr, struct cfpkt *pkt);
+static void cfmuxl_ctrlcmd(struct layer *layr, enum caif_ctrlcmd ctrl,
+ int phyid);
+static struct layer *get_up(struct cfmuxl *muxl, int id);
+
+struct layer *cfmuxl_create()
+{
+ struct cfmuxl *this = kmalloc(sizeof(struct cfmuxl), GFP_ATOMIC);
+ if (!this)
+ return NULL;
+ memset(this, 0, sizeof(*this));
+ this->layer.receive = cfmuxl_receive;
+ this->layer.transmit = cfmuxl_transmit;
+ this->layer.ctrlcmd = cfmuxl_ctrlcmd;
+ INIT_LIST_HEAD(&this->srvl_list);
+ INIT_LIST_HEAD(&this->frml_list);
+ spin_lock_init(&this->transmit_lock);
+ spin_lock_init(&this->receive_lock);
+ snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "mux");
+ return &this->layer;
+}
+
+int cfmuxl_set_uplayer(struct layer *layr, struct layer *up, u8 linkid)
+{
+ struct cfmuxl *muxl = container_obj(layr);
+ spin_lock(&muxl->receive_lock);
+ list_add(&up->node, &muxl->srvl_list);
+ spin_unlock(&muxl->receive_lock);
+ return 0;
+}
+
+bool cfmuxl_is_phy_inuse(struct layer *layr, u8 phyid)
+{
+ struct list_head *node;
+ struct layer *layer;
+ struct cfmuxl *muxl = container_obj(layr);
+ bool match = false;
+ spin_lock(&muxl->receive_lock);
+
+ list_for_each(node, &muxl->srvl_list) {
+ layer = list_entry(node, struct layer, node);
+ if (cfsrvl_phyid_match(layer, phyid)) {
+ match = true;
+ break;
+ }
+
+ }
+ spin_unlock(&muxl->receive_lock);
+ return match;
+}
+
+u8 cfmuxl_get_phyid(struct layer *layr, u8 channel_id)
+{
+ struct layer *up;
+ int phyid;
+ struct cfmuxl *muxl = container_obj(layr);
+ spin_lock(&muxl->receive_lock);
+ up = get_up(muxl, channel_id);
+ if (up != NULL)
+ phyid = cfsrvl_getphyid(up);
+ else
+ phyid = 0;
+ spin_unlock(&muxl->receive_lock);
+ return phyid;
+}
+
+int cfmuxl_set_dnlayer(struct layer *layr, struct layer *dn, u8 phyid)
+{
+ struct cfmuxl *muxl = (struct cfmuxl *) layr;
+ spin_lock(&muxl->transmit_lock);
+ list_add(&dn->node, &muxl->frml_list);
+ spin_unlock(&muxl->transmit_lock);
+ return 0;
+}
+struct layer *get_from_id(struct list_head *list, int id)
+{
+ struct list_head *node;
+ struct layer *layer;
+ list_for_each(node, list) {
+ layer = list_entry(node, struct layer, node);
+ if (layer->id == id)
+ return layer;
+ }
+ return NULL;
+}
+
+struct layer *cfmuxl_remove_dnlayer(struct layer *layr, u8 phyid)
+{
+ struct cfmuxl *muxl = container_obj(layr);
+ struct layer *dn;
+ spin_lock(&muxl->transmit_lock);
+ memset(muxl->dn_cache, 0, sizeof(muxl->dn_cache));
+ dn = get_from_id(&muxl->frml_list, phyid);
+ if (dn == NULL)
+ return NULL;
+ list_del(&dn->node);
+ caif_assert(dn != NULL);
+ spin_unlock(&muxl->transmit_lock);
+ return dn;
+}
+
+
+/* Invariant: lock is taken */
+static struct layer *get_up(struct cfmuxl *muxl, int id)
+{
+ struct layer *up;
+ int idx = id % UP_CACHE_SIZE;
+ up = muxl->up_cache[idx];
+ if (up == NULL || up->id != id) {
+ up = get_from_id(&muxl->srvl_list, id);
+ muxl->up_cache[idx] = up;
+ }
+ return up;
+}
+
+/* Invariant: lock is taken */
+static struct layer *get_dn(struct cfmuxl *muxl, struct dev_info *dev_info)
+{
+ struct layer *dn;
+ int idx = dev_info->id % DN_CACHE_SIZE;
+ dn = muxl->dn_cache[idx];
+ if (dn == NULL || dn->id != dev_info->id) {
+ dn = get_from_id(&muxl->frml_list, dev_info->id);
+ muxl->dn_cache[idx] = dn;
+ }
+ return dn;
+}
+
+struct layer *cfmuxl_remove_uplayer(struct layer *layr, u8 id)
+{
+ struct layer *up;
+ struct cfmuxl *muxl = container_obj(layr);
+ spin_lock(&muxl->receive_lock);
+ up = get_up(muxl, id);
+ memset(muxl->up_cache, 0, sizeof(muxl->up_cache));
+ list_del(&up->node);
+ spin_unlock(&muxl->receive_lock);
+ return up;
+}
+
+static int cfmuxl_receive(struct layer *layr, struct cfpkt *pkt)
+{
+ int ret;
+ struct cfmuxl *muxl = container_obj(layr);
+ u8 id;
+ struct layer *up;
+ if (cfpkt_extr_head(pkt, &id, 1) < 0) {
+ pr_err("CAIF: %s(): erroneous Caif Packet\n", __func__);
+ cfpkt_destroy(pkt);
+ return -EPROTO;
+ }
+
+ spin_lock(&muxl->receive_lock);
+ up = get_up(muxl, id);
+ spin_unlock(&muxl->receive_lock);
+ if (up == NULL) {
+ pr_info("CAIF: %s():Received data on unknown link ID = %d "
+ "(0x%x) up == NULL", __func__, id, id);
+ cfpkt_destroy(pkt);
+ /*
+ * Don't return ERROR, since modem misbehaves and sends out
+ * flow on before linksetup response.
+ */
+ return /* CFGLU_EPROT; */ 0;
+ }
+
+ ret = up->receive(up, pkt);
+
+
+ return ret;
+}
+
+static int cfmuxl_transmit(struct layer *layr, struct cfpkt *pkt)
+{
+ int ret;
+ struct cfmuxl *muxl = container_obj(layr);
+ u8 linkid;
+ struct layer *dn;
+ struct payload_info *info = cfpkt_info(pkt);
+ dn = get_dn(muxl, cfpkt_info(pkt)->dev_info);
+ if (dn == NULL) {
+ pr_warning("CAIF: %s(): Send data on unknown phy "
+ "ID = %d (0x%x)\n",
+ __func__, info->dev_info->id, info->dev_info->id);
+ return -ENOTCONN;
+ }
+ info->hdr_len += 1;
+ linkid = info->channel_id;
+ cfpkt_add_head(pkt, &linkid, 1);
+ ret = dn->transmit(dn, pkt);
+ /* Remove MUX protocol header upon error. */
+ if (ret < 0)
+ cfpkt_extr_head(pkt, &linkid, 1);
+ return ret;
+}
+
+static void cfmuxl_ctrlcmd(struct layer *layr, enum caif_ctrlcmd ctrl,
+ int phyid)
+{
+ struct cfmuxl *muxl = container_obj(layr);
+ struct list_head *node;
+ struct layer *layer;
+ list_for_each(node, &muxl->srvl_list) {
+ layer = list_entry(node, struct layer, node);
+ if (cfsrvl_phyid_match(layer, phyid))
+ layer->ctrlcmd(layer, ctrl, phyid);
+ }
+}
diff --git a/net/caif/cfrfml.c b/net/caif/cfrfml.c
new file mode 100644
index 0000000..faa25f2
--- /dev/null
+++ b/net/caif/cfrfml.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/stddef.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <net/caif/caif_layer.h>
+#include <net/caif/cfsrvl.h>
+#include <net/caif/cfpkt.h>
+
+#define container_obj(layr) container_of(layr, struct cfsrvl, 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(struct layer *layr, struct cfpkt *pkt);
+static int cfrfml_transmit(struct layer *layr, struct cfpkt *pkt);
+
+struct layer *cfrfml_create(u8 channel_id, struct dev_info *dev_info)
+{
+ struct cfsrvl *rfm = kmalloc(sizeof(struct cfsrvl), GFP_ATOMIC);
+ if (!rfm) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return NULL;
+ }
+ caif_assert(offsetof(struct cfsrvl, layer) == 0);
+ memset(rfm, 0, sizeof(struct cfsrvl));
+ cfsrvl_init(rfm, channel_id, dev_info);
+ rfm->layer.receive = cfrfml_receive;
+ rfm->layer.transmit = cfrfml_transmit;
+ snprintf(rfm->layer.name, CAIF_LAYER_NAME_SZ, "rfm%d", channel_id);
+ return &rfm->layer;
+}
+
+void cffrml_destroy(struct layer *layer)
+{
+ kfree(layer);
+}
+
+static int cfrfml_receive(struct layer *layr, struct cfpkt *pkt)
+{
+ u8 tmp;
+ bool segmented;
+ int ret;
+ caif_assert(layr->up != NULL);
+ caif_assert(layr->receive != NULL);
+
+ /*
+ * RFM is taking care of segmentation and stripping of
+ * segmentation bit.
+ */
+ if (cfpkt_extr_head(pkt, &tmp, 1) < 0) {
+ pr_err("CAIF: %s(): Packet is erroneous!\n", __func__);
+ cfpkt_destroy(pkt);
+ return -EPROTO;
+ }
+ segmented = tmp & RFM_SEGMENTATION_BIT;
+ caif_assert(!segmented);
+
+ ret = layr->up->receive(layr->up, pkt);
+ return ret;
+}
+
+static int cfrfml_transmit(struct layer *layr, struct cfpkt *pkt)
+{
+ u8 tmp = 0;
+ int ret;
+ struct cfsrvl *service = container_obj(layr);
+
+ caif_assert(layr->dn != NULL);
+ caif_assert(layr->dn->transmit != NULL);
+
+ if (!cfsrvl_ready(service, &ret))
+ return ret;
+
+ if (!cfpkt_getlen(pkt) > CAIF_MAX_PAYLOAD_SIZE) {
+ pr_err("CAIF: %s():Packet too large - size=%d\n",
+ __func__, cfpkt_getlen(pkt));
+ return -EOVERFLOW;
+ }
+ if (cfpkt_add_head(pkt, &tmp, 1) < 0) {
+ pr_err("CAIF: %s(): Packet is erroneous!\n", __func__);
+ return -EPROTO;
+ }
+
+ /* Add info for MUX-layer to route the packet out. */
+ cfpkt_info(pkt)->channel_id = service->layer.id;
+ /*
+ * To optimize alignment, we add up the size of CAIF header before
+ * payload.
+ */
+ cfpkt_info(pkt)->hdr_len = 1;
+ cfpkt_info(pkt)->dev_info = &service->dev_info;
+ ret = layr->dn->transmit(layr->dn, pkt);
+ if (ret < 0)
+ cfpkt_extr_head(pkt, &tmp, 1);
+ return ret;
+}
diff --git a/net/caif/cfserl.c b/net/caif/cfserl.c
new file mode 100644
index 0000000..202bb89
--- /dev/null
+++ b/net/caif/cfserl.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/stddef.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <net/caif/caif_layer.h>
+#include <net/caif/cfpkt.h>
+#define container_obj(layr) ((struct cfserl *) layr)
+
+#define CFSERL_STX 0x02
+#define CAIF_MINIUM_PACKET_SIZE 4
+struct cfserl {
+ struct layer layer;
+ struct cfpkt *incomplete_frm;
+ spinlock_t sync;
+ bool usestx;
+};
+#define STXLEN(layr) (layr->usestx ? 1 : 0)
+
+static int cfserl_receive(struct layer *layr, struct cfpkt *pkt);
+static int cfserl_transmit(struct layer *layr, struct cfpkt *pkt);
+static void cfserl_ctrlcmd(struct layer *layr, enum caif_ctrlcmd ctrl,
+ int phyid);
+
+struct cfserl *cfserl_create(int type, int instance, bool use_stx)
+{
+ struct cfserl *this = kmalloc(sizeof(struct cfserl), GFP_ATOMIC);
+ if (!this) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return NULL;
+ }
+ caif_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;
+ spin_lock_init(&this->sync);
+ snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "ser1");
+ return this;
+}
+
+void cfserl_set_uplayer(struct cfserl *this, struct layer *up)
+{
+ this->layer.up = up;
+}
+
+void cfserl_set_dnlayer(struct cfserl *this, struct layer *dn)
+{
+ this->layer.dn = dn;
+}
+
+static int cfserl_receive(struct layer *l, struct cfpkt *newpkt)
+{
+ struct cfserl *layr = container_obj(l);
+ u16 pkt_len;
+ struct cfpkt *pkt = NULL;
+ struct cfpkt *tail_pkt = NULL;
+ u8 tmp8;
+ u16 tmp;
+ u8 stx = CFSERL_STX;
+ int ret;
+ u16 expectlen = 0;
+ caif_assert(newpkt != NULL);
+ spin_lock(&layr->sync);
+
+ if (layr->incomplete_frm != NULL) {
+
+ layr->incomplete_frm =
+ cfpkt_append(layr->incomplete_frm, newpkt, expectlen);
+ pkt = layr->incomplete_frm;
+ } else {
+ pkt = newpkt;
+ }
+ layr->incomplete_frm = NULL;
+
+ do {
+ /* Search for STX at start of pkt if STX is used */
+ if (layr->usestx) {
+ cfpkt_extr_head(pkt, &tmp8, 1);
+ if (tmp8 != CFSERL_STX) {
+ while (cfpkt_more(pkt)
+ && tmp8 != CFSERL_STX) {
+ cfpkt_extr_head(pkt, &tmp8, 1);
+ }
+ if (!cfpkt_more(pkt)) {
+ cfpkt_destroy(pkt);
+ layr->incomplete_frm = NULL;
+ spin_unlock(&layr->sync);
+ return -EPROTO;
+ }
+ }
+ }
+
+ pkt_len = cfpkt_getlen(pkt);
+
+ /*
+ * pkt_len is the accumulated length of the packet data
+ * we have received so far.
+ * Exit if frame doesn't hold length.
+ */
+
+ if (pkt_len < 2) {
+ if (layr->usestx)
+ cfpkt_add_head(pkt, &stx, 1);
+ layr->incomplete_frm = pkt;
+ spin_unlock(&layr->sync);
+ return 0;
+ }
+
+ /*
+ * Find length of frame.
+ * expectlen is the length we need for a full frame.
+ */
+ cfpkt_peek_head(pkt, &tmp, 2);
+ expectlen = le16_to_cpu(tmp) + 2;
+ /*
+ * Frame error handling
+ */
+ if (expectlen < CAIF_MINIUM_PACKET_SIZE
+ || expectlen > CAIF_MAX_FRAMESIZE) {
+ if (!layr->usestx) {
+ if (pkt != NULL)
+ cfpkt_destroy(pkt);
+ layr->incomplete_frm = NULL;
+ expectlen = 0;
+ spin_unlock(&layr->sync);
+ return -EPROTO;
+ }
+ continue;
+ }
+
+ if (pkt_len < expectlen) {
+ /* Too little received data */
+ if (layr->usestx)
+ cfpkt_add_head(pkt, &stx, 1);
+ layr->incomplete_frm = pkt;
+ spin_unlock(&layr->sync);
+ return 0;
+ }
+
+ /*
+ * Enough data for at least one frame.
+ * Split the frame, if too long
+ */
+ if (pkt_len > expectlen)
+ tail_pkt = cfpkt_split(pkt, expectlen);
+ else
+ tail_pkt = NULL;
+
+ /* Send the first part of packet upwards.*/
+ spin_unlock(&layr->sync);
+ ret = layr->layer.up->receive(layr->layer.up, pkt);
+ spin_lock(&layr->sync);
+ if (ret == -EILSEQ) {
+ if (layr->usestx) {
+ if (tail_pkt != NULL)
+ pkt = cfpkt_append(pkt, tail_pkt, 0);
+
+ /* Start search for next STX if frame failed */
+ continue;
+ } else {
+ cfpkt_destroy(pkt);
+ pkt = NULL;
+ }
+ }
+
+ pkt = tail_pkt;
+
+ } while (pkt != NULL);
+
+ spin_unlock(&layr->sync);
+ return 0;
+}
+
+static int cfserl_transmit(struct layer *layer, struct cfpkt *newpkt)
+{
+ struct cfserl *layr = container_obj(layer);
+ int ret;
+ u8 tmp8 = CFSERL_STX;
+ if (layr->usestx)
+ cfpkt_add_head(newpkt, &tmp8, 1);
+ ret = layer->dn->transmit(layer->dn, newpkt);
+ if (ret < 0)
+ cfpkt_extr_head(newpkt, &tmp8, 1);
+
+ return ret;
+}
+
+static void cfserl_ctrlcmd(struct layer *layr, enum caif_ctrlcmd ctrl,
+ int phyid)
+{
+ layr->up->ctrlcmd(layr->up, ctrl, phyid);
+}
diff --git a/net/caif/cfsrvl.c b/net/caif/cfsrvl.c
new file mode 100644
index 0000000..8306966
--- /dev/null
+++ b/net/caif/cfsrvl.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <net/caif/caif_layer.h>
+#include <net/caif/cfsrvl.h>
+#include <net/caif/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) container_of(layr, struct cfsrvl, layer)
+
+static void cfservl_ctrlcmd(struct layer *layr, enum caif_ctrlcmd ctrl,
+ int phyid)
+{
+ struct cfsrvl *service = container_obj(layr);
+ caif_assert(layr->up != NULL);
+ caif_assert(layr->up->ctrlcmd != NULL);
+ switch (ctrl) {
+ case CAIF_CTRLCMD_INIT_RSP:
+ service->open = true;
+ layr->up->ctrlcmd(layr->up, ctrl, phyid);
+ break;
+ case CAIF_CTRLCMD_DEINIT_RSP:
+ case CAIF_CTRLCMD_INIT_FAIL_RSP:
+ service->open = false;
+ layr->up->ctrlcmd(layr->up, ctrl, phyid);
+ break;
+ case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND:
+ if (phyid != service->dev_info.id)
+ 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->dev_info.id)
+ 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_PHYIF_DOWN_IND:
+ /* In case interface is down, let's fake a remove shutdown */
+ layr->up->ctrlcmd(layr->up,
+ CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND, phyid);
+ break;
+ case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND:
+ layr->up->ctrlcmd(layr->up, ctrl, phyid);
+ break;
+ default:
+ pr_warning("CAIF: %s(): "
+ "Unexpected ctrl in cfsrvl (%d)\n", __func__, 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;
+ }
+}
+
+static int cfservl_modemcmd(struct layer *layr, enum caif_modemcmd ctrl)
+{
+ struct cfsrvl *service = container_obj(layr);
+ caif_assert(layr != NULL);
+ caif_assert(layr->dn != NULL);
+ caif_assert(layr->dn->transmit != NULL);
+ switch (ctrl) {
+ case CAIF_MODEMCMD_FLOW_ON_REQ:
+ {
+ struct cfpkt *pkt;
+ struct payload_info *info;
+ u8 flow_on = SRVL_FLOW_ON;
+ pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE);
+ if (!pkt) {
+ pr_warning("CAIF: %s(): Out of memory\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ if (cfpkt_add_head(pkt, &flow_on, 1) < 0) {
+ pr_err("CAIF: %s(): Packet is erroneous!\n",
+ __func__);
+ cfpkt_destroy(pkt);
+ return -EPROTO;
+ }
+ info = cfpkt_info(pkt);
+ info->channel_id = service->layer.id;
+ info->hdr_len = 1;
+ info->dev_info = &service->dev_info;
+ return layr->dn->transmit(layr->dn, pkt);
+ }
+ case CAIF_MODEMCMD_FLOW_OFF_REQ:
+ {
+ struct cfpkt *pkt;
+ struct payload_info *info;
+ u8 flow_off = SRVL_FLOW_OFF;
+ pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE);
+ if (cfpkt_add_head(pkt, &flow_off, 1) < 0) {
+ pr_err("CAIF: %s(): Packet is erroneous!\n",
+ __func__);
+ cfpkt_destroy(pkt);
+ return -EPROTO;
+ }
+ info = cfpkt_info(pkt);
+ info->channel_id = service->layer.id;
+ info->hdr_len = 1;
+ info->dev_info = &service->dev_info;
+ return layr->dn->transmit(layr->dn, pkt);
+ }
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+void cfservl_destroy(struct layer *layer)
+{
+ kfree(layer);
+}
+
+void cfsrvl_init(struct cfsrvl *service,
+ u8 channel_id,
+ struct dev_info *dev_info)
+{
+ caif_assert(offsetof(struct cfsrvl, layer) == 0);
+ service->open = false;
+ service->modem_flow_on = true;
+ service->phy_flow_on = true;
+ service->layer.id = channel_id;
+ service->layer.ctrlcmd = cfservl_ctrlcmd;
+ service->layer.modemcmd = cfservl_modemcmd;
+ service->dev_info = *dev_info;
+}
+
+bool cfsrvl_ready(struct cfsrvl *service, int *err)
+{
+ if (service->open && service->modem_flow_on && service->phy_flow_on)
+ return true;
+ if (!service->open) {
+ *err = -ENOTCONN;
+ return false;
+ }
+ caif_assert(!(service->modem_flow_on && service->phy_flow_on));
+ *err = -EAGAIN;
+ return false;
+}
+u8 cfsrvl_getphyid(struct layer *layer)
+{
+ struct cfsrvl *servl = container_obj(layer);
+ return servl->dev_info.id;
+}
+
+bool cfsrvl_phyid_match(struct layer *layer, int phyid)
+{
+ struct cfsrvl *servl = container_obj(layer);
+ return servl->dev_info.id == phyid;
+}
diff --git a/net/caif/cfutill.c b/net/caif/cfutill.c
new file mode 100644
index 0000000..5f39c99
--- /dev/null
+++ b/net/caif/cfutill.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <net/caif/caif_layer.h>
+#include <net/caif/cfsrvl.h>
+#include <net/caif/cfpkt.h>
+
+#define container_obj(layr) ((struct cfsrvl *) 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(struct layer *layr, struct cfpkt *pkt);
+static int cfutill_transmit(struct layer *layr, struct cfpkt *pkt);
+
+struct layer *cfutill_create(u8 channel_id, struct dev_info *dev_info)
+{
+ struct cfsrvl *util = kmalloc(sizeof(struct cfsrvl), GFP_ATOMIC);
+ if (!util) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return NULL;
+ }
+ caif_assert(offsetof(struct cfsrvl, layer) == 0);
+ memset(util, 0, sizeof(struct cfsrvl));
+ cfsrvl_init(util, channel_id, dev_info);
+ util->layer.receive = cfutill_receive;
+ util->layer.transmit = cfutill_transmit;
+ snprintf(util->layer.name, CAIF_LAYER_NAME_SZ - 1, "util1");
+ return &util->layer;
+}
+
+static int cfutill_receive(struct layer *layr, struct cfpkt *pkt)
+{
+ u8 cmd = -1;
+ struct cfsrvl *service = container_obj(layr);
+ caif_assert(layr != NULL);
+ caif_assert(layr->up != NULL);
+ caif_assert(layr->up->receive != NULL);
+ caif_assert(layr->up->ctrlcmd != NULL);
+ if (cfpkt_extr_head(pkt, &cmd, 1) < 0) {
+ pr_err("CAIF: %s(): Packet is erroneous!\n", __func__);
+ cfpkt_destroy(pkt);
+ return -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 */
+ pr_err("CAIF: %s(): REMOTE SHUTDOWN REQUEST RECEIVED\n",
+ __func__);
+ layr->ctrlcmd(layr, CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND, 0);
+ service->open = false;
+ cfpkt_destroy(pkt);
+ return 0;
+ default:
+ cfpkt_destroy(pkt);
+ pr_warning("CAIF: %s(): Unknown service control %d (0x%x)\n",
+ __func__, cmd, cmd);
+ return -EPROTO;
+ }
+}
+
+static int cfutill_transmit(struct layer *layr, struct cfpkt *pkt)
+{
+ u8 zero = 0;
+ struct payload_info *info;
+ int ret;
+ struct cfsrvl *service = container_obj(layr);
+ caif_assert(layr != NULL);
+ caif_assert(layr->dn != NULL);
+ caif_assert(layr->dn->transmit != NULL);
+ if (!cfsrvl_ready(service, &ret))
+ return ret;
+
+ if (cfpkt_getlen(pkt) > CAIF_MAX_PAYLOAD_SIZE) {
+ pr_err("CAIF: %s(): packet too large size=%d\n",
+ __func__, cfpkt_getlen(pkt));
+ return -EOVERFLOW;
+ }
+
+ cfpkt_add_head(pkt, &zero, 1);
+ /* Add info for MUX-layer to route the packet out. */
+ info = cfpkt_info(pkt);
+ info->channel_id = service->layer.id;
+ /*
+ * To optimize alignment, we add up the size of CAIF header before
+ * payload.
+ */
+ info->hdr_len = 1;
+ info->dev_info = &service->dev_info;
+ ret = layr->dn->transmit(layr->dn, pkt);
+ if (ret < 0) {
+ u32 tmp32;
+ cfpkt_extr_head(pkt, &tmp32, 4);
+ }
+ return ret;
+}
diff --git a/net/caif/cfveil.c b/net/caif/cfveil.c
new file mode 100644
index 0000000..fcd89cc
--- /dev/null
+++ b/net/caif/cfveil.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/stddef.h>
+#include <linux/slab.h>
+#include <net/caif/caif_layer.h>
+#include <net/caif/cfsrvl.h>
+#include <net/caif/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) container_of(layr, struct cfsrvl, layer)
+
+static int cfvei_receive(struct layer *layr, struct cfpkt *pkt);
+static int cfvei_transmit(struct layer *layr, struct cfpkt *pkt);
+
+struct layer *cfvei_create(u8 channel_id, struct dev_info *dev_info)
+{
+ struct cfsrvl *vei = kmalloc(sizeof(struct cfsrvl), GFP_ATOMIC);
+ if (!vei) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return NULL;
+ }
+ caif_assert(offsetof(struct cfsrvl, layer) == 0);
+ memset(vei, 0, sizeof(struct cfsrvl));
+ cfsrvl_init(vei, channel_id, dev_info);
+ vei->layer.receive = cfvei_receive;
+ vei->layer.transmit = cfvei_transmit;
+ snprintf(vei->layer.name, CAIF_LAYER_NAME_SZ - 1, "vei%d", channel_id);
+ return &vei->layer;
+}
+
+static int cfvei_receive(struct layer *layr, struct cfpkt *pkt)
+{
+ u8 cmd;
+ int ret;
+ caif_assert(layr->up != NULL);
+ caif_assert(layr->receive != NULL);
+ caif_assert(layr->ctrlcmd != NULL);
+
+
+ if (cfpkt_extr_head(pkt, &cmd, 1) < 0) {
+ pr_err("CAIF: %s(): Packet is erroneous!\n", __func__);
+ cfpkt_destroy(pkt);
+ return -EPROTO;
+ }
+ switch (cmd) {
+ case VEI_PAYLOAD:
+ ret = layr->up->receive(layr->up, pkt);
+ return ret;
+ case VEI_FLOW_OFF:
+ layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_OFF_IND, 0);
+ cfpkt_destroy(pkt);
+ return 0;
+ case VEI_FLOW_ON:
+ layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_ON_IND, 0);
+ cfpkt_destroy(pkt);
+ return 0;
+ case VEI_SET_PIN: /* SET RS232 PIN */
+ cfpkt_destroy(pkt);
+ return 0;
+ default: /* SET RS232 PIN */
+ pr_warning("CAIF: %s():Unknown VEI control packet %d (0x%x)!\n",
+ __func__, cmd, cmd);
+ cfpkt_destroy(pkt);
+ return -EPROTO;
+ }
+}
+
+static int cfvei_transmit(struct layer *layr, struct cfpkt *pkt)
+{
+ u8 tmp = 0;
+ struct payload_info *info;
+ int ret;
+ struct cfsrvl *service = container_obj(layr);
+ if (!cfsrvl_ready(service, &ret))
+ return ret;
+ caif_assert(layr->dn != NULL);
+ caif_assert(layr->dn->transmit != NULL);
+ if (!cfpkt_getlen(pkt) > CAIF_MAX_PAYLOAD_SIZE) {
+ pr_warning("CAIF: %s(): Packet too large - size=%d\n",
+ __func__, cfpkt_getlen(pkt));
+ return -EOVERFLOW;
+ }
+
+ if (cfpkt_add_head(pkt, &tmp, 1) < 0) {
+ pr_err("CAIF: %s(): Packet is erroneous!\n", __func__);
+ return -EPROTO;
+ }
+
+ /* Add info-> for MUX-layer to route the packet out. */
+ info = cfpkt_info(pkt);
+ info->channel_id = service->layer.id;
+ info->hdr_len = 1;
+ info->dev_info = &service->dev_info;
+ ret = layr->dn->transmit(layr->dn, pkt);
+ if (ret < 0)
+ cfpkt_extr_head(pkt, &tmp, 1);
+ return ret;
+}
diff --git a/net/caif/cfvidl.c b/net/caif/cfvidl.c
new file mode 100644
index 0000000..74d9849
--- /dev/null
+++ b/net/caif/cfvidl.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <net/caif/caif_layer.h>
+#include <net/caif/cfsrvl.h>
+#include <net/caif/cfpkt.h>
+
+#define container_obj(layr) ((struct cfsrvl *) layr)
+
+static int cfvidl_receive(struct layer *layr, struct cfpkt *pkt);
+static int cfvidl_transmit(struct layer *layr, struct cfpkt *pkt);
+
+struct layer *cfvidl_create(u8 channel_id, struct dev_info *dev_info)
+{
+ struct cfsrvl *vid = kmalloc(sizeof(struct cfsrvl), GFP_ATOMIC);
+ if (!vid) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return NULL;
+ }
+ caif_assert(offsetof(struct cfsrvl, layer) == 0);
+
+ memset(vid, 0, sizeof(struct cfsrvl));
+ cfsrvl_init(vid, channel_id, dev_info);
+ vid->layer.receive = cfvidl_receive;
+ vid->layer.transmit = cfvidl_transmit;
+ snprintf(vid->layer.name, CAIF_LAYER_NAME_SZ - 1, "vid1");
+ return &vid->layer;
+}
+
+static int cfvidl_receive(struct layer *layr, struct cfpkt *pkt)
+{
+ u32 videoheader;
+ if (cfpkt_extr_head(pkt, &videoheader, 4) < 0) {
+ pr_err("CAIF: %s(): Packet is erroneous!\n", __func__);
+ cfpkt_destroy(pkt);
+ return -EPROTO;
+ }
+ return layr->up->receive(layr->up, pkt);
+}
+
+static int cfvidl_transmit(struct layer *layr, struct cfpkt *pkt)
+{
+ struct cfsrvl *service = container_obj(layr);
+ struct payload_info *info;
+ u32 videoheader = 0;
+ int ret;
+ if (!cfsrvl_ready(service, &ret))
+ return ret;
+ cfpkt_add_head(pkt, &videoheader, 4);
+ /* Add info for MUX-layer to route the packet out */
+ info = cfpkt_info(pkt);
+ info->channel_id = service->layer.id;
+ info->dev_info = &service->dev_info;
+ ret = layr->dn->transmit(layr->dn, pkt);
+ if (ret < 0)
+ cfpkt_extr_head(pkt, &videoheader, 4);
+ return ret;
+}
--
1.6.3.3
^ permalink raw reply related
* [PATCH net-next-2.6 v3 04/12] net-caif: add CAIF Link layer device header files
From: sjur.brandeland @ 2010-02-22 22:05 UTC (permalink / raw)
To: netdev, davem, marcel
Cc: daniel.martensson, kaber, stefano.babic, randy.dunlap,
Sjur Braendeland
In-Reply-To: <1266876331-23988-4-git-send-email-sjur.brandeland@stericsson.com>
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
Changes from PATCHv2:
- Use socket addressing when creating channels.
Header files for CAIF Link layer net-device,
and link-layer registration.
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
include/net/caif/caif_dev.h | 81 ++++++++++++++++++++++++++++++++++++++++
include/net/caif/caif_device.h | 57 ++++++++++++++++++++++++++++
2 files changed, 138 insertions(+), 0 deletions(-)
diff --git a/include/net/caif/caif_dev.h b/include/net/caif/caif_dev.h
new file mode 100644
index 0000000..3cf7012
--- /dev/null
+++ b/include/net/caif/caif_dev.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/ sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef CAIF_DEV_H_
+#define CAIF_DEV_H_
+
+#include <net/caif/caif_layer.h>
+#include <net/caif/cfcnfg.h>
+#include <linux/caif/caif_socket.h>
+#include <linux/if.h>
+
+/**
+ * caif_connect_request - Request data for CAIF channel setup.
+ * @sockaddr: Socket address to connect.
+ * @priority: Priority of the connection.
+ * @link_selector: Link selector (high bandwidth or low latency)
+ * @link_name: Name of the CAIF Link Layer to use.
+ *
+ * This struct is used when connecting a CAIF channel.
+ * It contains all CAIF channel configuration options.
+ */
+struct caif_connect_request {
+ int protocol;
+ struct sockaddr_caif sockaddr;
+ enum caif_channel_priority priority;
+ enum caif_link_selector link_selector;
+ char link_name[16];
+ struct caif_param param;
+};
+
+/**
+ * caif_connect_client - Connect a client to CAIF Core Stack.
+ * @config: Channel setup parameters, specifying what address
+ * to connect on the Modem.
+ * @client_layer: User implementation of client layer. This layer
+ * MUST have receive and control callback functions
+ * implemented.
+ *
+ * This function connects a CAIF channel. The Client must implement
+ * the struct layer. This layer represents the Client layer and holds
+ * receive functions and control callback functions. Control callback
+ * function will receive information about connect/disconnect responses,
+ * flow control etc (see enum caif_control).
+ * E.g. CAIF Socket will call this function for each socket it connects
+ * and have one client_layer instance for each socket.
+ */
+int caif_connect_client(struct caif_connect_request *config,
+ struct layer *client_layer);
+
+/**
+ * caif_disconnect_client - Disconnects a client from the CAIF stack.
+ *
+ * @client_layer: Client layer to be removed.
+ */
+int caif_disconnect_client(struct layer *client_layer);
+
+
+/**
+ * connect_req_to_link_param - Translate configuration parameters
+ * from socket format to internal format.
+ * @cnfg: Pointer to configuration handler
+ * @con_req: Configuration parameters supplied in function
+ * caif_connect_client
+ * @channel_setup_param: Parameters supplied to the CAIF Core stack for
+ * setting up channels.
+ *
+ */
+int connect_req_to_link_param(struct cfcnfg *cnfg,
+ struct caif_connect_request *con_req,
+ struct cfctrl_link_param *channel_setup_param);
+
+/**
+ * get_caif_conf() - Get the configuration handler.
+ */
+struct cfcnfg *get_caif_conf(void);
+
+
+#endif /* CAIF_DEV_H_ */
diff --git a/include/net/caif/caif_device.h b/include/net/caif/caif_device.h
new file mode 100644
index 0000000..ba22ee5
--- /dev/null
+++ b/include/net/caif/caif_device.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/ sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef CAIF_DEVICE_H_
+#define CAIF_DEVICE_H_
+#include <linux/kernel.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/caif/caif_socket.h>
+#include <net/caif/caif_device.h>
+
+
+/**
+ * struct caif_dev_common - data shared between CAIF drivers and stack.
+ * @flowctrl: Flow Control callback function. This function is
+ * supplied by CAIF Core Stack and is used by CAIF
+ * Link Layer to send flow-stop to CAIF Core.
+ * The flow information will be distributed to all
+ * clients of CAIF.
+ *
+ * @link_select: Profile of device, either high-bandwidth or
+ * low-latency. This member is set by CAIF Link
+ * Layer Device in order to indicate if this device
+ * is a high bandwidth or low latency device.
+ *
+ * @use_frag: CAIF Frames may be fragmented.
+ * Is set by CAIF Link Layer in order to indicate if the
+ * interface receives fragmented frames that must be
+ * assembled by CAIF Core Layer.
+ *
+ * @use_fcs: Indicate if Frame CheckSum (fcs) is used.
+ * Is set if the physical interface is
+ * using Frame Checksum on the CAIF Frames.
+ *
+ * @use_stx: Indicate STart of frame eXtension (stx) in use.
+ * Is set if the CAIF Link Layer expects
+ * CAIF Frames to start with the STX byte.
+ *
+ * This structure is shared between the CAIF drivers and the CAIF stack.
+ * It is used by the device to register its behavior.
+ * CAIF Core layer must set the member flowctrl in order to supply
+ * CAIF Link Layer with the flow control function.
+ *
+ */
+ struct caif_dev_common {
+ void (*flowctrl)(struct net_device *net, int on);
+ enum caif_link_selector link_select;
+ int use_frag;
+ int use_fcs;
+ int use_stx;
+};
+
+#endif /* CAIF_DEVICE_H_ */
+
--
1.6.3.3
^ permalink raw reply related
* [PATCH net-next-2.6 v3 03/12] net-caif: add CAIF core protocol stack header files
From: sjur.brandeland @ 2010-02-22 22:05 UTC (permalink / raw)
To: netdev, davem, marcel
Cc: daniel.martensson, kaber, stefano.babic, randy.dunlap,
Sjur Braendeland
In-Reply-To: <1266876331-23988-3-git-send-email-sjur.brandeland@stericsson.com>
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
Add include files for the CAIF Core protocol stack.
caif_layer.h - Defines the structure of the CAIF protocol layers
cfcnfg.h - CAIF Configuration Module for services and link layers
cfctrl.h - CAIF Control Protocol Layer
cffrml.h - CAIF Framing Layer
cfmuxl.h - CAIF Muxing Layer
cfpkt.h - CAIF Packet layer (skb helper functions)
cfserl.h - CAIF Serial Layer
cfsrvl.h - CAIF Service Layer
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
include/net/caif/caif_layer.h | 283 +++++++++++++++++++++++++++++++++++++++++
include/net/caif/cfcnfg.h | 134 +++++++++++++++++++
include/net/caif/cfctrl.h | 136 ++++++++++++++++++++
include/net/caif/cffrml.h | 17 +++
include/net/caif/cfmuxl.h | 22 +++
include/net/caif/cfpkt.h | 274 +++++++++++++++++++++++++++++++++++++++
include/net/caif/cfserl.h | 13 ++
include/net/caif/cfsrvl.h | 34 +++++
8 files changed, 913 insertions(+), 0 deletions(-)
diff --git a/include/net/caif/caif_layer.h b/include/net/caif/caif_layer.h
new file mode 100644
index 0000000..5732ad0
--- /dev/null
+++ b/include/net/caif/caif_layer.h
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland / sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef CAIF_LAYER_H_
+#define CAIF_LAYER_H_
+
+#include <linux/list.h>
+
+struct layer;
+struct cfpkt;
+struct cfpktq;
+struct payload_info;
+struct caif_packet_funcs;
+
+#define CAIF_MAX_FRAMESIZE 4096
+#define CAIF_MAX_PAYLOAD_SIZE (4096 - 64)
+#define CAIF_NEEDED_HEADROOM (10)
+#define CAIF_NEEDED_TAILROOM (2)
+
+
+#define CAIF_LAYER_NAME_SZ 16
+#define CAIF_SUCCESS 1
+#define CAIF_FAILURE 0
+
+/**
+ * caif_assert() - Assert function for CAIF.
+ * @assert: expression to evaluate.
+ *
+ * This function will print a error message and a do WARN_ON if the
+ * assertion failes. Normally this will do a stack up at the current location.
+ */
+#define caif_assert(assert)\
+do if (!(assert)) { \
+ pr_err("caif:Assert detected:'%s'\n", #assert); \
+ WARN_ON(!(assert));\
+} while (0)
+
+/**
+ * enum caif_ctrlcmd - CAIF Stack Control Signaling sent in layer.ctrlcmd().
+ *
+ * @CAIF_CTRLCMD_FLOW_OFF_IND: Flow Control is OFF, transmit function
+ * should stop sending data
+ *
+ * @CAIF_CTRLCMD_FLOW_ON_IND: Flow Control is ON, transmit function
+ * can start sending data
+ *
+ * @CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND: Remote end modem has decided to close
+ * down channel
+ *
+ * @CAIF_CTRLCMD_INIT_RSP: Called initially when the layer below
+ * has finished initialization
+ *
+ * @CAIF_CTRLCMD_DEINIT_RSP: Called when de-initialization is
+ * complete
+ *
+ * @CAIF_CTRLCMD_INIT_FAIL_RSP: Called if initialization fails
+ *
+ * @_CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND: CAIF Link layer temporarily cannot
+ * send more packets.
+ * @_CAIF_CTRLCMD_PHYIF_FLOW_ON_IND: Called if CAIF Link layer is able
+ * to send packets again.
+ * @_CAIF_CTRLCMD_PHYIF_DOWN_IND: Called if CAIF Link layer is going
+ * down.
+ *
+ * These commands are sent upwards in the CAIF stack to the CAIF Client.
+ * They are used for signaling originating from the modem or CAIF Link Layer.
+ * These are either responses (*_RSP) or events (*_IND).
+ */
+enum caif_ctrlcmd {
+ CAIF_CTRLCMD_FLOW_OFF_IND,
+ CAIF_CTRLCMD_FLOW_ON_IND,
+ CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND,
+ CAIF_CTRLCMD_INIT_RSP,
+ CAIF_CTRLCMD_DEINIT_RSP,
+ CAIF_CTRLCMD_INIT_FAIL_RSP,
+ _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND,
+ _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND,
+ _CAIF_CTRLCMD_PHYIF_DOWN_IND,
+};
+
+/**
+ * enum caif_modemcmd - Modem Control Signaling, sent from CAIF Client
+ * to the CAIF Link Layer or modem.
+ *
+ * @CAIF_MODEMCMD_FLOW_ON_REQ: Flow Control is ON, transmit function
+ * can start sending data.
+ *
+ * @CAIF_MODEMCMD_FLOW_OFF_REQ: Flow Control is OFF, transmit function
+ * should stop sending data.
+ *
+ * @_CAIF_MODEMCMD_PHYIF_USEFULL: Notify physical layer that it is in use
+ *
+ * @_CAIF_MODEMCMD_PHYIF_USELESS: Notify physical layer that it is
+ * no longer in use.
+ *
+ * These are requests sent 'downwards' in the stack.
+ * Flow ON, OFF can be indicated to the modem.
+ */
+enum caif_modemcmd {
+ CAIF_MODEMCMD_FLOW_ON_REQ = 0,
+ CAIF_MODEMCMD_FLOW_OFF_REQ = 1,
+ _CAIF_MODEMCMD_PHYIF_USEFULL = 3,
+ _CAIF_MODEMCMD_PHYIF_USELESS = 4
+};
+
+/**
+ * enum caif_direction - CAIF Packet Direction.
+ * Indicate if a packet is to be sent out or to be received in.
+ * @CAIF_DIR_IN: Incoming packet received.
+ * @CAIF_DIR_OUT: Outgoing packet to be transmitted.
+ */
+enum caif_direction {
+ CAIF_DIR_IN = 0,
+ CAIF_DIR_OUT = 1
+};
+
+/**
+ * struct layer - CAIF Stack layer.
+ * Defines the framework for the CAIF Core Stack.
+ * @up: Pointer up to the layer above.
+ * @dn: Pointer down to the layer below.
+ * @node: List node used when layer participate in a list.
+ * @receive: Packet receive function.
+ * @transmit: Packet transmit funciton.
+ * @ctrlcmd: Used for control signalling upwards in the stack.
+ * @modemcmd: Used for control signaling downwards in the stack.
+ * @prio: Priority of this layer.
+ * @id: The identity of this layer
+ * @type: The type of this layer
+ * @name: Name of the layer.
+ *
+ * This structure defines the layered structure in CAIF.
+ *
+ * It defines CAIF 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
+ * layer->up->receive(layer->up, packet);
+ *
+ * -In order to send data downwards do
+ * layer->dn->transmit(layer->dn, info, packet);
+ */
+struct layer {
+ struct layer *up;
+ struct layer *dn;
+ struct list_head node;
+
+ /*
+ * receive() - 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 it to the next layer using up->receive().
+ * -# If parsing of the packet fails, the packet must be
+ * destroyed and -1 returned from the function.
+ * -# If parsing succeeds (and above layers return OK) then
+ * the function must return a value > 0.
+ *
+ * Returns result < 0 indicates an error, 0 or positive value
+ * indicates success.
+ *
+ * @layr: Pointer to the current layer the receive function is
+ * implemented for (this pointer).
+ * @cfpkt: Pointer to CaifPacket to be handled.
+ */
+ int (*receive)(struct layer *layr, struct cfpkt *cfpkt);
+
+ /*
+ * transmit() - 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 accessed after passing it to the below
+ * layer using dn->transmit().
+ *
+ * -# If transmit fails, however, the ownership is returned
+ * to thecaller. The caller of "dn->transmit()" must
+ * destroy or resend packet.
+ *
+ * -# Return value less than zero means error, zero or
+ * greater than zero means OK.
+ *
+ * result < 0 indicates an error, 0 or positive value
+ * indicate success.
+ *
+ * @layr: Pointer to the current layer the receive function
+ * isimplemented for (this pointer).
+ * @cfpkt: Pointer to CaifPacket to be handled.
+ */
+ int (*transmit) (struct layer *layr, struct cfpkt *cfpkt);
+
+ /*
+ * cttrlcmd() - Control Function upwards in CAIF Stack.
+ * Used for signaling responses (CAIF_CTRLCMD_*_RSP)
+ * and asynchronous events from the modem (CAIF_CTRLCMD_*_IND)
+ *
+ * @layr: Pointer to the current layer the receive function
+ * is implemented for (this pointer).
+ * @ctrl: Control Command.
+ */
+ void (*ctrlcmd) (struct layer *layr, enum caif_ctrlcmd ctrl,
+ int phyid);
+
+ /*
+ * modemctrl() - Control Function used for controlling the modem.
+ * Used to signal down-wards in the CAIF stack.
+ * Returns 0 on success, < 0 upon failure.
+ *
+ * @layr: Pointer to the current layer the receive function
+ * is implemented for (this pointer).
+ * @ctrl: Control Command.
+ */
+ int (*modemcmd) (struct layer *layr, enum caif_modemcmd ctrl);
+
+ unsigned short prio;
+ unsigned int id;
+ unsigned int type;
+ char name[CAIF_LAYER_NAME_SZ];
+};
+
+/**
+ * layer_set_up() - Set the up pointer for a specified layer.
+ * @layr: Layer where up pointer shall be set.
+ * @above: Layer above.
+ */
+#define layer_set_up(layr, above) ((layr)->up = (struct layer *)(above))
+
+/**
+ * layer_set_dn() - Set the down pointer for a specified layer.
+ * @layr: Layer where down pointer shall be set.
+ * @below: Layer below.
+ */
+#define layer_set_dn(layr, below) ((layr)->dn = (struct layer *)(below))
+
+
+
+/**
+ * struct dev_info - Physical Device info information about physical layer.
+ * @dev: Pointer to native physical device.
+ * @id: Physical ID of the physical connection used by the
+ * logical CAIF 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.
+ */
+struct dev_info {
+ void *dev;
+ unsigned int id;
+};
+
+/**
+ * struct payload_info - Payload information embedded in packet (sk_buff).
+ *
+ * @dev_info: Information about the receiving device.
+ *
+ * @hdr_len: Header length, used to align pay load on 32bit boundary.
+ *
+ * @channel_id: Channel ID of the logical CAIF connection.
+ * Used by mux to insert channel id into the caif packet.
+ */
+struct payload_info {
+ struct dev_info *dev_info;
+ unsigned short hdr_len;
+ unsigned short channel_id;
+};
+
+#endif /* CAIF_LAYER_H_ */
diff --git a/include/net/caif/cfcnfg.h b/include/net/caif/cfcnfg.h
new file mode 100644
index 0000000..57ad876
--- /dev/null
+++ b/include/net/caif/cfcnfg.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef CFCNFG_H_
+#define CFCNFG_H_
+#include <linux/spinlock.h>
+#include <net/caif/caif_layer.h>
+#include <net/caif/cfctrl.h>
+
+struct cfcnfg;
+
+/**
+ * enum cfcnfg_phy_type - Types of physical layers defined in CAIF Stack
+ *
+ * @CFPHYTYPE_FRAG: Fragmented frames physical interface.
+ * @CFPHYTYPE_CAIF: Generic CAIF physical interface
+ */
+enum cfcnfg_phy_type {
+ CFPHYTYPE_FRAG = 1,
+ CFPHYTYPE_CAIF,
+ CFPHYTYPE_MAX
+};
+
+/**
+ * enum cfcnfg_phy_preference - Physical preference HW Abstraction
+ *
+ * @CFPHYPREF_UNSPECIFIED: Default physical interface
+ *
+ * @CFPHYPREF_LOW_LAT: Default physical interface for low-latency
+ * traffic
+ * @CFPHYPREF_HIGH_BW: Default physical interface for high-bandwidth
+ * traffic
+ * @CFPHYPREF_LOOP: TEST only Loopback interface simulating modem
+ * responses.
+ *
+ */
+enum cfcnfg_phy_preference {
+ CFPHYPREF_UNSPECIFIED,
+ CFPHYPREF_LOW_LAT,
+ CFPHYPREF_HIGH_BW,
+ CFPHYPREF_LOOP
+};
+
+/**
+ * cfcnfg_create() - Create the CAIF configuration object.
+ */
+struct cfcnfg *cfcnfg_create(void);
+
+/**
+ * cfcnfg_remove() - Remove the CFCNFG object
+ * @cfg: config object
+ */
+void cfcnfg_remove(struct cfcnfg *cfg);
+
+/**
+ * cfcnfg_add_phy_layer() - Adds a physical layer to the CAIF stack.
+ * @cnfg: Pointer to a CAIF configuration object, created by
+ * cfcnfg_create().
+ * @phy_type: Specifies the type of physical interface, e.g.
+ * CFPHYTYPE_FRAG.
+ * @dev: Pointer to link layer device
+ * @phy_layer: Specify the physical layer. The transmit function
+ * MUST be set in the structure.
+ * @phyid: The assigned physical ID for this layer, used in
+ * cfcnfg_add_adapt_layer to specify PHY for the link.
+ * @pref: The phy (link layer) preference.
+ * @fcs: Specify if checksum is used in CAIF Framing Layer.
+ * @stx: Specify if Start Of Frame eXtention is used.
+ */
+
+void
+cfcnfg_add_phy_layer(struct cfcnfg *cnfg, enum cfcnfg_phy_type phy_type,
+ void *dev, struct layer *phy_layer, u16 *phyid,
+ enum cfcnfg_phy_preference pref,
+ bool fcs, bool stx);
+
+/**
+ * cfcnfg_del_phy_layer - Deletes an phy layer from the CAIF stack.
+ *
+ * @cnfg: Pointer to a CAIF configuration object, created by
+ * cfcnfg_create().
+ * @phy_layer: Adaptation layer to be removed.
+ */
+int cfcnfg_del_phy_layer(struct cfcnfg *cnfg, struct layer *phy_layer);
+
+
+/**
+ * cfcnfg_del_adapt_layer - Deletes an adaptation layer from the CAIF stack.
+ *
+ * @cnfg: Pointer to a CAIF configuration object, created by
+ * cfcnfg_create().
+ * @adap_layer: Adaptation layer to be removed.
+ */
+int cfcnfg_del_adapt_layer(struct cfcnfg *cnfg, struct layer *adap_layer);
+
+
+/**
+ * cfcnfg_add_adaptation_layer - Add an adaptation layer to the CAIF stack.
+ *
+ * The adaptation Layer is where the interface to application or higher-level
+ * driver functionality is implemented.
+ *
+ * @cnfg: Pointer to a CAIF configuration object, created by
+ * cfcnfg_create().
+ * @param: Link setup parameters.
+ * @adap_layer: Specify the adaptation layer; the receive and
+ * flow-control functions MUST be set in the structure.
+ *
+ */
+int
+cfcnfg_add_adaptation_layer(struct cfcnfg *cnfg,
+ struct cfctrl_link_param *param,
+ struct layer *adap_layer);
+/**
+ * cfcnfg_get_phyid() - Get physical ID, given type.
+ * Returns one of the physical interfaces matching the given type.
+ * Zero if no match is found.
+ * @cnfg: Configuration object
+ * @phy_pref: Caif Link Layer preference
+ */
+struct dev_info *cfcnfg_get_phyid(struct cfcnfg *cnfg,
+ enum cfcnfg_phy_preference phy_pref);
+
+/**
+ * cfcnfg_get_named() - Get the Physical Identifier of CAIF Link Layer
+ * @cnfg: Configuration object
+ * @name: Name of the Physical Layer (Caif Link Layer)
+ */
+int cfcnfg_get_named(struct cfcnfg *cnfg, char *name);
+
+#endif /* CFCNFG_H_ */
diff --git a/include/net/caif/cfctrl.h b/include/net/caif/cfctrl.h
new file mode 100644
index 0000000..302e5a2
--- /dev/null
+++ b/include/net/caif/cfctrl.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef CFCTRL_H_
+#define CFCTRL_H_
+#include <net/caif/caif_layer.h>
+#include <net/caif/cfsrvl.h>
+
+/* CAIF Control packet commands */
+enum cfctrl_cmd {
+ 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_MASK = 0xf
+};
+
+/* Channel types */
+enum cfctrl_srv {
+ 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
+};
+
+#define CFCTRL_RSP_BIT 0x20
+#define CFCTRL_ERR_BIT 0x10
+
+struct cfctrl_rsp {
+ void (*linksetup_rsp)(struct layer *layer, u8 linkid,
+ enum cfctrl_srv serv, u8 phyid,
+ struct layer *adapt_layer);
+ void (*linkdestroy_rsp)(struct layer *layer, u8 linkid,
+ struct layer *client_layer);
+ void (*linkerror_ind)(void);
+ void (*enum_rsp)(void);
+ void (*sleep_rsp)(void);
+ void (*wake_rsp)(void);
+ void (*restart_rsp)(void);
+ void (*radioset_rsp)(void);
+ void (*reject_rsp)(struct layer *layer, u8 linkid,
+ struct layer *client_layer);;
+};
+
+/* Link Setup Parameters for CAIF-Links. */
+struct cfctrl_link_param {
+ enum cfctrl_srv linktype;/* (T3,T0) Type of Channel */
+ u8 priority; /* (P4,P0) Priority of the channel */
+ u8 phyid; /* (U2-U0) Physical interface to connect */
+ u8 endpoint; /* (E1,E0) Endpoint for data channels */
+ u8 chtype; /* (H1,H0) Channel-Type, applies to
+ * VEI, DEBUG */
+ union {
+ struct {
+ u8 connid; /* (D7,D0) Video LinkId */
+ } video;
+
+ struct {
+ u32 connid; /* (N31,Ngit0) Connection ID used
+ * for Datagram */
+ } datagram;
+
+ struct {
+ u32 connid; /* Connection ID used for RFM */
+ char volume[20]; /* Volume to mount for RFM */
+ } rfm; /* Configuration for RFM */
+
+ struct {
+ u16 fifosize_kb; /* Psock FIFO size in KB */
+ u16 fifosize_bufs; /* Psock # signal buffers */
+ char name[16]; /* Name of the PSOCK service */
+ u8 params[255]; /* Link setup Parameters> */
+ u16 paramlen; /* Length of Link Setup
+ * Parameters */
+ } utility; /* Configuration for Utility Links (Psock) */
+ } u;
+};
+
+/* This structure is used internally in CFCTRL */
+struct cfctrl_request_info {
+ int sequence_no;
+ enum cfctrl_cmd cmd;
+ u8 channel_id;
+ struct cfctrl_link_param param;
+ struct cfctrl_request_info *next;
+ struct layer *client_layer;
+};
+
+struct cfctrl {
+ struct cfsrvl serv;
+ struct cfctrl_rsp res;
+ atomic_t req_seq_no;
+ atomic_t rsp_seq_no;
+ struct cfctrl_request_info *first_req;
+ spinlock_t info_list_lock;
+#ifndef CAIF_NO_LOOP
+ u8 loop_linkid;
+ int loop_linkused[256];
+ spinlock_t loop_linkid_lock;
+#endif
+
+};
+
+void cfctrl_enum_req(struct layer *cfctrl, u8 physlinkid);
+void cfctrl_linkup_request(struct layer *cfctrl,
+ struct cfctrl_link_param *param,
+ struct layer *user_layer);
+int cfctrl_linkdown_req(struct layer *cfctrl, u8 linkid,
+ struct layer *client);
+void cfctrl_sleep_req(struct layer *cfctrl);
+void cfctrl_wake_req(struct layer *cfctrl);
+void cfctrl_getstartreason_req(struct layer *cfctrl);
+struct layer *cfctrl_create(void);
+void cfctrl_set_dnlayer(struct layer *this, struct layer *dn);
+void cfctrl_set_uplayer(struct layer *this, struct layer *up);
+struct cfctrl_rsp *cfctrl_get_respfuncs(struct layer *layer);
+bool cfctrl_req_eq(struct cfctrl_request_info *r1,
+ struct cfctrl_request_info *r2);
+void cfctrl_insert_req(struct cfctrl *ctrl,
+ struct cfctrl_request_info *req);
+struct cfctrl_request_info *cfctrl_remove_req(struct cfctrl *ctrl,
+ struct cfctrl_request_info *req);
+#endif /* CFCTRL_H_ */
diff --git a/include/net/caif/cffrml.h b/include/net/caif/cffrml.h
new file mode 100644
index 0000000..5412598
--- /dev/null
+++ b/include/net/caif/cffrml.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef CFFRML_H_
+#define CFFRML_H_
+#include <net/caif/caif_layer.h>
+
+struct cffrml;
+struct layer *cffrml_create(u16 phyid, bool DoFCS);
+void cffrml_set_uplayer(struct layer *this, struct layer *up);
+void cffrml_set_dnlayer(struct layer *this, struct layer *dn);
+void cffrml_destroy(struct layer *layer);
+
+#endif /* CFFRML_H_ */
diff --git a/include/net/caif/cfmuxl.h b/include/net/caif/cfmuxl.h
new file mode 100644
index 0000000..16253bf
--- /dev/null
+++ b/include/net/caif/cfmuxl.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef CFMUXL_H_
+#define CFMUXL_H_
+#include <net/caif/caif_layer.h>
+
+struct cfsrvl;
+struct cffrml;
+
+struct layer *cfmuxl_create(void);
+int cfmuxl_set_uplayer(struct layer *layr, struct layer *up, u8 linkid);
+struct layer *cfmuxl_remove_dnlayer(struct layer *layr, u8 phyid);
+int cfmuxl_set_dnlayer(struct layer *layr, struct layer *up, u8 phyid);
+struct layer *cfmuxl_remove_uplayer(struct layer *layr, u8 linkid);
+bool cfmuxl_is_phy_inuse(struct layer *layr, u8 phyid);
+u8 cfmuxl_get_phyid(struct layer *layr, __u8 channel_id);
+
+#endif /* CFMUXL_H_ */
diff --git a/include/net/caif/cfpkt.h b/include/net/caif/cfpkt.h
new file mode 100644
index 0000000..93d8835
--- /dev/null
+++ b/include/net/caif/cfpkt.h
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef CFPKT_H_
+#define CFPKT_H_
+#include <net/caif/caif_layer.h>
+#include <linux/types.h>
+struct cfpkt;
+
+/* Create a CAIF packet.
+ * len: Length of packet to be created
+ * @return New packet.
+ */
+struct cfpkt *cfpkt_create(u16 len);
+
+/* Create a CAIF packet.
+ * data Data to copy.
+ * len Length of packet to be created
+ * @return New packet.
+ */
+struct cfpkt *cfpkt_create_uplink(const unsigned char *data, unsigned int len);
+/*
+ * Destroy a CAIF Packet.
+ * pkt Packet to be destoyed.
+ */
+void cfpkt_destroy(struct cfpkt *pkt);
+
+/*
+ * Extract header from packet.
+ *
+ * pkt Packet to extract header data from.
+ * data Pointer to copy the header data into.
+ * len Length of head data to copy.
+ * @return zero on success and error code upon failure
+ */
+int cfpkt_extr_head(struct cfpkt *pkt, void *data, u16 len);
+
+/*
+ * Peek header from packet.
+ * Reads data from packet without changing packet.
+ *
+ * pkt Packet to extract header data from.
+ * data Pointer to copy the header data into.
+ * len Length of head data to copy.
+ * @return zero on success and error code upon failure
+ */
+int cfpkt_peek_head(struct cfpkt *pkt, void *data, u16 len);
+
+/*
+ * Extract header from trailer (end of packet).
+ *
+ * pkt Packet to extract header data from.
+ * data Pointer to copy the trailer data into.
+ * len Length of header data to copy.
+ * @return zero on success and error code upon failure
+ */
+int cfpkt_extr_trail(struct cfpkt *pkt, void *data, u16 len);
+
+/*
+ * Add header to packet.
+ *
+ *
+ * pkt Packet to add header data to.
+ * data Pointer to data to copy into the header.
+ * len Length of header data to copy.
+ * @return zero on success and error code upon failure
+ */
+int cfpkt_add_head(struct cfpkt *pkt, const void *data, u16 len);
+
+/*
+ * Add trailer to packet.
+ *
+ *
+ * pkt Packet to add trailer data to.
+ * data Pointer to data to copy into the trailer.
+ * len Length of trailer data to copy.
+ * @return zero on success and error code upon failure
+ */
+int cfpkt_add_trail(struct cfpkt *pkt, const void *data, u16 len);
+
+/*
+ * Pad trailer on packet.
+ * Moves data pointer in packet, no content copied.
+ *
+ * pkt Packet in which to pad trailer.
+ * len Length of padding to add.
+ * @return zero on success and error code upon failure
+ */
+int cfpkt_pad_trail(struct cfpkt *pkt, u16 len);
+
+/*
+ * Add a single byte to packet body (tail).
+ *
+ * pkt Packet in which to add byte.
+ * data Byte to add.
+ * @return zero on success and error code upon failure
+ */
+int cfpkt_addbdy(struct cfpkt *pkt, const u8 data);
+
+/*
+ * Add a data to packet body (tail).
+ *
+ * pkt Packet in which to add data.
+ * data Pointer to data to copy into the packet body.
+ * len Length of data to add.
+ * @return zero on success and error code upon failure
+ */
+int cfpkt_add_body(struct cfpkt *pkt, const void *data, u16 len);
+
+/*
+ * Checks whether there are more data to process in packet.
+ * pkt Packet to check.
+ * @return true if more data are available in packet false otherwise
+ */
+bool cfpkt_more(struct cfpkt *pkt);
+
+/*
+ * Checks whether 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 cfpkt_create().
+ * pkt Packet to check.
+ * @return true on error false otherwise
+ */
+bool cfpkt_erroneous(struct cfpkt *pkt);
+
+/*
+ * Get the packet length.
+ * pkt Packet to get length from.
+ * @return Number of bytes in packet.
+ */
+u16 cfpkt_getlen(struct cfpkt *pkt);
+
+/*
+ * Set the packet length, by adjusting the trailer pointer according to length.
+ * pkt Packet to set length.
+ * len Packet length.
+ * @return Number of bytes in packet.
+ */
+int cfpkt_setlen(struct cfpkt *pkt, u16 len);
+
+/*
+ * cfpkt_append - Appends a packet's data to another packet.
+ * dstpkt: Packet to append data into, WILL BE FREED BY THIS FUNCTION
+ * addpkt: Packet to be appended and automatically released,
+ * WILL BE FREED BY THIS FUNCTION.
+ * expectlen: Packet's expected total length. This should be considered
+ * as a hint.
+ * NB: Input packets will be destroyed after appending and cannot be used
+ * after calling this function.
+ * @return The new appended packet.
+ */
+struct cfpkt *cfpkt_append(struct cfpkt *dstpkt, struct cfpkt *addpkt,
+ u16 expectlen);
+
+/*
+ * cfpkt_split - Split a packet into two packets at the specified split point.
+ * pkt: Packet to be split (will contain the first part of the data on exit)
+ * pos: Position to split packet in two parts.
+ * @return The new packet, containing the second part of the data.
+ */
+struct cfpkt *cfpkt_split(struct cfpkt *pkt, u16 pos);
+
+/*
+ * Iteration function, iterates the packet buffers from start to end.
+ *
+ * Checksum iteration function used to iterate buffers
+ * (we may have packets consisting of a chain of buffers)
+ * pkt: Packet to calculate checksum for
+ * iter_func: Function pointer to iteration function
+ * chks: Checksum calculated so far.
+ * buf: Pointer to the buffer to checksum
+ * len: Length of buf.
+ * data: Initial checksum value.
+ * @return Checksum of buffer.
+ */
+
+u16 cfpkt_iterate(struct cfpkt *pkt,
+ u16 (*iter_func)(__u16 chks, void *buf, __u16 len),
+ u16 data);
+
+/* Append by giving user access to packet buffer
+ * cfpkt Packet to append to
+ * buf Buffer inside pkt that user shall copy data into
+ * buflen Length of buffer and number of bytes added to packet
+ * @return 0 on error, 1 on success
+ */
+int cfpkt_raw_append(struct cfpkt *cfpkt, void **buf, unsigned int buflen);
+
+/* Extract by giving user access to packet buffer
+ * cfpkt Packet to extract from
+ * buf Buffer inside pkt that user shall copy data from
+ * buflen Length of buffer and number of bytes removed from packet
+ * @return 0 on error, 1 on success
+ */
+int cfpkt_raw_extract(struct cfpkt *cfpkt, void **buf, unsigned int buflen);
+
+/* Map from a "native" packet (e.g. Linux Socket Buffer) to a CAIF packet.
+ * dir - Direction indicating whether this packet is to be sent or received.
+ * nativepkt - The native packet to be transformed to a CAIF packet
+ * @return The mapped CAIF Packet CFPKT.
+ */
+struct cfpkt *cfpkt_fromnative(enum caif_direction dir, void *nativepkt);
+
+/* Map from a CAIF packet to a "native" packet (e.g. Linux Socket Buffer).
+ * pkt - The CAIF packet to be transformed into a "native" packet.
+ * @return The native packet transformed from a CAIF packet.
+ */
+void *cfpkt_tonative(struct cfpkt *pkt);
+
+/*
+ * Insert a packet in the packet queue.
+ * pktq Packet queue to insert into
+ * pkt Packet to be inserted in queue
+ * prio Priority of packet
+ */
+void cfpkt_queue(struct cfpktq *pktq, struct cfpkt *pkt,
+ unsigned short prio);
+
+/*
+ * Remove a packet from the packet queue.
+ * pktq Packet queue to fetch packets from.
+ * @return Dequeued packet.
+ */
+struct cfpkt *cfpkt_dequeue(struct cfpktq *pktq);
+
+/*
+ * Peek into a packet from the packet queue.
+ * pktq Packet queue to fetch packets from.
+ * @return Peeked packet.
+ */
+struct cfpkt *cfpkt_qpeek(struct cfpktq *pktq);
+
+/*
+ * Initiates the packet queue.
+ * @return Pointer to new packet queue.
+ */
+struct cfpktq *cfpktq_create(void);
+
+/*
+ * Get the number of packets in the queue.
+ * pktq Packet queue to fetch count from.
+ * @return Number of packets in queue.
+ */
+int cfpkt_qcount(struct cfpktq *pktq);
+
+/*
+ * Put content of packet into buffer for debuging purposes.
+ * pkt Packet to copy data from
+ * buf Buffer to copy data into
+ * buflen Length of data to copy
+ * @return Pointer to copied data
+ */
+char *cfpkt_log_pkt(struct cfpkt *pkt, char *buf, int buflen);
+
+/*
+ * Clones a packet and releases the original packet.
+ * This is used for taking ownership of a packet e.g queueing.
+ * pkt Packet to clone and release.
+ * @return Cloned packet.
+ */
+struct cfpkt *cfpkt_clone_release(struct cfpkt *pkt);
+
+
+/*
+ * Returns packet information for a packet.
+ * pkt Packet to get info from;
+ * @return Packet information
+ */
+struct payload_info *cfpkt_info(struct cfpkt *pkt);
+/*! @} */
+#endif /* CFPKT_H_ */
diff --git a/include/net/caif/cfserl.h b/include/net/caif/cfserl.h
new file mode 100644
index 0000000..fd0ec77
--- /dev/null
+++ b/include/net/caif/cfserl.h
@@ -0,0 +1,13 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef CFSERL_H_
+#define CFSERL_H_
+#include <net/caif/caif_layer.h>
+
+struct layer *cfserl_create(int type, int instance, bool use_stx);
+
+#endif /* CFSERL_H_ */
diff --git a/include/net/caif/cfsrvl.h b/include/net/caif/cfsrvl.h
new file mode 100644
index 0000000..ddbe52b
--- /dev/null
+++ b/include/net/caif/cfsrvl.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef CFSRVL_H_
+#define CFSRVL_H_
+#include <linux/list.h>
+#include <linux/stddef.h>
+#include <linux/types.h>
+struct cfsrvl {
+ struct layer layer;
+ bool open;
+ bool phy_flow_on;
+ bool modem_flow_on;
+ struct dev_info dev_info;
+};
+
+struct layer *cfvei_create(u8 linkid, struct dev_info *dev_info);
+struct layer *cfdgml_create(u8 linkid, struct dev_info *dev_info);
+struct layer *cfutill_create(u8 linkid, struct dev_info *dev_info);
+struct layer *cfvidl_create(u8 linkid, struct dev_info *dev_info);
+struct layer *cfrfml_create(u8 linkid, struct dev_info *dev_info);
+struct layer *cfdbgl_create(u8 linkid, struct dev_info *dev_info);
+bool cfsrvl_phyid_match(struct layer *layer, int phyid);
+void cfservl_destroy(struct layer *layer);
+void cfsrvl_init(struct cfsrvl *service,
+ u8 channel_id,
+ struct dev_info *dev_info);
+bool cfsrvl_ready(struct cfsrvl *service, int *err);
+u8 cfsrvl_getphyid(struct layer *layer);
+
+#endif /* CFSRVL_H_ */
--
1.6.3.3
^ 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