aboutsummaryrefslogtreecommitdiff
path: root/net/caif
diff options
context:
space:
mode:
Diffstat (limited to 'net/caif')
-rw-r--r--net/caif/Kconfig13
-rw-r--r--net/caif/Makefile3
-rw-r--r--net/caif/caif_config_util.c99
-rw-r--r--net/caif/caif_dev.c585
-rw-r--r--net/caif/caif_socket.c257
-rw-r--r--net/caif/caif_usb.c204
-rw-r--r--net/caif/cfcnfg.c591
-rw-r--r--net/caif/cfctrl.c282
-rw-r--r--net/caif/cfdbgl.c13
-rw-r--r--net/caif/cfdgml.c32
-rw-r--r--net/caif/cffrml.c82
-rw-r--r--net/caif/cfmuxl.c197
-rw-r--r--net/caif/cfpkt_skbuff.c228
-rw-r--r--net/caif/cfrfml.c44
-rw-r--r--net/caif/cfserl.c24
-rw-r--r--net/caif/cfsrvl.c70
-rw-r--r--net/caif/cfutill.c23
-rw-r--r--net/caif/cfveil.c22
-rw-r--r--net/caif/cfvidl.c20
-rw-r--r--net/caif/chnl_net.c132
20 files changed, 1552 insertions, 1369 deletions
diff --git a/net/caif/Kconfig b/net/caif/Kconfig
index 529750da962..d3694953b1d 100644
--- a/net/caif/Kconfig
+++ b/net/caif/Kconfig
@@ -25,7 +25,7 @@ config CAIF_DEBUG
bool "Enable Debug"
depends on CAIF
default n
- --- help ---
+ ---help---
Enable the inclusion of debug code in the CAIF stack.
Be aware that doing this will impact performance.
If unsure say N.
@@ -40,3 +40,14 @@ config CAIF_NETDEV
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.
+
+config CAIF_USB
+ tristate "CAIF USB support"
+ depends on CAIF
+ default n
+ ---help---
+ Say Y if you are using CAIF over USB CDC NCM.
+ 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 N.
diff --git a/net/caif/Makefile b/net/caif/Makefile
index 9d38e406e4a..cc2b51154d0 100644
--- a/net/caif/Makefile
+++ b/net/caif/Makefile
@@ -5,10 +5,11 @@ caif-y := caif_dev.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
+ cfsrvl.o cfpkt_skbuff.o
obj-$(CONFIG_CAIF) += caif.o
obj-$(CONFIG_CAIF_NETDEV) += chnl_net.o
obj-$(CONFIG_CAIF) += caif_socket.o
+obj-$(CONFIG_CAIF_USB) += caif_usb.o
export-y := caif.o
diff --git a/net/caif/caif_config_util.c b/net/caif/caif_config_util.c
deleted file mode 100644
index d522d8c1703..00000000000
--- a/net/caif/caif_config_util.c
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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;
- int res;
-
- memset(l, 0, sizeof(*l));
- /* In caif protocol low value is high priority */
- l->priority = CAIF_PRIO_MAX - s->priority + 1;
-
- if (s->ifindex != 0){
- res = cfcnfg_get_id_from_ifi(cnfg, s->ifindex);
- if (res < 0)
- return res;
- l->phyid = res;
- }
- 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;
- case CAIFPROTO_DEBUG:
- l->linktype = CFCTRL_SRV_DBG;
- l->endpoint = s->sockaddr.u.dbg.service;
- l->chtype = s->sockaddr.u.dbg.type;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c
index a42a408306e..edbca468fa7 100644
--- a/net/caif/caif_dev.c
+++ b/net/caif/caif_dev.c
@@ -1,83 +1,108 @@
/*
* CAIF Interface registration.
* Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * Author: Sjur Brendeland
* 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>
+ * Borrowed heavily from file: pn_dev.c. Thanks to Remi Denis-Courmont
* and Sakari Ailus <sakari.ailus@nokia.com>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
-#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 <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/spinlock.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/caif_dev.h>
#include <net/caif/cfpkt.h>
#include <net/caif/cfcnfg.h>
+#include <net/caif/cfserl.h>
MODULE_LICENSE("GPL");
-#define TIMEOUT (HZ*5)
/* Used for local tracking of the CAIF net devices */
struct caif_device_entry {
struct cflayer layer;
struct list_head list;
- atomic_t in_use;
- atomic_t state;
- u16 phyid;
struct net_device *netdev;
- wait_queue_head_t event;
+ int __percpu *pcpu_refcnt;
+ spinlock_t flow_lock;
+ struct sk_buff *xoff_skb;
+ void (*xoff_skb_dtor)(struct sk_buff *skb);
+ bool xoff;
};
struct caif_device_entry_list {
struct list_head list;
/* Protects simulanous deletes in list */
- spinlock_t lock;
+ struct mutex lock;
};
struct caif_net {
+ struct cfcnfg *cfg;
struct caif_device_entry_list caifdevs;
};
static int caif_net_id;
-static struct cfcnfg *cfg;
+static int q_high = 50; /* Percent */
+
+struct cfcnfg *get_cfcnfg(struct net *net)
+{
+ struct caif_net *caifn;
+ caifn = net_generic(net, caif_net_id);
+ return caifn->cfg;
+}
+EXPORT_SYMBOL(get_cfcnfg);
static 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;
}
+static void caifd_put(struct caif_device_entry *e)
+{
+ this_cpu_dec(*e->pcpu_refcnt);
+}
+
+static void caifd_hold(struct caif_device_entry *e)
+{
+ this_cpu_inc(*e->pcpu_refcnt);
+}
+
+static int caifd_refcnt_read(struct caif_device_entry *e)
+{
+ int i, refcnt = 0;
+ for_each_possible_cpu(i)
+ refcnt += *per_cpu_ptr(e->pcpu_refcnt, i);
+ return refcnt;
+}
+
/* 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);
+
+ caifd = kzalloc(sizeof(*caifd), GFP_KERNEL);
if (!caifd)
return NULL;
+ caifd->pcpu_refcnt = alloc_percpu(int);
+ if (!caifd->pcpu_refcnt) {
+ kfree(caifd);
+ return NULL;
+ }
caifd->netdev = dev;
- list_add(&caifd->list, &caifdevs->list);
- init_waitqueue_head(&caifd->event);
+ dev_hold(dev);
return caifd;
}
@@ -86,100 +111,166 @@ 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) {
+
+ list_for_each_entry_rcu(caifd, &caifdevs->list, list) {
if (caifd->netdev == dev)
return caifd;
}
return NULL;
}
-static void caif_device_destroy(struct net_device *dev)
+static void caif_flow_cb(struct sk_buff *skb)
{
- 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;
+ void (*dtor)(struct sk_buff *skb) = NULL;
+ bool send_xoff;
- spin_lock_bh(&caifdevs->lock);
- caifd = caif_get(dev);
- if (caifd == NULL) {
- spin_unlock_bh(&caifdevs->lock);
+ WARN_ON(skb->dev == NULL);
+
+ rcu_read_lock();
+ caifd = caif_get(skb->dev);
+
+ WARN_ON(caifd == NULL);
+ if (caifd == NULL)
return;
- }
- list_del(&caifd->list);
- spin_unlock_bh(&caifdevs->lock);
+ caifd_hold(caifd);
+ rcu_read_unlock();
+
+ spin_lock_bh(&caifd->flow_lock);
+ send_xoff = caifd->xoff;
+ caifd->xoff = 0;
+ dtor = caifd->xoff_skb_dtor;
+
+ if (WARN_ON(caifd->xoff_skb != skb))
+ skb = NULL;
+
+ caifd->xoff_skb = NULL;
+ caifd->xoff_skb_dtor = NULL;
+
+ spin_unlock_bh(&caifd->flow_lock);
- kfree(caifd);
+ if (dtor && skb)
+ dtor(skb);
+
+ if (send_xoff)
+ caifd->layer.up->
+ ctrlcmd(caifd->layer.up,
+ _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND,
+ caifd->layer.id);
+ caifd_put(caifd);
}
static int transmit(struct cflayer *layer, struct cfpkt *pkt)
{
+ int err, high = 0, qlen = 0;
struct caif_device_entry *caifd =
container_of(layer, struct caif_device_entry, layer);
- struct sk_buff *skb, *skb2;
- int ret = -EINVAL;
+ struct sk_buff *skb;
+ struct netdev_queue *txq;
+
+ rcu_read_lock_bh();
+
skb = cfpkt_tonative(pkt);
skb->dev = caifd->netdev;
+ skb_reset_network_header(skb);
+ skb->protocol = htons(ETH_P_CAIF);
+
+ /* Check if we need to handle xoff */
+ if (likely(caifd->netdev->tx_queue_len == 0))
+ goto noxoff;
+
+ if (unlikely(caifd->xoff))
+ goto noxoff;
+
+ if (likely(!netif_queue_stopped(caifd->netdev))) {
+ /* If we run with a TX queue, check if the queue is too long*/
+ txq = netdev_get_tx_queue(skb->dev, 0);
+ qlen = qdisc_qlen(rcu_dereference_bh(txq->qdisc));
+
+ if (likely(qlen == 0))
+ goto noxoff;
+
+ high = (caifd->netdev->tx_queue_len * q_high) / 100;
+ if (likely(qlen < high))
+ goto noxoff;
+ }
+
+ /* Hold lock while accessing xoff */
+ spin_lock_bh(&caifd->flow_lock);
+ if (caifd->xoff) {
+ spin_unlock_bh(&caifd->flow_lock);
+ goto noxoff;
+ }
+
/*
- * 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.
+ * Handle flow off, we do this by temporary hi-jacking this
+ * skb's destructor function, and replace it with our own
+ * flow-on callback. The callback will set flow-on and call
+ * the original destructor.
*/
- if (netif_queue_stopped(caifd->netdev))
- return -EAGAIN;
- skb2 = skb_get(skb);
- ret = dev_queue_xmit(skb2);
+ pr_debug("queue has stopped(%d) or is full (%d > %d)\n",
+ netif_queue_stopped(caifd->netdev),
+ qlen, high);
+ caifd->xoff = 1;
+ caifd->xoff_skb = skb;
+ caifd->xoff_skb_dtor = skb->destructor;
+ skb->destructor = caif_flow_cb;
+ spin_unlock_bh(&caifd->flow_lock);
- if (!ret)
- kfree_skb(skb);
- else
- return -EAGAIN;
+ caifd->layer.up->ctrlcmd(caifd->layer.up,
+ _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND,
+ caifd->layer.id);
+noxoff:
+ rcu_read_unlock_bh();
- return 0;
-}
+ err = dev_queue_xmit(skb);
+ if (err > 0)
+ err = -EIO;
-static int modemcmd(struct cflayer *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;
+ return err;
}
/*
- * Stuff received packets to associated sockets.
+ * Stuff received packets into the CAIF stack.
* 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);
+ int err;
+
pkt = cfpkt_fromnative(CAIF_DIR_IN, skb);
+
+ rcu_read_lock();
caifd = caif_get(dev);
- if (!caifd || !caifd->layer.up || !caifd->layer.up->receive)
- return NET_RX_DROP;
- if (caifd->layer.up->receive(caifd->layer.up, pkt))
+ if (!caifd || !caifd->layer.up || !caifd->layer.up->receive ||
+ !netif_oper_up(caifd->netdev)) {
+ rcu_read_unlock();
+ kfree_skb(skb);
return NET_RX_DROP;
+ }
- return 0;
+ /* Hold reference to netdevice while using CAIF stack */
+ caifd_hold(caifd);
+ rcu_read_unlock();
+
+ err = caifd->layer.up->receive(caifd->layer.up, pkt);
+
+ /* For -EILSEQ the packet is not freed so so it now */
+ if (err == -EILSEQ)
+ cfpkt_destroy(pkt);
+
+ /* Release reference to stack upwards */
+ caifd_put(caifd);
+
+ if (err != 0)
+ err = NET_RX_DROP;
+ return err;
}
static struct packet_type caif_packet_type __read_mostly = {
@@ -189,129 +280,208 @@ static struct packet_type caif_packet_type __read_mostly = {
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)
+ struct caif_device_entry *caifd;
+
+ rcu_read_lock();
+
+ caifd = caif_get(dev);
+ if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) {
+ rcu_read_unlock();
return;
+ }
+
+ caifd_hold(caifd);
+ rcu_read_unlock();
caifd->layer.up->ctrlcmd(caifd->layer.up,
on ?
_CAIF_CTRLCMD_PHYIF_FLOW_ON_IND :
_CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND,
caifd->layer.id);
+ caifd_put(caifd);
+}
+
+void caif_enroll_dev(struct net_device *dev, struct caif_dev_common *caifdev,
+ struct cflayer *link_support, int head_room,
+ struct cflayer **layer,
+ int (**rcv_func)(struct sk_buff *, struct net_device *,
+ struct packet_type *,
+ struct net_device *))
+{
+ struct caif_device_entry *caifd;
+ enum cfcnfg_phy_preference pref;
+ struct cfcnfg *cfg = get_cfcnfg(dev_net(dev));
+ struct caif_device_entry_list *caifdevs;
+
+ caifdevs = caif_device_list(dev_net(dev));
+ caifd = caif_device_alloc(dev);
+ if (!caifd)
+ return;
+ *layer = &caifd->layer;
+ spin_lock_init(&caifd->flow_lock);
+
+ switch (caifdev->link_select) {
+ case CAIF_LINK_HIGH_BANDW:
+ pref = CFPHYPREF_HIGH_BW;
+ break;
+ case CAIF_LINK_LOW_LATENCY:
+ pref = CFPHYPREF_LOW_LAT;
+ break;
+ default:
+ pref = CFPHYPREF_HIGH_BW;
+ break;
+ }
+ mutex_lock(&caifdevs->lock);
+ list_add_rcu(&caifd->list, &caifdevs->list);
+
+ strncpy(caifd->layer.name, dev->name,
+ sizeof(caifd->layer.name) - 1);
+ caifd->layer.name[sizeof(caifd->layer.name) - 1] = 0;
+ caifd->layer.transmit = transmit;
+ cfcnfg_add_phy_layer(cfg,
+ dev,
+ &caifd->layer,
+ pref,
+ link_support,
+ caifdev->use_fcs,
+ head_room);
+ mutex_unlock(&caifdevs->lock);
+ if (rcv_func)
+ *rcv_func = receive;
}
+EXPORT_SYMBOL(caif_enroll_dev);
/* notify Caif of device events */
static int caif_device_notify(struct notifier_block *me, unsigned long what,
- void *arg)
+ void *ptr)
{
- struct net_device *dev = arg;
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct caif_device_entry *caifd = NULL;
struct caif_dev_common *caifdev;
- enum cfcnfg_phy_preference pref;
- int res = -EINVAL;
- enum cfcnfg_phy_type phy_type;
+ struct cfcnfg *cfg;
+ struct cflayer *layer, *link_support;
+ int head_room = 0;
+ struct caif_device_entry_list *caifdevs;
+
+ cfg = get_cfcnfg(dev_net(dev));
+ caifdevs = caif_device_list(dev_net(dev));
- if (dev->type != ARPHRD_CAIF)
+ caifd = caif_get(dev);
+ if (caifd == NULL && dev->type != ARPHRD_CAIF)
return 0;
switch (what) {
case NETDEV_REGISTER:
- netdev_info(dev, "register\n");
- caifd = caif_device_alloc(dev);
- if (caifd == NULL)
+ if (caifd != NULL)
break;
+
caifdev = netdev_priv(dev);
+
+ link_support = NULL;
+ if (caifdev->use_frag) {
+ head_room = 1;
+ link_support = cfserl_create(dev->ifindex,
+ caifdev->use_stx);
+ if (!link_support) {
+ pr_warn("Out of memory\n");
+ break;
+ }
+ }
+ caif_enroll_dev(dev, caifdev, link_support, head_room,
+ &layer, NULL);
caifdev->flowctrl = dev_flowctrl;
- atomic_set(&caifd->state, what);
- res = 0;
break;
case NETDEV_UP:
- netdev_info(dev, "up\n");
+ rcu_read_lock();
+
caifd = caif_get(dev);
- if (caifd == NULL)
- break;
- caifdev = netdev_priv(dev);
- if (atomic_read(&caifd->state) == NETDEV_UP) {
- netdev_info(dev, "already up\n");
+ if (caifd == NULL) {
+ rcu_read_unlock();
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;
-
- switch (caifdev->link_select) {
- case CAIF_LINK_HIGH_BANDW:
- pref = CFPHYPREF_HIGH_BW;
- break;
- case CAIF_LINK_LOW_LATENCY:
- pref = CFPHYPREF_LOW_LAT;
- break;
- default:
- pref = CFPHYPREF_HIGH_BW;
- break;
- }
- dev_hold(dev);
- cfcnfg_add_phy_layer(get_caif_conf(),
- phy_type,
- dev,
- &caifd->layer,
- &caifd->phyid,
- pref,
- 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;
+
+ caifd->xoff = 0;
+ cfcnfg_set_phy_state(cfg, &caifd->layer, true);
+ rcu_read_unlock();
+
break;
- case NETDEV_GOING_DOWN:
+ case NETDEV_DOWN:
+ rcu_read_lock();
+
caifd = caif_get(dev);
- if (caifd == NULL)
- break;
- netdev_info(dev, "going down\n");
+ if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) {
+ rcu_read_unlock();
+ return -EINVAL;
+ }
- if (atomic_read(&caifd->state) == NETDEV_GOING_DOWN ||
- atomic_read(&caifd->state) == NETDEV_DOWN)
- break;
+ cfcnfg_set_phy_state(cfg, &caifd->layer, false);
+ caifd_hold(caifd);
+ rcu_read_unlock();
- 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);
- might_sleep();
- 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;
- netdev_info(dev, "down\n");
- if (atomic_read(&caifd->in_use))
- netdev_warn(dev,
- "Unregistering an active CAIF device\n");
- cfcnfg_del_phy_layer(get_caif_conf(), &caifd->layer);
- dev_put(dev);
- atomic_set(&caifd->state, what);
+ spin_lock_bh(&caifd->flow_lock);
+
+ /*
+ * Replace our xoff-destructor with original destructor.
+ * We trust that skb->destructor *always* is called before
+ * the skb reference is invalid. The hijacked SKB destructor
+ * takes the flow_lock so manipulating the skb->destructor here
+ * should be safe.
+ */
+ if (caifd->xoff_skb_dtor != NULL && caifd->xoff_skb != NULL)
+ caifd->xoff_skb->destructor = caifd->xoff_skb_dtor;
+
+ caifd->xoff = 0;
+ caifd->xoff_skb_dtor = NULL;
+ caifd->xoff_skb = NULL;
+
+ spin_unlock_bh(&caifd->flow_lock);
+ caifd_put(caifd);
break;
case NETDEV_UNREGISTER:
+ mutex_lock(&caifdevs->lock);
+
caifd = caif_get(dev);
- if (caifd == NULL)
+ if (caifd == NULL) {
+ mutex_unlock(&caifdevs->lock);
break;
- netdev_info(dev, "unregister\n");
- atomic_set(&caifd->state, what);
- caif_device_destroy(dev);
+ }
+ list_del_rcu(&caifd->list);
+
+ /*
+ * NETDEV_UNREGISTER is called repeatedly until all reference
+ * counts for the net-device are released. If references to
+ * caifd is taken, simply ignore NETDEV_UNREGISTER and wait for
+ * the next call to NETDEV_UNREGISTER.
+ *
+ * If any packets are in flight down the CAIF Stack,
+ * cfcnfg_del_phy_layer will return nonzero.
+ * If no packets are in flight, the CAIF Stack associated
+ * with the net-device un-registering is freed.
+ */
+
+ if (caifd_refcnt_read(caifd) != 0 ||
+ cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0) {
+
+ pr_info("Wait for device inuse\n");
+ /* Enrole device if CAIF Stack is still in use */
+ list_add_rcu(&caifd->list, &caifdevs->list);
+ mutex_unlock(&caifdevs->lock);
+ break;
+ }
+
+ synchronize_rcu();
+ dev_put(caifd->netdev);
+ free_percpu(caifd->pcpu_refcnt);
+ kfree(caifd);
+
+ mutex_unlock(&caifdevs->lock);
break;
}
return 0;
@@ -322,61 +492,51 @@ static struct notifier_block caif_device_notifier = {
.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 cflayer *client_layer, int *ifindex,
- int *headroom, int *tailroom)
-{
- struct cfctrl_link_param param;
- int ret;
- ret = connect_req_to_link_param(get_caif_conf(), conn_req, &param);
- if (ret)
- return ret;
- /* Hook up the adaptation layer. */
- return cfcnfg_add_adaptation_layer(get_caif_conf(), &param,
- client_layer, ifindex,
- headroom, tailroom);
-}
-EXPORT_SYMBOL(caif_connect_client);
-
-int caif_disconnect_client(struct cflayer *adap_layer)
-{
- return cfcnfg_disconn_adapt_layer(get_caif_conf(), adap_layer);
-}
-EXPORT_SYMBOL(caif_disconnect_client);
-
-void caif_release_client(struct cflayer *adap_layer)
-{
- cfcnfg_release_adap_layer(adap_layer);
-}
-EXPORT_SYMBOL(caif_release_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);
+ mutex_init(&caifn->caifdevs.lock);
+
+ caifn->cfg = cfcnfg_create();
+ if (!caifn->cfg)
+ return -ENOMEM;
+
return 0;
}
static void caif_exit_net(struct net *net)
{
- struct net_device *dev;
- int res;
+ struct caif_device_entry *caifd, *tmp;
+ struct caif_device_entry_list *caifdevs =
+ caif_device_list(net);
+ struct cfcnfg *cfg = get_cfcnfg(net);
+
rtnl_lock();
- for_each_netdev(net, dev) {
- if (dev->type != ARPHRD_CAIF)
- continue;
- res = dev_close(dev);
- caif_device_destroy(dev);
+ mutex_lock(&caifdevs->lock);
+
+ list_for_each_entry_safe(caifd, tmp, &caifdevs->list, list) {
+ int i = 0;
+ list_del_rcu(&caifd->list);
+ cfcnfg_set_phy_state(cfg, &caifd->layer, false);
+
+ while (i < 10 &&
+ (caifd_refcnt_read(caifd) != 0 ||
+ cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0)) {
+
+ pr_info("Wait for device inuse\n");
+ msleep(250);
+ i++;
+ }
+ synchronize_rcu();
+ dev_put(caifd->netdev);
+ free_percpu(caifd->pcpu_refcnt);
+ kfree(caifd);
}
+ cfcnfg_remove(cfg);
+
+ mutex_unlock(&caifdevs->lock);
rtnl_unlock();
}
@@ -391,32 +551,23 @@ static struct pernet_operations caif_net_ops = {
static int __init caif_device_init(void)
{
int result;
- cfg = cfcnfg_create();
- if (!cfg) {
- pr_warn("can't create cfcnfg\n");
- goto err_cfcnfg_create_failed;
- }
- result = register_pernet_device(&caif_net_ops);
- if (result) {
- kfree(cfg);
- cfg = NULL;
+ result = register_pernet_subsys(&caif_net_ops);
+
+ if (result)
return result;
- }
- dev_add_pack(&caif_packet_type);
+
register_netdevice_notifier(&caif_device_notifier);
+ dev_add_pack(&caif_packet_type);
return result;
-err_cfcnfg_create_failed:
- return -ENODEV;
}
static 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);
+ dev_remove_pack(&caif_packet_type);
+ unregister_pernet_subsys(&caif_net_ops);
}
module_init(caif_device_init);
diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c
index 1bf0cf50379..e8437094d15 100644
--- a/net/caif/caif_socket.c
+++ b/net/caif/caif_socket.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland sjur.brandeland@stericsson.com
+ * Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*/
@@ -19,7 +19,7 @@
#include <linux/uaccess.h>
#include <linux/debugfs.h>
#include <linux/caif/caif_socket.h>
-#include <asm/atomic.h>
+#include <linux/pkt_sched.h>
#include <net/sock.h>
#include <net/tcp_states.h>
#include <net/caif/caif_layer.h>
@@ -43,33 +43,9 @@ enum caif_states {
#define TX_FLOW_ON_BIT 1
#define RX_FLOW_ON_BIT 2
-static struct dentry *debugfsdir;
-
-#ifdef CONFIG_DEBUG_FS
-struct debug_fs_counter {
- atomic_t caif_nr_socks;
- atomic_t num_connect_req;
- atomic_t num_connect_resp;
- atomic_t num_connect_fail_resp;
- atomic_t num_disconnect;
- 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;
-};
-static 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)
-#define dbfs_atomic_dec(v)
-#endif
-
struct caifsock {
struct sock sk; /* must be first member */
struct cflayer layer;
- char name[CAIF_LAYER_NAME_SZ]; /* Used for debugging */
u32 flow_state;
struct caif_connect_request conn_req;
struct mutex readlock;
@@ -148,28 +124,25 @@ static void caif_flow_ctrl(struct sock *sk, int mode)
static int caif_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
int err;
- int skb_len;
unsigned long flags;
struct sk_buff_head *list = &sk->sk_receive_queue;
struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >=
- (unsigned)sk->sk_rcvbuf && rx_flow_is_on(cf_sk)) {
- pr_debug("sending flow OFF (queue len = %d %d)\n",
- atomic_read(&cf_sk->sk.sk_rmem_alloc),
- sk_rcvbuf_lowwater(cf_sk));
+ (unsigned int)sk->sk_rcvbuf && rx_flow_is_on(cf_sk)) {
+ net_dbg_ratelimited("sending flow OFF (queue len = %d %d)\n",
+ atomic_read(&cf_sk->sk.sk_rmem_alloc),
+ sk_rcvbuf_lowwater(cf_sk));
set_rx_flow_off(cf_sk);
- dbfs_atomic_inc(&cnt.num_rx_flow_off);
caif_flow_ctrl(sk, CAIF_MODEMCMD_FLOW_OFF_REQ);
}
err = sk_filter(sk, skb);
if (err)
return err;
- if (!sk_rmem_schedule(sk, skb->truesize) && rx_flow_is_on(cf_sk)) {
+ if (!sk_rmem_schedule(sk, skb, skb->truesize) && rx_flow_is_on(cf_sk)) {
set_rx_flow_off(cf_sk);
- pr_debug("sending flow OFF due to rmem_schedule\n");
- dbfs_atomic_inc(&cnt.num_rx_flow_off);
+ net_dbg_ratelimited("sending flow OFF due to rmem_schedule\n");
caif_flow_ctrl(sk, CAIF_MODEMCMD_FLOW_OFF_REQ);
}
skb->dev = NULL;
@@ -179,14 +152,13 @@ static int caif_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
* may be freed by other threads of control pulling packets
* from the queue.
*/
- skb_len = skb->len;
spin_lock_irqsave(&list->lock, flags);
if (!sock_flag(sk, SOCK_DEAD))
__skb_queue_tail(list, skb);
spin_unlock_irqrestore(&list->lock, flags);
if (!sock_flag(sk, SOCK_DEAD))
- sk->sk_data_ready(sk, skb_len);
+ sk->sk_data_ready(sk);
else
kfree_skb(skb);
return 0;
@@ -202,39 +174,51 @@ static int caif_sktrecv_cb(struct cflayer *layr, struct cfpkt *pkt)
skb = cfpkt_tonative(pkt);
if (unlikely(cf_sk->sk.sk_state != CAIF_CONNECTED)) {
- cfpkt_destroy(pkt);
+ kfree_skb(skb);
return 0;
}
caif_queue_rcv_skb(&cf_sk->sk, skb);
return 0;
}
+static void cfsk_hold(struct cflayer *layr)
+{
+ struct caifsock *cf_sk = container_of(layr, struct caifsock, layer);
+ sock_hold(&cf_sk->sk);
+}
+
+static void cfsk_put(struct cflayer *layr)
+{
+ struct caifsock *cf_sk = container_of(layr, struct caifsock, layer);
+ sock_put(&cf_sk->sk);
+}
+
/* Packet Control Callback function called from CAIF */
static void caif_ctrl_cb(struct cflayer *layr,
- enum caif_ctrlcmd flow,
- int phyid)
+ enum caif_ctrlcmd flow,
+ int phyid)
{
struct caifsock *cf_sk = container_of(layr, struct caifsock, layer);
switch (flow) {
case CAIF_CTRLCMD_FLOW_ON_IND:
/* OK from modem to start sending again */
- dbfs_atomic_inc(&cnt.num_tx_flow_on_ind);
set_tx_flow_on(cf_sk);
cf_sk->sk.sk_state_change(&cf_sk->sk);
break;
case CAIF_CTRLCMD_FLOW_OFF_IND:
/* Modem asks us to shut up */
- dbfs_atomic_inc(&cnt.num_tx_flow_off_ind);
set_tx_flow_off(cf_sk);
cf_sk->sk.sk_state_change(&cf_sk->sk);
break;
case CAIF_CTRLCMD_INIT_RSP:
/* We're now connected */
- dbfs_atomic_inc(&cnt.num_connect_resp);
+ caif_client_register_refcnt(&cf_sk->layer,
+ cfsk_hold, cfsk_put);
cf_sk->sk.sk_state = CAIF_CONNECTED;
set_tx_flow_on(cf_sk);
+ cf_sk->sk.sk_shutdown = 0;
cf_sk->sk.sk_state_change(&cf_sk->sk);
break;
@@ -242,12 +226,10 @@ static void caif_ctrl_cb(struct cflayer *layr,
/* We're now disconnected */
cf_sk->sk.sk_state = CAIF_DISCONNECTED;
cf_sk->sk.sk_state_change(&cf_sk->sk);
- cfcnfg_release_adap_layer(&cf_sk->layer);
break;
case CAIF_CTRLCMD_INIT_FAIL_RSP:
/* Connect request failed */
- dbfs_atomic_inc(&cnt.num_connect_fail_resp);
cf_sk->sk.sk_err = ECONNREFUSED;
cf_sk->sk.sk_state = CAIF_DISCONNECTED;
cf_sk->sk.sk_shutdown = SHUTDOWN_MASK;
@@ -261,7 +243,6 @@ static void caif_ctrl_cb(struct cflayer *layr,
case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND:
/* Modem has closed this connection, or device is down. */
- dbfs_atomic_inc(&cnt.num_remote_shutdown_ind);
cf_sk->sk.sk_shutdown = SHUTDOWN_MASK;
cf_sk->sk.sk_err = ECONNRESET;
set_rx_flow_on(cf_sk);
@@ -281,7 +262,6 @@ static void caif_check_flow_release(struct sock *sk)
return;
if (atomic_read(&sk->sk_rmem_alloc) <= sk_rcvbuf_lowwater(cf_sk)) {
- dbfs_atomic_inc(&cnt.num_rx_flow_on);
set_rx_flow_on(cf_sk);
caif_flow_ctrl(sk, CAIF_MODEMCMD_FLOW_ON_REQ);
}
@@ -292,7 +272,7 @@ static void caif_check_flow_release(struct sock *sk)
* changed locking, address handling and added MSG_TRUNC.
*/
static int caif_seqpkt_recvmsg(struct kiocb *iocb, struct socket *sock,
- struct msghdr *m, size_t len, int flags)
+ struct msghdr *m, size_t len, int flags)
{
struct sock *sk = sock->sk;
@@ -364,8 +344,8 @@ static long caif_stream_data_wait(struct sock *sk, long timeo)
* changed locking calls, changed address handling.
*/
static int caif_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
- struct msghdr *msg, size_t size,
- int flags)
+ struct msghdr *msg, size_t size,
+ int flags)
{
struct sock *sk = sock->sk;
int copied = 0;
@@ -377,8 +357,6 @@ static int caif_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
if (flags&MSG_OOB)
goto out;
- msg->msg_namelen = 0;
-
/*
* Lock the socket to prevent queue disordering
* while sleeps in memcpy_tomsg
@@ -480,7 +458,7 @@ out:
* CAIF flow-on and sock_writable.
*/
static long caif_wait_for_flow_on(struct caifsock *cf_sk,
- int wait_writeable, long timeo, int *err)
+ int wait_writeable, long timeo, int *err)
{
struct sock *sk = &cf_sk->sk;
DEFINE_WAIT(wait);
@@ -519,48 +497,22 @@ static int transmit_skb(struct sk_buff *skb, struct caifsock *cf_sk,
int noblock, long timeo)
{
struct cfpkt *pkt;
- int ret, loopcnt = 0;
pkt = cfpkt_fromnative(CAIF_DIR_OUT, skb);
- memset(cfpkt_info(pkt), 0, sizeof(struct caif_payload_info));
- do {
+ memset(skb->cb, 0, sizeof(struct caif_payload_info));
+ cfpkt_set_prio(pkt, cf_sk->sk.sk_priority);
- ret = -ETIMEDOUT;
-
- /* Slight paranoia, probably not needed. */
- if (unlikely(loopcnt++ > 1000)) {
- pr_warn("transmit retries failed, error = %d\n", ret);
- break;
- }
+ if (cf_sk->layer.dn == NULL) {
+ kfree_skb(skb);
+ return -EINVAL;
+ }
- if (cf_sk->layer.dn != NULL)
- ret = cf_sk->layer.dn->transmit(cf_sk->layer.dn, pkt);
- if (likely(ret >= 0))
- break;
- /* if transmit return -EAGAIN, then retry */
- if (noblock && ret == -EAGAIN)
- break;
- timeo = caif_wait_for_flow_on(cf_sk, 0, timeo, &ret);
- if (signal_pending(current)) {
- ret = sock_intr_errno(timeo);
- break;
- }
- if (ret)
- break;
- if (cf_sk->sk.sk_state != CAIF_CONNECTED ||
- sock_flag(&cf_sk->sk, SOCK_DEAD) ||
- (cf_sk->sk.sk_shutdown & RCV_SHUTDOWN)) {
- ret = -EPIPE;
- cf_sk->sk.sk_err = EPIPE;
- break;
- }
- } while (ret == -EAGAIN);
- return ret;
+ return cf_sk->layer.dn->transmit(cf_sk->layer.dn, pkt);
}
/* Copied from af_unix:unix_dgram_sendmsg, and adapted to CAIF */
static int caif_seqpkt_sendmsg(struct kiocb *kiocb, struct socket *sock,
- struct msghdr *msg, size_t len)
+ struct msghdr *msg, size_t len)
{
struct sock *sk = sock->sk;
struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
@@ -620,7 +572,9 @@ static int caif_seqpkt_sendmsg(struct kiocb *kiocb, struct socket *sock,
goto err;
ret = transmit_skb(skb, cf_sk, noblock, timeo);
if (ret < 0)
- goto err;
+ /* skb is already freed */
+ return ret;
+
return len;
err:
kfree_skb(skb);
@@ -633,7 +587,7 @@ err:
* and other minor adaptations.
*/
static int caif_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
- struct msghdr *msg, size_t len)
+ struct msghdr *msg, size_t len)
{
struct sock *sk = sock->sk;
struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
@@ -694,10 +648,10 @@ static int caif_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
}
err = transmit_skb(skb, cf_sk,
msg->msg_flags&MSG_DONTWAIT, timeo);
- if (err < 0) {
- kfree_skb(skb);
+ if (err < 0)
+ /* skb is already freed */
goto pipe_err;
- }
+
sent += size;
}
@@ -712,7 +666,7 @@ out_err:
}
static int setsockopt(struct socket *sock,
- int lvl, int opt, char __user *ov, unsigned int ol)
+ 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);
@@ -740,12 +694,12 @@ static int setsockopt(struct socket *sock,
if (cf_sk->sk.sk_protocol != CAIFPROTO_UTIL)
return -ENOPROTOOPT;
lock_sock(&(cf_sk->sk));
- cf_sk->conn_req.param.size = ol;
if (ol > sizeof(cf_sk->conn_req.param.data) ||
copy_from_user(&cf_sk->conn_req.param.data, ov, ol)) {
release_sock(&cf_sk->sk);
return -EINVAL;
}
+ cf_sk->conn_req.param.size = ol;
release_sock(&cf_sk->sk);
return 0;
@@ -826,7 +780,8 @@ static int caif_connect(struct socket *sock, struct sockaddr *uaddr,
sk->sk_state == CAIF_DISCONNECTED);
if (sk->sk_shutdown & SHUTDOWN_MASK) {
/* Allow re-connect after SHUTDOWN_IND */
- caif_disconnect_client(&cf_sk->layer);
+ caif_disconnect_client(sock_net(sk), &cf_sk->layer);
+ caif_free_client(&cf_sk->layer);
break;
}
/* No reconnect on a seqpacket socket */
@@ -864,10 +819,11 @@ static int caif_connect(struct socket *sock, struct sockaddr *uaddr,
/*ifindex = id of the interface.*/
cf_sk->conn_req.ifindex = cf_sk->sk.sk_bound_dev_if;
- dbfs_atomic_inc(&cnt.num_connect_req);
cf_sk->layer.receive = caif_sktrecv_cb;
- err = caif_connect_client(&cf_sk->conn_req,
+
+ err = caif_connect_client(sock_net(sk), &cf_sk->conn_req,
&cf_sk->layer, &ifindex, &headroom, &tailroom);
+
if (err < 0) {
cf_sk->sk.sk_socket->state = SS_UNCONNECTED;
cf_sk->sk.sk_state = CAIF_DISCONNECTED;
@@ -935,7 +891,6 @@ static int caif_release(struct socket *sock)
{
struct sock *sk = sock->sk;
struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
- int res = 0;
if (!sk)
return 0;
@@ -947,13 +902,12 @@ static int caif_release(struct socket *sock)
* caif_queue_rcv_skb checks SOCK_DEAD holding the queue lock,
* this ensures no packets when sock is dead.
*/
- spin_lock(&sk->sk_receive_queue.lock);
+ spin_lock_bh(&sk->sk_receive_queue.lock);
sock_set_flag(sk, SOCK_DEAD);
- spin_unlock(&sk->sk_receive_queue.lock);
+ spin_unlock_bh(&sk->sk_receive_queue.lock);
sock->sk = NULL;
- dbfs_atomic_inc(&cnt.num_disconnect);
-
+ WARN_ON(IS_ERR(cf_sk->debugfs_socket_dir));
if (cf_sk->debugfs_socket_dir != NULL)
debugfs_remove_recursive(cf_sk->debugfs_socket_dir);
@@ -961,24 +915,20 @@ static int caif_release(struct socket *sock)
sk->sk_state = CAIF_DISCONNECTED;
sk->sk_shutdown = SHUTDOWN_MASK;
- if (cf_sk->sk.sk_socket->state == SS_CONNECTED ||
- cf_sk->sk.sk_socket->state == SS_CONNECTING)
- res = caif_disconnect_client(&cf_sk->layer);
-
+ caif_disconnect_client(sock_net(sk), &cf_sk->layer);
cf_sk->sk.sk_socket->state = SS_DISCONNECTING;
wake_up_interruptible_poll(sk_sleep(sk), POLLERR|POLLHUP);
sock_orphan(sk);
- cf_sk->layer.dn = NULL;
sk_stream_kill_queues(&cf_sk->sk);
release_sock(sk);
sock_put(sk);
- return res;
+ return 0;
}
/* Copied from af_unix.c:unix_poll(), added CAIF tx_flow handling */
static unsigned int caif_poll(struct file *file,
- struct socket *sock, poll_table *wait)
+ struct socket *sock, poll_table *wait)
{
struct sock *sk = sock->sk;
unsigned int mask;
@@ -1060,15 +1010,15 @@ static void caif_sock_destructor(struct sock *sk)
caif_assert(sk_unhashed(sk));
caif_assert(!sk->sk_socket);
if (!sock_flag(sk, SOCK_DEAD)) {
- pr_info("Attempt to release alive CAIF socket: %p\n", sk);
+ pr_debug("Attempt to release alive CAIF socket: %p\n", sk);
return;
}
sk_stream_kill_queues(&cf_sk->sk);
- dbfs_atomic_dec(&cnt.caif_nr_socks);
+ caif_free_client(&cf_sk->layer);
}
static int caif_create(struct net *net, struct socket *sock, int protocol,
- int kern)
+ int kern)
{
struct sock *sk = NULL;
struct caifsock *cf_sk = NULL;
@@ -1108,6 +1058,18 @@ static int caif_create(struct net *net, struct socket *sock, int protocol,
/* Store the protocol */
sk->sk_protocol = (unsigned char) protocol;
+ /* Initialize default priority for well-known cases */
+ switch (protocol) {
+ case CAIFPROTO_AT:
+ sk->sk_priority = TC_PRIO_CONTROL;
+ break;
+ case CAIFPROTO_RFM:
+ sk->sk_priority = TC_PRIO_INTERACTIVE_BULK;
+ break;
+ default:
+ sk->sk_priority = TC_PRIO_BESTEFFORT;
+ }
+
/*
* Lock in order to try to stop someone from opening the socket
* too early.
@@ -1127,35 +1089,8 @@ static int caif_create(struct net *net, struct socket *sock, int protocol,
set_rx_flow_on(cf_sk);
/* Set default options on configuration */
- cf_sk->sk.sk_priority= CAIF_PRIO_NORMAL;
cf_sk->conn_req.link_selector = CAIF_LINK_LOW_LATENCY;
cf_sk->conn_req.protocol = protocol;
- /* Increase the number of sockets created. */
- dbfs_atomic_inc(&cnt.caif_nr_socks);
-#ifdef CONFIG_DEBUG_FS
- if (!IS_ERR(debugfsdir)) {
- /* Fill in some information concerning the misc socket. */
- snprintf(cf_sk->name, sizeof(cf_sk->name), "cfsk%d",
- atomic_read(&cnt.caif_nr_socks));
-
- cf_sk->debugfs_socket_dir =
- debugfs_create_dir(cf_sk->name, debugfsdir);
- debugfs_create_u32("sk_state", S_IRUSR | S_IWUSR,
- cf_sk->debugfs_socket_dir,
- (u32 *) &cf_sk->sk.sk_state);
- debugfs_create_u32("flow_state", S_IRUSR | S_IWUSR,
- cf_sk->debugfs_socket_dir, &cf_sk->flow_state);
- debugfs_create_u32("sk_rmem_alloc", S_IRUSR | S_IWUSR,
- cf_sk->debugfs_socket_dir,
- (u32 *) &cf_sk->sk.sk_rmem_alloc);
- debugfs_create_u32("sk_wmem_alloc", S_IRUSR | S_IWUSR,
- cf_sk->debugfs_socket_dir,
- (u32 *) &cf_sk->sk.sk_wmem_alloc);
- debugfs_create_u32("identity", S_IRUSR | S_IWUSR,
- cf_sk->debugfs_socket_dir,
- (u32 *) &cf_sk->layer.id);
- }
-#endif
release_sock(&cf_sk->sk);
return 0;
}
@@ -1167,7 +1102,7 @@ static struct net_proto_family caif_family_ops = {
.owner = THIS_MODULE,
};
-static int af_caif_init(void)
+static int __init caif_sktinit_module(void)
{
int err = sock_register(&caif_family_ops);
if (!err)
@@ -1175,51 +1110,9 @@ static int af_caif_init(void)
return 0;
}
-static int __init caif_sktinit_module(void)
-{
-#ifdef CONFIG_DEBUG_FS
- debugfsdir = debugfs_create_dir("caif_sk", NULL);
- if (!IS_ERR(debugfsdir)) {
- debugfs_create_u32("num_sockets", S_IRUSR | S_IWUSR,
- debugfsdir,
- (u32 *) &cnt.caif_nr_socks);
- debugfs_create_u32("num_connect_req", S_IRUSR | S_IWUSR,
- debugfsdir,
- (u32 *) &cnt.num_connect_req);
- debugfs_create_u32("num_connect_resp", S_IRUSR | S_IWUSR,
- debugfsdir,
- (u32 *) &cnt.num_connect_resp);
- debugfs_create_u32("num_connect_fail_resp", S_IRUSR | S_IWUSR,
- debugfsdir,
- (u32 *) &cnt.num_connect_fail_resp);
- debugfs_create_u32("num_disconnect", S_IRUSR | S_IWUSR,
- debugfsdir,
- (u32 *) &cnt.num_disconnect);
- 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
- return af_caif_init();
-}
-
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);
diff --git a/net/caif/caif_usb.c b/net/caif/caif_usb.c
new file mode 100644
index 00000000000..ba02db02290
--- /dev/null
+++ b/net/caif/caif_usb.c
@@ -0,0 +1,204 @@
+/*
+ * CAIF USB handler
+ * Copyright (C) ST-Ericsson AB 2011
+ * Author: Sjur Brendeland
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/slab.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/usb/usbnet.h>
+#include <linux/etherdevice.h>
+#include <net/netns/generic.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 CFUSB_PAD_DESCR_SZ 1 /* Alignment descriptor length */
+#define CFUSB_ALIGNMENT 4 /* Number of bytes to align. */
+#define CFUSB_MAX_HEADLEN (CFUSB_PAD_DESCR_SZ + CFUSB_ALIGNMENT-1)
+#define STE_USB_VID 0x04cc /* USB Product ID for ST-Ericsson */
+#define STE_USB_PID_CAIF 0x230f /* Product id for CAIF Modems */
+
+struct cfusbl {
+ struct cflayer layer;
+ u8 tx_eth_hdr[ETH_HLEN];
+};
+
+static bool pack_added;
+
+static int cfusbl_receive(struct cflayer *layr, struct cfpkt *pkt)
+{
+ u8 hpad;
+
+ /* Remove padding. */
+ cfpkt_extr_head(pkt, &hpad, 1);
+ cfpkt_extr_head(pkt, NULL, hpad);
+ return layr->up->receive(layr->up, pkt);
+}
+
+static int cfusbl_transmit(struct cflayer *layr, struct cfpkt *pkt)
+{
+ struct caif_payload_info *info;
+ u8 hpad;
+ u8 zeros[CFUSB_ALIGNMENT];
+ struct sk_buff *skb;
+ struct cfusbl *usbl = container_of(layr, struct cfusbl, layer);
+
+ skb = cfpkt_tonative(pkt);
+
+ skb_reset_network_header(skb);
+ skb->protocol = htons(ETH_P_IP);
+
+ info = cfpkt_info(pkt);
+ hpad = (info->hdr_len + CFUSB_PAD_DESCR_SZ) & (CFUSB_ALIGNMENT - 1);
+
+ if (skb_headroom(skb) < ETH_HLEN + CFUSB_PAD_DESCR_SZ + hpad) {
+ pr_warn("Headroom to small\n");
+ kfree_skb(skb);
+ return -EIO;
+ }
+ memset(zeros, 0, hpad);
+
+ cfpkt_add_head(pkt, zeros, hpad);
+ cfpkt_add_head(pkt, &hpad, 1);
+ cfpkt_add_head(pkt, usbl->tx_eth_hdr, sizeof(usbl->tx_eth_hdr));
+ return layr->dn->transmit(layr->dn, pkt);
+}
+
+static void cfusbl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
+ int phyid)
+{
+ if (layr->up && layr->up->ctrlcmd)
+ layr->up->ctrlcmd(layr->up, ctrl, layr->id);
+}
+
+static struct cflayer *cfusbl_create(int phyid, u8 ethaddr[ETH_ALEN],
+ u8 braddr[ETH_ALEN])
+{
+ struct cfusbl *this = kmalloc(sizeof(struct cfusbl), GFP_ATOMIC);
+
+ if (!this) {
+ pr_warn("Out of memory\n");
+ return NULL;
+ }
+ caif_assert(offsetof(struct cfusbl, layer) == 0);
+
+ memset(this, 0, sizeof(struct cflayer));
+ this->layer.receive = cfusbl_receive;
+ this->layer.transmit = cfusbl_transmit;
+ this->layer.ctrlcmd = cfusbl_ctrlcmd;
+ snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "usb%d", phyid);
+ this->layer.id = phyid;
+
+ /*
+ * Construct TX ethernet header:
+ * 0-5 destination address
+ * 5-11 source address
+ * 12-13 protocol type
+ */
+ ether_addr_copy(&this->tx_eth_hdr[ETH_ALEN], braddr);
+ ether_addr_copy(&this->tx_eth_hdr[ETH_ALEN], ethaddr);
+ this->tx_eth_hdr[12] = cpu_to_be16(ETH_P_802_EX1) & 0xff;
+ this->tx_eth_hdr[13] = (cpu_to_be16(ETH_P_802_EX1) >> 8) & 0xff;
+ pr_debug("caif ethernet TX-header dst:%pM src:%pM type:%02x%02x\n",
+ this->tx_eth_hdr, this->tx_eth_hdr + ETH_ALEN,
+ this->tx_eth_hdr[12], this->tx_eth_hdr[13]);
+
+ return (struct cflayer *) this;
+}
+
+static struct packet_type caif_usb_type __read_mostly = {
+ .type = cpu_to_be16(ETH_P_802_EX1),
+};
+
+static int cfusbl_device_notify(struct notifier_block *me, unsigned long what,
+ void *ptr)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ struct caif_dev_common common;
+ struct cflayer *layer, *link_support;
+ struct usbnet *usbnet;
+ struct usb_device *usbdev;
+
+ /* Check whether we have a NCM device, and find its VID/PID. */
+ if (!(dev->dev.parent && dev->dev.parent->driver &&
+ strcmp(dev->dev.parent->driver->name, "cdc_ncm") == 0))
+ return 0;
+
+ usbnet = netdev_priv(dev);
+ usbdev = usbnet->udev;
+
+ pr_debug("USB CDC NCM device VID:0x%4x PID:0x%4x\n",
+ le16_to_cpu(usbdev->descriptor.idVendor),
+ le16_to_cpu(usbdev->descriptor.idProduct));
+
+ /* Check for VID/PID that supports CAIF */
+ if (!(le16_to_cpu(usbdev->descriptor.idVendor) == STE_USB_VID &&
+ le16_to_cpu(usbdev->descriptor.idProduct) == STE_USB_PID_CAIF))
+ return 0;
+
+ if (what == NETDEV_UNREGISTER)
+ module_put(THIS_MODULE);
+
+ if (what != NETDEV_REGISTER)
+ return 0;
+
+ __module_get(THIS_MODULE);
+
+ memset(&common, 0, sizeof(common));
+ common.use_frag = false;
+ common.use_fcs = false;
+ common.use_stx = false;
+ common.link_select = CAIF_LINK_HIGH_BANDW;
+ common.flowctrl = NULL;
+
+ link_support = cfusbl_create(dev->ifindex, dev->dev_addr,
+ dev->broadcast);
+
+ if (!link_support)
+ return -ENOMEM;
+
+ if (dev->num_tx_queues > 1)
+ pr_warn("USB device uses more than one tx queue\n");
+
+ caif_enroll_dev(dev, &common, link_support, CFUSB_MAX_HEADLEN,
+ &layer, &caif_usb_type.func);
+ if (!pack_added)
+ dev_add_pack(&caif_usb_type);
+ pack_added = true;
+
+ strncpy(layer->name, dev->name,
+ sizeof(layer->name) - 1);
+ layer->name[sizeof(layer->name) - 1] = 0;
+
+ return 0;
+}
+
+static struct notifier_block caif_device_notifier = {
+ .notifier_call = cfusbl_device_notify,
+ .priority = 0,
+};
+
+static int __init cfusbl_init(void)
+{
+ return register_netdevice_notifier(&caif_device_notifier);
+}
+
+static void __exit cfusbl_exit(void)
+{
+ unregister_netdevice_notifier(&caif_device_notifier);
+ dev_remove_pack(&caif_usb_type);
+}
+
+module_init(cfusbl_init);
+module_exit(cfusbl_exit);
diff --git a/net/caif/cfcnfg.c b/net/caif/cfcnfg.c
index 21ede141018..fa39fc29870 100644
--- a/net/caif/cfcnfg.c
+++ b/net/caif/cfcnfg.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*/
@@ -10,6 +10,7 @@
#include <linux/stddef.h>
#include <linux/slab.h>
#include <linux/netdevice.h>
+#include <linux/module.h>
#include <net/caif/caif_layer.h>
#include <net/caif/cfpkt.h>
#include <net/caif/cfcnfg.h>
@@ -18,20 +19,17 @@
#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
+#include <net/caif/caif_dev.h>
#define container_obj(layr) container_of(layr, struct cfcnfg, layer)
-#define RFM_FRAGMENT_SIZE 4030
/* Information about CAIF physical interfaces held by Config Module in order
* to manage physical interfaces
*/
struct cfcnfg_phyinfo {
+ struct list_head node;
+ bool up;
+
/* Pointer to the layer below the MUX (framing layer) */
struct cflayer *frm_layer;
/* Pointer to the lowest actual physical layer */
@@ -41,17 +39,14 @@ struct cfcnfg_phyinfo {
/* 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;
/* Interface index */
int ifindex;
- /* Use Start of frame extension */
- bool use_stx;
+ /* Protocol head room added for CAIF link layer */
+ int head_room;
/* Use Start of frame checksum */
bool use_fcs;
@@ -61,16 +56,16 @@ struct cfcnfg {
struct cflayer layer;
struct cflayer *ctrl;
struct cflayer *mux;
- u8 last_phyid;
- struct cfcnfg_phyinfo phy_layers[MAX_PHY_LAYERS];
+ struct list_head phys;
+ struct mutex lock;
};
static void cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id,
- enum cfctrl_srv serv, u8 phyid,
- struct cflayer *adapt_layer);
+ enum cfctrl_srv serv, u8 phyid,
+ struct cflayer *adapt_layer);
static void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id);
static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id,
- struct cflayer *adapt_layer);
+ struct cflayer *adapt_layer);
static void cfctrl_resp_func(void);
static void cfctrl_enum_resp(void);
@@ -78,12 +73,13 @@ struct cfcnfg *cfcnfg_create(void)
{
struct cfcnfg *this;
struct cfctrl_rsp *resp;
+
+ might_sleep();
+
/* Initiate this layer */
this = kzalloc(sizeof(struct cfcnfg), GFP_ATOMIC);
- if (!this) {
- pr_warn("Out of memory\n");
+ if (!this)
return NULL;
- }
this->mux = cfmuxl_create();
if (!this->mux)
goto out_of_mem;
@@ -101,27 +97,31 @@ struct cfcnfg *cfcnfg_create(void)
resp->radioset_rsp = cfctrl_resp_func;
resp->linksetup_rsp = cfcnfg_linkup_rsp;
resp->reject_rsp = cfcnfg_reject_rsp;
-
- this->last_phyid = 1;
+ INIT_LIST_HEAD(&this->phys);
cfmuxl_set_uplayer(this->mux, this->ctrl, 0);
layer_set_dn(this->ctrl, this->mux);
layer_set_up(this->ctrl, this);
+ mutex_init(&this->lock);
+
return this;
out_of_mem:
- pr_warn("Out of memory\n");
+ synchronize_rcu();
+
kfree(this->mux);
kfree(this->ctrl);
kfree(this);
return NULL;
}
-EXPORT_SYMBOL(cfcnfg_create);
void cfcnfg_remove(struct cfcnfg *cfg)
{
+ might_sleep();
if (cfg) {
+ synchronize_rcu();
+
kfree(cfg->mux);
- kfree(cfg->ctrl);
+ cfctrl_remove(cfg->ctrl);
kfree(cfg);
}
}
@@ -130,131 +130,83 @@ static void cfctrl_resp_func(void)
{
}
+static struct cfcnfg_phyinfo *cfcnfg_get_phyinfo_rcu(struct cfcnfg *cnfg,
+ u8 phyid)
+{
+ struct cfcnfg_phyinfo *phy;
+
+ list_for_each_entry_rcu(phy, &cnfg->phys, node)
+ if (phy->id == phyid)
+ return phy;
+ return NULL;
+}
+
static void cfctrl_enum_resp(void)
{
}
-struct dev_info *cfcnfg_get_phyid(struct cfcnfg *cnfg,
+static struct dev_info *cfcnfg_get_phyid(struct cfcnfg *cnfg,
enum cfcnfg_phy_preference phy_pref)
{
- u16 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;
- }
+ struct cfcnfg_phyinfo *phy;
+
+ list_for_each_entry_rcu(phy, &cnfg->phys, node) {
+ if (phy->up && phy->pref == phy_pref &&
+ phy->frm_layer != NULL)
+
+ return &phy->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;
- }
- }
+ list_for_each_entry_rcu(phy, &cnfg->phys, node)
+ if (phy->up)
+ return &phy->dev_info;
return NULL;
}
-static struct cfcnfg_phyinfo *cfcnfg_get_phyinfo(struct cfcnfg *cnfg,
- u8 phyid)
+static int cfcnfg_get_id_from_ifi(struct cfcnfg *cnfg, int ifi)
{
- 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 NULL;
-}
+ struct cfcnfg_phyinfo *phy;
-
-int cfcnfg_get_id_from_ifi(struct cfcnfg *cnfg, int ifi)
-{
- int i;
- for (i = 0; i < MAX_PHY_LAYERS; i++)
- if (cnfg->phy_layers[i].frm_layer != NULL &&
- cnfg->phy_layers[i].ifindex == ifi)
- return i;
+ list_for_each_entry_rcu(phy, &cnfg->phys, node)
+ if (phy->ifindex == ifi && phy->up)
+ return phy->id;
return -ENODEV;
}
-int cfcnfg_disconn_adapt_layer(struct cfcnfg *cnfg, struct cflayer *adap_layer)
+int caif_disconnect_client(struct net *net, struct cflayer *adap_layer)
{
- u8 channel_id = 0;
- int ret = 0;
- struct cflayer *servl = NULL;
- struct cfcnfg_phyinfo *phyinfo = NULL;
- u8 phyid = 0;
+ u8 channel_id;
+ struct cfcnfg *cfg = get_cfcnfg(net);
+
caif_assert(adap_layer != NULL);
+ cfctrl_cancel_req(cfg->ctrl, adap_layer);
channel_id = adap_layer->id;
- if (adap_layer->dn == NULL || channel_id == 0) {
- pr_err("adap_layer->dn == NULL or adap_layer->id is 0\n");
- ret = -ENOTCONN;
- goto end;
- }
- servl = cfmuxl_remove_uplayer(cnfg->mux, channel_id);
- if (servl == NULL)
- goto end;
- layer_set_up(servl, NULL);
- ret = cfctrl_linkdown_req(cnfg->ctrl, channel_id, adap_layer);
- if (servl == NULL) {
- pr_err("PROTOCOL ERROR - Error removing service_layer Channel_Id(%d)",
- channel_id);
- ret = -EINVAL;
- goto end;
- }
- caif_assert(channel_id == servl->id);
- if (adap_layer->dn != NULL) {
- phyid = cfsrvl_getphyid(adap_layer->dn);
-
- phyinfo = cfcnfg_get_phyinfo(cnfg, phyid);
- if (phyinfo == NULL) {
- pr_warn("No interface to send disconnect to\n");
- ret = -ENODEV;
- goto end;
- }
- if (phyinfo->id != phyid ||
- phyinfo->phy_layer->id != phyid ||
- phyinfo->frm_layer->id != phyid) {
- pr_err("Inconsistency in phy registration\n");
- ret = -EINVAL;
- goto 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);
- }
-end:
- cfsrvl_put(servl);
- cfctrl_cancel_req(cnfg->ctrl, adap_layer);
+ if (channel_id != 0) {
+ struct cflayer *servl;
+ servl = cfmuxl_remove_uplayer(cfg->mux, channel_id);
+ cfctrl_linkdown_req(cfg->ctrl, channel_id, adap_layer);
+ if (servl != NULL)
+ layer_set_up(servl, NULL);
+ } else
+ pr_debug("nothing to disconnect\n");
+
+ /* Do RCU sync before initiating cleanup */
+ synchronize_rcu();
if (adap_layer->ctrlcmd != NULL)
adap_layer->ctrlcmd(adap_layer, CAIF_CTRLCMD_DEINIT_RSP, 0);
- return ret;
-
-}
-EXPORT_SYMBOL(cfcnfg_disconn_adapt_layer);
+ return 0;
-void cfcnfg_release_adap_layer(struct cflayer *adap_layer)
-{
- if (adap_layer->dn)
- cfsrvl_put(adap_layer->dn);
}
-EXPORT_SYMBOL(cfcnfg_release_adap_layer);
+EXPORT_SYMBOL(caif_disconnect_client);
static void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id)
{
}
-int protohead[CFCTRL_SRV_MASK] = {
+static const int protohead[CFCTRL_SRV_MASK] = {
[CFCTRL_SRV_VEI] = 4,
[CFCTRL_SRV_DATAGRAM] = 7,
[CFCTRL_SRV_UTIL] = 4,
@@ -262,52 +214,156 @@ int protohead[CFCTRL_SRV_MASK] = {
[CFCTRL_SRV_DBG] = 3,
};
-int cfcnfg_add_adaptation_layer(struct cfcnfg *cnfg,
- struct cfctrl_link_param *param,
- struct cflayer *adap_layer,
- int *ifindex,
- int *proto_head,
- int *proto_tail)
+
+static int caif_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;
+ int res;
+
+ memset(l, 0, sizeof(*l));
+ /* In caif protocol low value is high priority */
+ l->priority = CAIF_PRIO_MAX - s->priority + 1;
+
+ if (s->ifindex != 0) {
+ res = cfcnfg_get_id_from_ifi(cnfg, s->ifindex);
+ if (res < 0)
+ return res;
+ l->phyid = res;
+ } 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;
+ l->endpoint = (s->sockaddr.u.at.type >> 2) & 0x3;
+ l->chtype = s->sockaddr.u.at.type & 0x3;
+ 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;
+ case CAIFPROTO_DEBUG:
+ l->linktype = CFCTRL_SRV_DBG;
+ l->endpoint = s->sockaddr.u.dbg.service;
+ l->chtype = s->sockaddr.u.dbg.type;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int caif_connect_client(struct net *net, struct caif_connect_request *conn_req,
+ struct cflayer *adap_layer, int *ifindex,
+ int *proto_head, int *proto_tail)
{
struct cflayer *frml;
+ struct cfcnfg_phyinfo *phy;
+ int err;
+ struct cfctrl_link_param param;
+ struct cfcnfg *cfg = get_cfcnfg(net);
+
+ rcu_read_lock();
+ err = caif_connect_req_to_link_param(cfg, conn_req, &param);
+ if (err)
+ goto unlock;
+
+ phy = cfcnfg_get_phyinfo_rcu(cfg, param.phyid);
+ if (!phy) {
+ err = -ENODEV;
+ goto unlock;
+ }
+ err = -EINVAL;
+
if (adap_layer == NULL) {
pr_err("adap_layer is zero\n");
- return -EINVAL;
+ goto unlock;
}
if (adap_layer->receive == NULL) {
pr_err("adap_layer->receive is NULL\n");
- return -EINVAL;
+ goto unlock;
}
if (adap_layer->ctrlcmd == NULL) {
pr_err("adap_layer->ctrlcmd == NULL\n");
- return -EINVAL;
+ goto unlock;
}
- frml = cnfg->phy_layers[param->phyid].frm_layer;
+
+ err = -ENODEV;
+ frml = phy->frm_layer;
if (frml == NULL) {
pr_err("Specified PHY type does not exist!\n");
- return -ENODEV;
+ goto unlock;
}
- 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);
-
- *ifindex = cnfg->phy_layers[param->phyid].ifindex;
- *proto_head =
- protohead[param->linktype]+
- (cnfg->phy_layers[param->phyid].use_stx ? 1 : 0);
+ caif_assert(param.phyid == phy->id);
+ caif_assert(phy->frm_layer->id ==
+ param.phyid);
+ caif_assert(phy->phy_layer->id ==
+ param.phyid);
+ *ifindex = phy->ifindex;
*proto_tail = 2;
+ *proto_head = protohead[param.linktype] + phy->head_room;
+
+ rcu_read_unlock();
/* FIXME: ENUMERATE INITIALLY WHEN ACTIVATING PHYSICAL INTERFACE */
- cfctrl_enum_req(cnfg->ctrl, param->phyid);
- return cfctrl_linkup_request(cnfg->ctrl, param, adap_layer);
+ cfctrl_enum_req(cfg->ctrl, param.phyid);
+ return cfctrl_linkup_request(cfg->ctrl, &param, adap_layer);
+
+unlock:
+ rcu_read_unlock();
+ return err;
}
-EXPORT_SYMBOL(cfcnfg_add_adaptation_layer);
+EXPORT_SYMBOL(caif_connect_client);
static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id,
- struct cflayer *adapt_layer)
+ struct cflayer *adapt_layer)
{
if (adapt_layer != NULL && adapt_layer->ctrlcmd != NULL)
adapt_layer->ctrlcmd(adapt_layer,
@@ -316,32 +372,45 @@ static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id,
static void
cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv,
- u8 phyid, struct cflayer *adapt_layer)
+ u8 phyid, struct cflayer *adapt_layer)
{
struct cfcnfg *cnfg = container_obj(layer);
struct cflayer *servicel = NULL;
struct cfcnfg_phyinfo *phyinfo;
struct net_device *netdev;
+ if (channel_id == 0) {
+ pr_warn("received channel_id zero\n");
+ if (adapt_layer != NULL && adapt_layer->ctrlcmd != NULL)
+ adapt_layer->ctrlcmd(adapt_layer,
+ CAIF_CTRLCMD_INIT_FAIL_RSP, 0);
+ return;
+ }
+
+ rcu_read_lock();
+
if (adapt_layer == NULL) {
- pr_debug("link setup response but no client exist, send linkdown back\n");
+ pr_debug("link setup response but no client exist,"
+ "send linkdown back\n");
cfctrl_linkdown_req(cnfg->ctrl, channel_id, NULL);
- return;
+ goto unlock;
}
caif_assert(cnfg != NULL);
caif_assert(phyid != 0);
- phyinfo = &cnfg->phy_layers[phyid];
+
+ phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phyid);
+ if (phyinfo == NULL) {
+ pr_err("ERROR: Link Layer Device disappeared"
+ "while connecting\n");
+ goto unlock;
+ }
+
+ caif_assert(phyinfo != NULL);
caif_assert(phyinfo->id == phyid);
caif_assert(phyinfo->phy_layer != NULL);
caif_assert(phyinfo->phy_layer->id == phyid);
- phyinfo->phy_ref_count++;
- if (phyinfo->phy_ref_count == 1 &&
- phyinfo->phy_layer->modemcmd != NULL) {
- phyinfo->phy_layer->modemcmd(phyinfo->phy_layer,
- _CAIF_MODEMCMD_PHYIF_USEFULL);
- }
adapt_layer->id = channel_id;
switch (serv) {
@@ -349,7 +418,8 @@ cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv,
servicel = cfvei_create(channel_id, &phyinfo->dev_info);
break;
case CFCTRL_SRV_DATAGRAM:
- servicel = cfdgml_create(channel_id, &phyinfo->dev_info);
+ servicel = cfdgml_create(channel_id,
+ &phyinfo->dev_info);
break;
case CFCTRL_SRV_RFM:
netdev = phyinfo->dev_info.dev;
@@ -366,129 +436,176 @@ cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv,
servicel = cfdbgl_create(channel_id, &phyinfo->dev_info);
break;
default:
- pr_err("Protocol error. Link setup response - unknown channel type\n");
- return;
- }
- if (!servicel) {
- pr_warn("Out of memory\n");
- return;
+ pr_err("Protocol error. Link setup response "
+ "- unknown channel type\n");
+ goto unlock;
}
+ if (!servicel)
+ goto unlock;
layer_set_dn(servicel, cnfg->mux);
cfmuxl_set_uplayer(cnfg->mux, servicel, channel_id);
layer_set_up(servicel, adapt_layer);
layer_set_dn(adapt_layer, servicel);
- cfsrvl_get(servicel);
+
+ rcu_read_unlock();
+
servicel->ctrlcmd(servicel, CAIF_CTRLCMD_INIT_RSP, 0);
+ return;
+unlock:
+ rcu_read_unlock();
}
void
-cfcnfg_add_phy_layer(struct cfcnfg *cnfg, enum cfcnfg_phy_type phy_type,
+cfcnfg_add_phy_layer(struct cfcnfg *cnfg,
struct net_device *dev, struct cflayer *phy_layer,
- u16 *phyid, enum cfcnfg_phy_preference pref,
- bool fcs, bool stx)
+ enum cfcnfg_phy_preference pref,
+ struct cflayer *link_support,
+ bool fcs, int head_room)
{
struct cflayer *frml;
- struct cflayer *phy_driver = NULL;
+ struct cfcnfg_phyinfo *phyinfo = NULL;
int i;
+ u8 phyid;
+ mutex_lock(&cnfg->lock);
- if (cnfg->phy_layers[cnfg->last_phyid].frm_layer == NULL) {
- *phyid = cnfg->last_phyid;
+ /* CAIF protocol allow maximum 6 link-layers */
+ for (i = 0; i < 7; i++) {
+ phyid = (dev->ifindex + i) & 0x7;
+ if (phyid == 0)
+ continue;
+ if (cfcnfg_get_phyinfo_rcu(cnfg, phyid) == NULL)
+ goto got_phyid;
+ }
+ pr_warn("Too many CAIF Link Layers (max 6)\n");
+ goto out;
+
+got_phyid:
+ phyinfo = kzalloc(sizeof(struct cfcnfg_phyinfo), GFP_ATOMIC);
+ if (!phyinfo)
+ goto out_err;
+
+ phy_layer->id = phyid;
+ phyinfo->pref = pref;
+ phyinfo->id = phyid;
+ phyinfo->dev_info.id = phyid;
+ phyinfo->dev_info.dev = dev;
+ phyinfo->phy_layer = phy_layer;
+ phyinfo->ifindex = dev->ifindex;
+ phyinfo->head_room = head_room;
+ phyinfo->use_fcs = fcs;
+
+ frml = cffrml_create(phyid, fcs);
+
+ if (!frml)
+ goto out_err;
+ phyinfo->frm_layer = frml;
+ layer_set_up(frml, cnfg->mux);
- /* range: * 1..(MAX_PHY_LAYERS-1) */
- cnfg->last_phyid =
- (cnfg->last_phyid % (MAX_PHY_LAYERS - 1)) + 1;
+ if (link_support != NULL) {
+ link_support->id = phyid;
+ layer_set_dn(frml, link_support);
+ layer_set_up(link_support, frml);
+ layer_set_dn(link_support, phy_layer);
+ layer_set_up(phy_layer, link_support);
} 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("No Available PHY ID\n");
- return;
+ layer_set_dn(frml, phy_layer);
+ layer_set_up(phy_layer, frml);
}
- switch (phy_type) {
- case CFPHYTYPE_FRAG:
- phy_driver =
- cfserl_create(CFPHYTYPE_FRAG, *phyid, stx);
- if (!phy_driver) {
- pr_warn("Out of memory\n");
- return;
- }
+ list_add_rcu(&phyinfo->node, &cnfg->phys);
+out:
+ mutex_unlock(&cnfg->lock);
+ return;
- break;
- case CFPHYTYPE_CAIF:
- phy_driver = NULL;
- break;
- default:
- pr_err("%d\n", phy_type);
- return;
- break;
+out_err:
+ kfree(phyinfo);
+ mutex_unlock(&cnfg->lock);
+}
+EXPORT_SYMBOL(cfcnfg_add_phy_layer);
+
+int cfcnfg_set_phy_state(struct cfcnfg *cnfg, struct cflayer *phy_layer,
+ bool up)
+{
+ struct cfcnfg_phyinfo *phyinfo;
+
+ rcu_read_lock();
+ phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phy_layer->id);
+ if (phyinfo == NULL) {
+ rcu_read_unlock();
+ return -ENODEV;
}
- 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;
- cnfg->phy_layers[*phyid].ifindex = dev->ifindex;
- cnfg->phy_layers[*phyid].use_stx = stx;
- cnfg->phy_layers[*phyid].use_fcs = fcs;
-
- phy_layer->type = phy_type;
- frml = cffrml_create(*phyid, fcs);
- if (!frml) {
- pr_warn("Out of memory\n");
- return;
+ if (phyinfo->up == up) {
+ rcu_read_unlock();
+ return 0;
}
- cnfg->phy_layers[*phyid].frm_layer = frml;
- cfmuxl_set_dnlayer(cnfg->mux, frml, *phyid);
- layer_set_up(frml, cnfg->mux);
+ phyinfo->up = up;
- 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);
+ if (up) {
+ cffrml_hold(phyinfo->frm_layer);
+ cfmuxl_set_dnlayer(cnfg->mux, phyinfo->frm_layer,
+ phy_layer->id);
} else {
- layer_set_dn(frml, phy_layer);
- layer_set_up(phy_layer, frml);
+ cfmuxl_remove_dnlayer(cnfg->mux, phy_layer->id);
+ cffrml_put(phyinfo->frm_layer);
}
+
+ rcu_read_unlock();
+ return 0;
}
-EXPORT_SYMBOL(cfcnfg_add_phy_layer);
+EXPORT_SYMBOL(cfcnfg_set_phy_state);
int cfcnfg_del_phy_layer(struct cfcnfg *cnfg, struct cflayer *phy_layer)
{
struct cflayer *frml, *frml_dn;
u16 phyid;
+ struct cfcnfg_phyinfo *phyinfo;
+
+ might_sleep();
+
+ mutex_lock(&cnfg->lock);
+
phyid = phy_layer->id;
- caif_assert(phyid == cnfg->phy_layers[phyid].id);
- caif_assert(phy_layer == cnfg->phy_layers[phyid].phy_layer);
+ phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phyid);
+
+ if (phyinfo == NULL) {
+ mutex_unlock(&cnfg->lock);
+ return 0;
+ }
+ caif_assert(phyid == phyinfo->id);
+ caif_assert(phy_layer == phyinfo->phy_layer);
caif_assert(phy_layer->id == phyid);
- caif_assert(cnfg->phy_layers[phyid].frm_layer->id == phyid);
+ caif_assert(phyinfo->frm_layer->id == phyid);
+
+ list_del_rcu(&phyinfo->node);
+ synchronize_rcu();
- memset(&cnfg->phy_layers[phy_layer->id], 0,
- sizeof(struct cfcnfg_phyinfo));
- frml = cfmuxl_remove_dnlayer(cnfg->mux, phy_layer->id);
+ /* Fail if reference count is not zero */
+ if (cffrml_refcnt_read(phyinfo->frm_layer) != 0) {
+ pr_info("Wait for device inuse\n");
+ list_add_rcu(&phyinfo->node, &cnfg->phys);
+ mutex_unlock(&cnfg->lock);
+ return -EAGAIN;
+ }
+
+ frml = phyinfo->frm_layer;
frml_dn = frml->dn;
cffrml_set_uplayer(frml, NULL);
cffrml_set_dnlayer(frml, NULL);
- kfree(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);
+
+ if (phyinfo->phy_layer != frml_dn)
+ kfree(frml_dn);
+
+ cffrml_free(frml);
+ kfree(phyinfo);
+ mutex_unlock(&cnfg->lock);
+
return 0;
}
EXPORT_SYMBOL(cfcnfg_del_phy_layer);
diff --git a/net/caif/cfctrl.c b/net/caif/cfctrl.c
index 3cd8f978e30..0f455227da8 100644
--- a/net/caif/cfctrl.c
+++ b/net/caif/cfctrl.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*/
@@ -9,6 +9,7 @@
#include <linux/stddef.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
+#include <linux/pkt_sched.h>
#include <net/caif/caif_layer.h>
#include <net/caif/cfpkt.h>
#include <net/caif/cfctrl.h>
@@ -17,15 +18,14 @@
#define UTILITY_NAME_LENGTH 16
#define CFPKT_CTRL_PKT_LEN 20
-
#ifdef CAIF_NO_LOOP
static int handle_loop(struct cfctrl *ctrl,
- int cmd, struct cfpkt *pkt){
+ int cmd, struct cfpkt *pkt){
return -1;
}
#else
static int handle_loop(struct cfctrl *ctrl,
- int cmd, struct cfpkt *pkt);
+ int cmd, struct cfpkt *pkt);
#endif
static int cfctrl_recv(struct cflayer *layr, struct cfpkt *pkt);
static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
@@ -36,29 +36,43 @@ struct cflayer *cfctrl_create(void)
{
struct dev_info dev_info;
struct cfctrl *this =
- kmalloc(sizeof(struct cfctrl), GFP_ATOMIC);
- if (!this) {
- pr_warn("Out of memory\n");
+ kzalloc(sizeof(struct cfctrl), GFP_ATOMIC);
+ if (!this)
return NULL;
- }
caif_assert(offsetof(struct cfctrl, serv.layer) == 0);
memset(&dev_info, 0, sizeof(dev_info));
dev_info.id = 0xff;
- memset(this, 0, sizeof(*this));
cfsrvl_init(&this->serv, 0, &dev_info, false);
atomic_set(&this->req_seq_no, 1);
atomic_set(&this->rsp_seq_no, 1);
this->serv.layer.receive = cfctrl_recv;
sprintf(this->serv.layer.name, "ctrl");
this->serv.layer.ctrlcmd = cfctrl_ctrlcmd;
+#ifndef CAIF_NO_LOOP
spin_lock_init(&this->loop_linkid_lock);
+ this->loop_linkid = 1;
+#endif
spin_lock_init(&this->info_list_lock);
INIT_LIST_HEAD(&this->list);
- this->loop_linkid = 1;
return &this->serv.layer;
}
-static bool param_eq(struct cfctrl_link_param *p1, struct cfctrl_link_param *p2)
+void cfctrl_remove(struct cflayer *layer)
+{
+ struct cfctrl_request_info *p, *tmp;
+ struct cfctrl *ctrl = container_obj(layer);
+
+ spin_lock_bh(&ctrl->info_list_lock);
+ list_for_each_entry_safe(p, tmp, &ctrl->list, list) {
+ list_del(&p->list);
+ kfree(p);
+ }
+ spin_unlock_bh(&ctrl->info_list_lock);
+ kfree(layer);
+}
+
+static bool param_eq(const struct cfctrl_link_param *p1,
+ const struct cfctrl_link_param *p2)
{
bool eq =
p1->linktype == p2->linktype &&
@@ -100,8 +114,8 @@ static bool param_eq(struct cfctrl_link_param *p1, struct cfctrl_link_param *p2)
return false;
}
-bool cfctrl_req_eq(struct cfctrl_request_info *r1,
- struct cfctrl_request_info *r2)
+static bool cfctrl_req_eq(const struct cfctrl_request_info *r1,
+ const struct cfctrl_request_info *r2)
{
if (r1->cmd != r2->cmd)
return false;
@@ -112,23 +126,22 @@ bool cfctrl_req_eq(struct cfctrl_request_info *r1,
}
/* Insert request at the end */
-void cfctrl_insert_req(struct cfctrl *ctrl,
+static void cfctrl_insert_req(struct cfctrl *ctrl,
struct cfctrl_request_info *req)
{
- spin_lock(&ctrl->info_list_lock);
+ spin_lock_bh(&ctrl->info_list_lock);
atomic_inc(&ctrl->req_seq_no);
req->sequence_no = atomic_read(&ctrl->req_seq_no);
list_add_tail(&req->list, &ctrl->list);
- spin_unlock(&ctrl->info_list_lock);
+ spin_unlock_bh(&ctrl->info_list_lock);
}
/* Compare and remove request */
-struct cfctrl_request_info *cfctrl_remove_req(struct cfctrl *ctrl,
- struct cfctrl_request_info *req)
+static struct cfctrl_request_info *cfctrl_remove_req(struct cfctrl *ctrl,
+ struct cfctrl_request_info *req)
{
struct cfctrl_request_info *p, *tmp, *first;
- spin_lock(&ctrl->info_list_lock);
first = list_first_entry(&ctrl->list, struct cfctrl_request_info, list);
list_for_each_entry_safe(p, tmp, &ctrl->list, list) {
@@ -144,7 +157,6 @@ struct cfctrl_request_info *cfctrl_remove_req(struct cfctrl *ctrl,
}
p = NULL;
out:
- spin_unlock(&ctrl->info_list_lock);
return p;
}
@@ -154,16 +166,6 @@ struct cfctrl_rsp *cfctrl_get_respfuncs(struct cflayer *layer)
return &this->res;
}
-void cfctrl_set_dnlayer(struct cflayer *this, struct cflayer *dn)
-{
- this->dn = dn;
-}
-
-void cfctrl_set_uplayer(struct cflayer *this, struct cflayer *up)
-{
- this->up = up;
-}
-
static void init_info(struct caif_payload_info *info, struct cfctrl *cfctrl)
{
info->hdr_len = 0;
@@ -173,30 +175,30 @@ static void init_info(struct caif_payload_info *info, struct cfctrl *cfctrl)
void cfctrl_enum_req(struct cflayer *layer, u8 physlinkid)
{
+ struct cfpkt *pkt;
struct cfctrl *cfctrl = container_obj(layer);
- int ret;
- struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
- if (!pkt) {
- pr_warn("Out of memory\n");
+ struct cflayer *dn = cfctrl->serv.layer.dn;
+
+ if (!dn) {
+ pr_debug("not able to send enum request\n");
return;
}
+ pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
+ if (!pkt)
+ 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("Could not transmit enum message\n");
- cfpkt_destroy(pkt);
- }
+ cfpkt_set_prio(pkt, TC_PRIO_CONTROL);
+ dn->transmit(dn, pkt);
}
int cfctrl_linkup_request(struct cflayer *layer,
- struct cfctrl_link_param *param,
- struct cflayer *user_layer)
+ struct cfctrl_link_param *param,
+ struct cflayer *user_layer)
{
struct cfctrl *cfctrl = container_obj(layer);
u32 tmp32;
@@ -205,14 +207,27 @@ int cfctrl_linkup_request(struct cflayer *layer,
struct cfctrl_request_info *req;
int ret;
char utility_name[16];
- struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
- if (!pkt) {
- pr_warn("Out of memory\n");
- return -ENOMEM;
+ struct cfpkt *pkt;
+ struct cflayer *dn = cfctrl->serv.layer.dn;
+
+ if (!dn) {
+ pr_debug("not able to send linkup request\n");
+ return -ENODEV;
}
+
+ if (cfctrl_cancel_req(layer, user_layer) > 0) {
+ /* Slight Paranoia, check if already connecting */
+ pr_err("Duplicate connect request for same client\n");
+ WARN_ON(1);
+ return -EALREADY;
+ }
+
+ pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
+ if (!pkt)
+ return -ENOMEM;
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->chtype << 4) | param->linktype);
+ cfpkt_addbdy(pkt, (param->priority << 3) | param->phyid);
cfpkt_addbdy(pkt, param->endpoint & 0x03);
switch (param->linktype) {
@@ -257,10 +272,8 @@ int cfctrl_linkup_request(struct cflayer *layer,
return -EINVAL;
}
req = kzalloc(sizeof(*req), GFP_KERNEL);
- if (!req) {
- pr_warn("Out of memory\n");
+ if (!req)
return -ENOMEM;
- }
req->client_layer = user_layer;
req->cmd = CFCTRL_CMD_LINK_SETUP;
req->param = *param;
@@ -272,105 +285,66 @@ int cfctrl_linkup_request(struct cflayer *layer,
* might arrive with the newly allocated channel ID.
*/
cfpkt_info(pkt)->dev_info->id = param->phyid;
+ cfpkt_set_prio(pkt, TC_PRIO_CONTROL);
ret =
- cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
+ dn->transmit(dn, pkt);
if (ret < 0) {
- pr_err("Could not transmit linksetup request\n");
- cfpkt_destroy(pkt);
- return -ENODEV;
+ int count;
+
+ count = cfctrl_cancel_req(&cfctrl->serv.layer,
+ user_layer);
+ if (count != 1) {
+ pr_err("Could not remove request (%d)", count);
+ return -ENODEV;
+ }
}
return 0;
}
int cfctrl_linkdown_req(struct cflayer *layer, u8 channelid,
- struct cflayer *client)
+ struct cflayer *client)
{
int ret;
+ struct cfpkt *pkt;
struct cfctrl *cfctrl = container_obj(layer);
- struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
- if (!pkt) {
- pr_warn("Out of memory\n");
- return -ENOMEM;
+ struct cflayer *dn = cfctrl->serv.layer.dn;
+
+ if (!dn) {
+ pr_debug("not able to send link-down request\n");
+ return -ENODEV;
}
+ pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
+ if (!pkt)
+ return -ENOMEM;
cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_DESTROY);
cfpkt_addbdy(pkt, channelid);
init_info(cfpkt_info(pkt), cfctrl);
+ cfpkt_set_prio(pkt, TC_PRIO_CONTROL);
ret =
- cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
- if (ret < 0) {
- pr_err("Could not transmit link-down request\n");
- cfpkt_destroy(pkt);
- }
+ dn->transmit(dn, pkt);
+#ifndef CAIF_NO_LOOP
+ cfctrl->loop_linkused[channelid] = 0;
+#endif
return ret;
}
-void cfctrl_sleep_req(struct cflayer *layer)
-{
- int ret;
- struct cfctrl *cfctrl = container_obj(layer);
- struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
- if (!pkt) {
- pr_warn("Out of memory\n");
- 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 cflayer *layer)
-{
- int ret;
- struct cfctrl *cfctrl = container_obj(layer);
- struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
- if (!pkt) {
- pr_warn("Out of memory\n");
- 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 cflayer *layer)
-{
- int ret;
- struct cfctrl *cfctrl = container_obj(layer);
- struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
- if (!pkt) {
- pr_warn("Out of memory\n");
- 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_cancel_req(struct cflayer *layr, struct cflayer *adap_layer)
+int cfctrl_cancel_req(struct cflayer *layr, struct cflayer *adap_layer)
{
struct cfctrl_request_info *p, *tmp;
struct cfctrl *ctrl = container_obj(layr);
- spin_lock(&ctrl->info_list_lock);
+ int found = 0;
+ spin_lock_bh(&ctrl->info_list_lock);
list_for_each_entry_safe(p, tmp, &ctrl->list, list) {
if (p->client_layer == adap_layer) {
- pr_debug("cancel req :%d\n", p->sequence_no);
list_del(&p->list);
kfree(p);
+ found++;
}
}
- spin_unlock(&ctrl->info_list_lock);
+ spin_unlock_bh(&ctrl->info_list_lock);
+ return found;
}
static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt)
@@ -389,7 +363,8 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt)
cfpkt_extr_head(pkt, &cmdrsp, 1);
cmd = cmdrsp & CFCTRL_CMD_MASK;
if (cmd != CFCTRL_CMD_LINK_ERR
- && CFCTRL_RSP_BIT != (CFCTRL_RSP_BIT & cmdrsp)) {
+ && CFCTRL_RSP_BIT != (CFCTRL_RSP_BIT & cmdrsp)
+ && CFCTRL_ERR_BIT != (CFCTRL_ERR_BIT & cmdrsp)) {
if (handle_loop(cfctrl, cmd, pkt) != 0)
cmdrsp |= CFCTRL_ERR_BIT;
}
@@ -515,18 +490,20 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt)
cfpkt_extr_head(pkt, &param, len);
break;
default:
- pr_warn("Request setup - invalid link type (%d)\n",
+ pr_warn("Request setup, invalid type (%d)\n",
serv);
goto error;
}
rsp.cmd = cmd;
rsp.param = linkparam;
+ spin_lock_bh(&cfctrl->info_list_lock);
req = cfctrl_remove_req(cfctrl, &rsp);
if (CFCTRL_ERR_BIT == (CFCTRL_ERR_BIT & cmdrsp) ||
cfpkt_erroneous(pkt)) {
- pr_err("Invalid O/E bit or parse error on CAIF control channel\n");
+ pr_err("Invalid O/E bit or parse error "
+ "on CAIF control channel\n");
cfctrl->res.reject_rsp(cfctrl->serv.layer.up,
0,
req ? req->client_layer
@@ -539,8 +516,9 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt)
client_layer : NULL);
}
- if (req != NULL)
- kfree(req);
+ kfree(req);
+
+ spin_unlock_bh(&cfctrl->info_list_lock);
}
break;
case CFCTRL_CMD_LINK_DESTROY:
@@ -578,18 +556,34 @@ error:
}
static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
- int phyid)
+ 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 (!list_empty(&this->list)) {
+ spin_lock_bh(&this->info_list_lock);
+ if (!list_empty(&this->list))
pr_debug("Received flow off in control layer\n");
+ spin_unlock_bh(&this->info_list_lock);
+ break;
+ case _CAIF_CTRLCMD_PHYIF_DOWN_IND: {
+ struct cfctrl_request_info *p, *tmp;
+
+ /* Find all connect request and report failure */
+ spin_lock_bh(&this->info_list_lock);
+ list_for_each_entry_safe(p, tmp, &this->list, list) {
+ if (p->param.phyid == phyid) {
+ list_del(&p->list);
+ p->client_layer->ctrlcmd(p->client_layer,
+ CAIF_CTRLCMD_INIT_FAIL_RSP,
+ phyid);
+ kfree(p);
+ }
}
- spin_unlock(&this->info_list_lock);
+ spin_unlock_bh(&this->info_list_lock);
break;
+ }
default:
break;
}
@@ -599,27 +593,33 @@ static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
static int handle_loop(struct cfctrl *ctrl, int cmd, struct cfpkt *pkt)
{
static int last_linkid;
+ static int dec;
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--)
+ spin_lock_bh(&ctrl->loop_linkid_lock);
+ if (!dec) {
+ for (linkid = last_linkid + 1; linkid < 254; linkid++)
+ if (!ctrl->loop_linkused[linkid])
+ goto found;
+ }
+ dec = 1;
+ for (linkid = last_linkid - 1; linkid > 1; linkid--)
if (!ctrl->loop_linkused[linkid])
goto found;
- spin_unlock(&ctrl->loop_linkid_lock);
- pr_err("Out of link-ids\n");
- return -EINVAL;
+ spin_unlock_bh(&ctrl->loop_linkid_lock);
+ return -1;
found:
+ if (linkid < 10)
+ dec = 0;
+
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);
+ spin_unlock_bh(&ctrl->loop_linkid_lock);
cfpkt_peek_head(pkt, &linktype, 1);
if (linktype == CFCTRL_SRV_UTIL) {
tmp = 0x01;
@@ -629,10 +629,10 @@ found:
break;
case CFCTRL_CMD_LINK_DESTROY:
- spin_lock(&ctrl->loop_linkid_lock);
+ spin_lock_bh(&ctrl->loop_linkid_lock);
cfpkt_peek_head(pkt, &linkid, 1);
ctrl->loop_linkused[linkid] = 0;
- spin_unlock(&ctrl->loop_linkid_lock);
+ spin_unlock_bh(&ctrl->loop_linkid_lock);
break;
default:
break;
diff --git a/net/caif/cfdbgl.c b/net/caif/cfdbgl.c
index 11a2af4c162..7aae0b56829 100644
--- a/net/caif/cfdbgl.c
+++ b/net/caif/cfdbgl.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*/
@@ -19,13 +19,10 @@ static int cfdbgl_transmit(struct cflayer *layr, struct cfpkt *pkt);
struct cflayer *cfdbgl_create(u8 channel_id, struct dev_info *dev_info)
{
- struct cfsrvl *dbg = kmalloc(sizeof(struct cfsrvl), GFP_ATOMIC);
- if (!dbg) {
- pr_warn("Out of memory\n");
+ struct cfsrvl *dbg = kzalloc(sizeof(struct cfsrvl), GFP_ATOMIC);
+ if (!dbg)
return NULL;
- }
caif_assert(offsetof(struct cfsrvl, layer) == 0);
- memset(dbg, 0, sizeof(struct cfsrvl));
cfsrvl_init(dbg, channel_id, dev_info, false);
dbg->layer.receive = cfdbgl_receive;
dbg->layer.transmit = cfdbgl_transmit;
@@ -44,8 +41,10 @@ static int cfdbgl_transmit(struct cflayer *layr, struct cfpkt *pkt)
struct caif_payload_info *info;
int ret;
- if (!cfsrvl_ready(service, &ret))
+ if (!cfsrvl_ready(service, &ret)) {
+ cfpkt_destroy(pkt);
return ret;
+ }
/* Add info for MUX-layer to route the packet out */
info = cfpkt_info(pkt);
diff --git a/net/caif/cfdgml.c b/net/caif/cfdgml.c
index d3ed264ad6c..3bdddb32d55 100644
--- a/net/caif/cfdgml.c
+++ b/net/caif/cfdgml.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*/
@@ -13,12 +13,12 @@
#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
#define DGM_MTU 1500
static int cfdgml_receive(struct cflayer *layr, struct cfpkt *pkt);
@@ -26,13 +26,10 @@ static int cfdgml_transmit(struct cflayer *layr, struct cfpkt *pkt);
struct cflayer *cfdgml_create(u8 channel_id, struct dev_info *dev_info)
{
- struct cfsrvl *dgm = kmalloc(sizeof(struct cfsrvl), GFP_ATOMIC);
- if (!dgm) {
- pr_warn("Out of memory\n");
+ struct cfsrvl *dgm = kzalloc(sizeof(struct cfsrvl), GFP_ATOMIC);
+ if (!dgm)
return NULL;
- }
caif_assert(offsetof(struct cfsrvl, layer) == 0);
- memset(dgm, 0, sizeof(struct cfsrvl));
cfsrvl_init(dgm, channel_id, dev_info, true);
dgm->layer.receive = cfdgml_receive;
dgm->layer.transmit = cfdgml_transmit;
@@ -84,18 +81,26 @@ static int cfdgml_receive(struct cflayer *layr, struct cfpkt *pkt)
static int cfdgml_transmit(struct cflayer *layr, struct cfpkt *pkt)
{
+ u8 packet_type;
u32 zero = 0;
struct caif_payload_info *info;
struct cfsrvl *service = container_obj(layr);
int ret;
- if (!cfsrvl_ready(service, &ret))
+
+ if (!cfsrvl_ready(service, &ret)) {
+ cfpkt_destroy(pkt);
return ret;
+ }
/* STE Modem cannot handle more than 1500 bytes datagrams */
- if (cfpkt_getlen(pkt) > DGM_MTU)
+ if (cfpkt_getlen(pkt) > DGM_MTU) {
+ cfpkt_destroy(pkt);
return -EMSGSIZE;
+ }
- cfpkt_add_head(pkt, &zero, 4);
+ cfpkt_add_head(pkt, &zero, 3);
+ packet_type = 0x08; /* B9 set - UNCLASSIFIED */
+ cfpkt_add_head(pkt, &packet_type, 1);
/* Add info for MUX-layer to route the packet out. */
info = cfpkt_info(pkt);
@@ -105,10 +110,5 @@ static int cfdgml_transmit(struct cflayer *layr, struct cfpkt *pkt)
*/
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;
+ return layr->dn->transmit(layr->dn, pkt);
}
diff --git a/net/caif/cffrml.c b/net/caif/cffrml.c
index a445043931a..8bc7caa28e6 100644
--- a/net/caif/cffrml.c
+++ b/net/caif/cffrml.c
@@ -2,7 +2,7 @@
* CAIF Framing Layer.
*
* Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*/
@@ -12,6 +12,7 @@
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/crc-ccitt.h>
+#include <linux/netdevice.h>
#include <net/caif/caif_layer.h>
#include <net/caif/cfpkt.h>
#include <net/caif/cffrml.h>
@@ -21,25 +22,29 @@
struct cffrml {
struct cflayer layer;
bool dofcs; /* !< FCS active */
+ int __percpu *pcpu_refcnt;
};
static int cffrml_receive(struct cflayer *layr, struct cfpkt *pkt);
static int cffrml_transmit(struct cflayer *layr, struct cfpkt *pkt);
static void cffrml_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
- int phyid);
+ int phyid);
static u32 cffrml_rcv_error;
static u32 cffrml_rcv_checsum_error;
struct cflayer *cffrml_create(u16 phyid, bool use_fcs)
{
- struct cffrml *this = kmalloc(sizeof(struct cffrml), GFP_ATOMIC);
- if (!this) {
- pr_warn("Out of memory\n");
+ struct cffrml *this = kzalloc(sizeof(struct cffrml), GFP_ATOMIC);
+ if (!this)
+ return NULL;
+ this->pcpu_refcnt = alloc_percpu(int);
+ if (this->pcpu_refcnt == NULL) {
+ kfree(this);
return NULL;
}
+
caif_assert(offsetof(struct cffrml, layer) == 0);
- memset(this, 0, sizeof(struct cflayer));
this->layer.receive = cffrml_receive;
this->layer.transmit = cffrml_transmit;
this->layer.ctrlcmd = cffrml_ctrlcmd;
@@ -49,6 +54,13 @@ struct cflayer *cffrml_create(u16 phyid, bool use_fcs)
return (struct cflayer *) this;
}
+void cffrml_free(struct cflayer *layer)
+{
+ struct cffrml *this = container_obj(layer);
+ free_percpu(this->pcpu_refcnt);
+ kfree(layer);
+}
+
void cffrml_set_uplayer(struct cflayer *this, struct cflayer *up)
{
this->up = up;
@@ -112,42 +124,74 @@ static int cffrml_receive(struct cflayer *layr, struct cfpkt *pkt)
cfpkt_destroy(pkt);
return -EPROTO;
}
+
+ if (layr->up == NULL) {
+ pr_err("Layr up is missing!\n");
+ cfpkt_destroy(pkt);
+ return -EINVAL;
+ }
+
return layr->up->receive(layr->up, pkt);
}
static int cffrml_transmit(struct cflayer *layr, struct cfpkt *pkt)
{
- int tmp;
u16 chks;
u16 len;
- int ret;
+ __le16 data;
+
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);
+ data = cpu_to_le16(chks);
+ cfpkt_add_trail(pkt, &data, 2);
} else {
cfpkt_pad_trail(pkt, 2);
}
len = cfpkt_getlen(pkt);
- tmp = cpu_to_le16(len);
- cfpkt_add_head(pkt, &tmp, 2);
+ data = cpu_to_le16(len);
+ cfpkt_add_head(pkt, &data, 2);
cfpkt_info(pkt)->hdr_len += 2;
if (cfpkt_erroneous(pkt)) {
pr_err("Packet is erroneous!\n");
+ cfpkt_destroy(pkt);
return -EPROTO;
}
- ret = layr->dn->transmit(layr->dn, pkt);
- if (ret < 0) {
- /* Remove header on faulty packet. */
- cfpkt_extr_head(pkt, &tmp, 2);
+
+ if (layr->dn == NULL) {
+ cfpkt_destroy(pkt);
+ return -ENODEV;
+
}
- return ret;
+ return layr->dn->transmit(layr->dn, pkt);
}
static void cffrml_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
- int phyid)
+ int phyid)
{
- if (layr->up->ctrlcmd)
+ if (layr->up && layr->up->ctrlcmd)
layr->up->ctrlcmd(layr->up, ctrl, layr->id);
}
+
+void cffrml_put(struct cflayer *layr)
+{
+ struct cffrml *this = container_obj(layr);
+ if (layr != NULL && this->pcpu_refcnt != NULL)
+ this_cpu_dec(*this->pcpu_refcnt);
+}
+
+void cffrml_hold(struct cflayer *layr)
+{
+ struct cffrml *this = container_obj(layr);
+ if (layr != NULL && this->pcpu_refcnt != NULL)
+ this_cpu_inc(*this->pcpu_refcnt);
+}
+
+int cffrml_refcnt_read(struct cflayer *layr)
+{
+ int i, refcnt = 0;
+ struct cffrml *this = container_obj(layr);
+ for_each_possible_cpu(i)
+ refcnt += *per_cpu_ptr(this->pcpu_refcnt, i);
+ return refcnt;
+}
diff --git a/net/caif/cfmuxl.c b/net/caif/cfmuxl.c
index 46f34b2e047..8c5d6386319 100644
--- a/net/caif/cfmuxl.c
+++ b/net/caif/cfmuxl.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*/
@@ -9,6 +9,7 @@
#include <linux/stddef.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
+#include <linux/rculist.h>
#include <net/caif/cfpkt.h>
#include <net/caif/cfmuxl.h>
#include <net/caif/cfsrvl.h>
@@ -41,7 +42,7 @@ struct cfmuxl {
static int cfmuxl_receive(struct cflayer *layr, struct cfpkt *pkt);
static int cfmuxl_transmit(struct cflayer *layr, struct cfpkt *pkt);
static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
- int phyid);
+ int phyid);
static struct cflayer *get_up(struct cfmuxl *muxl, u16 id);
struct cflayer *cfmuxl_create(void)
@@ -61,111 +62,88 @@ struct cflayer *cfmuxl_create(void)
return &this->layer;
}
-int cfmuxl_set_uplayer(struct cflayer *layr, struct cflayer *up, u8 linkid)
+int cfmuxl_set_dnlayer(struct cflayer *layr, struct cflayer *dn, u8 phyid)
{
- struct cfmuxl *muxl = container_obj(layr);
- spin_lock(&muxl->receive_lock);
- cfsrvl_get(up);
- list_add(&up->node, &muxl->srvl_list);
- spin_unlock(&muxl->receive_lock);
+ struct cfmuxl *muxl = (struct cfmuxl *) layr;
+
+ spin_lock_bh(&muxl->transmit_lock);
+ list_add_rcu(&dn->node, &muxl->frml_list);
+ spin_unlock_bh(&muxl->transmit_lock);
return 0;
}
-bool cfmuxl_is_phy_inuse(struct cflayer *layr, u8 phyid)
+static struct cflayer *get_from_id(struct list_head *list, u16 id)
{
- struct list_head *node;
- struct cflayer *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 cflayer, node);
- if (cfsrvl_phyid_match(layer, phyid)) {
- match = true;
- break;
- }
-
+ struct cflayer *lyr;
+ list_for_each_entry_rcu(lyr, list, node) {
+ if (lyr->id == id)
+ return lyr;
}
- spin_unlock(&muxl->receive_lock);
- return match;
+
+ return NULL;
}
-u8 cfmuxl_get_phyid(struct cflayer *layr, u8 channel_id)
+int cfmuxl_set_uplayer(struct cflayer *layr, struct cflayer *up, u8 linkid)
{
- struct cflayer *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;
-}
+ struct cflayer *old;
-int cfmuxl_set_dnlayer(struct cflayer *layr, struct cflayer *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;
-}
+ spin_lock_bh(&muxl->receive_lock);
-static struct cflayer *get_from_id(struct list_head *list, u16 id)
-{
- struct list_head *node;
- struct cflayer *layer;
- list_for_each(node, list) {
- layer = list_entry(node, struct cflayer, node);
- if (layer->id == id)
- return layer;
- }
- return NULL;
+ /* Two entries with same id is wrong, so remove old layer from mux */
+ old = get_from_id(&muxl->srvl_list, linkid);
+ if (old != NULL)
+ list_del_rcu(&old->node);
+
+ list_add_rcu(&up->node, &muxl->srvl_list);
+ spin_unlock_bh(&muxl->receive_lock);
+
+ return 0;
}
struct cflayer *cfmuxl_remove_dnlayer(struct cflayer *layr, u8 phyid)
{
struct cfmuxl *muxl = container_obj(layr);
struct cflayer *dn;
- spin_lock(&muxl->transmit_lock);
- memset(muxl->dn_cache, 0, sizeof(muxl->dn_cache));
+ int idx = phyid % DN_CACHE_SIZE;
+
+ spin_lock_bh(&muxl->transmit_lock);
+ RCU_INIT_POINTER(muxl->dn_cache[idx], NULL);
dn = get_from_id(&muxl->frml_list, phyid);
- if (dn == NULL) {
- spin_unlock(&muxl->transmit_lock);
- return NULL;
- }
- list_del(&dn->node);
+ if (dn == NULL)
+ goto out;
+
+ list_del_rcu(&dn->node);
caif_assert(dn != NULL);
- spin_unlock(&muxl->transmit_lock);
+out:
+ spin_unlock_bh(&muxl->transmit_lock);
return dn;
}
-/* Invariant: lock is taken */
static struct cflayer *get_up(struct cfmuxl *muxl, u16 id)
{
struct cflayer *up;
int idx = id % UP_CACHE_SIZE;
- up = muxl->up_cache[idx];
+ up = rcu_dereference(muxl->up_cache[idx]);
if (up == NULL || up->id != id) {
+ spin_lock_bh(&muxl->receive_lock);
up = get_from_id(&muxl->srvl_list, id);
- muxl->up_cache[idx] = up;
+ rcu_assign_pointer(muxl->up_cache[idx], up);
+ spin_unlock_bh(&muxl->receive_lock);
}
return up;
}
-/* Invariant: lock is taken */
static struct cflayer *get_dn(struct cfmuxl *muxl, struct dev_info *dev_info)
{
struct cflayer *dn;
int idx = dev_info->id % DN_CACHE_SIZE;
- dn = muxl->dn_cache[idx];
+ dn = rcu_dereference(muxl->dn_cache[idx]);
if (dn == NULL || dn->id != dev_info->id) {
+ spin_lock_bh(&muxl->transmit_lock);
dn = get_from_id(&muxl->frml_list, dev_info->id);
- muxl->dn_cache[idx] = dn;
+ rcu_assign_pointer(muxl->dn_cache[idx], dn);
+ spin_unlock_bh(&muxl->transmit_lock);
}
return dn;
}
@@ -174,15 +152,22 @@ struct cflayer *cfmuxl_remove_uplayer(struct cflayer *layr, u8 id)
{
struct cflayer *up;
struct cfmuxl *muxl = container_obj(layr);
- spin_lock(&muxl->receive_lock);
- up = get_up(muxl, id);
+ int idx = id % UP_CACHE_SIZE;
+
+ if (id == 0) {
+ pr_warn("Trying to remove control layer\n");
+ return NULL;
+ }
+
+ spin_lock_bh(&muxl->receive_lock);
+ up = get_from_id(&muxl->srvl_list, id);
if (up == NULL)
goto out;
- memset(muxl->up_cache, 0, sizeof(muxl->up_cache));
- list_del(&up->node);
- cfsrvl_put(up);
+
+ RCU_INIT_POINTER(muxl->up_cache[idx], NULL);
+ list_del_rcu(&up->node);
out:
- spin_unlock(&muxl->receive_lock);
+ spin_unlock_bh(&muxl->receive_lock);
return up;
}
@@ -197,58 +182,86 @@ static int cfmuxl_receive(struct cflayer *layr, struct cfpkt *pkt)
cfpkt_destroy(pkt);
return -EPROTO;
}
-
- spin_lock(&muxl->receive_lock);
+ rcu_read_lock();
up = get_up(muxl, id);
- spin_unlock(&muxl->receive_lock);
+
if (up == NULL) {
- pr_info("Received data on unknown link ID = %d (0x%x) up == NULL",
- id, id);
+ pr_debug("Received data on unknown link ID = %d (0x%x)"
+ " up == NULL", id, id);
cfpkt_destroy(pkt);
/*
* Don't return ERROR, since modem misbehaves and sends out
* flow on before linksetup response.
*/
+
+ rcu_read_unlock();
return /* CFGLU_EPROT; */ 0;
}
+
+ /* We can't hold rcu_lock during receive, so take a ref count instead */
cfsrvl_get(up);
+ rcu_read_unlock();
+
ret = up->receive(up, pkt);
+
cfsrvl_put(up);
return ret;
}
static int cfmuxl_transmit(struct cflayer *layr, struct cfpkt *pkt)
{
- int ret;
struct cfmuxl *muxl = container_obj(layr);
+ int err;
u8 linkid;
struct cflayer *dn;
struct caif_payload_info *info = cfpkt_info(pkt);
- dn = get_dn(muxl, cfpkt_info(pkt)->dev_info);
+ BUG_ON(!info);
+
+ rcu_read_lock();
+
+ dn = get_dn(muxl, info->dev_info);
if (dn == NULL) {
- pr_warn("Send data on unknown phy ID = %d (0x%x)\n",
+ pr_debug("Send data on unknown phy ID = %d (0x%x)\n",
info->dev_info->id, info->dev_info->id);
+ rcu_read_unlock();
+ cfpkt_destroy(pkt);
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;
+
+ /* We can't hold rcu_lock during receive, so take a ref count instead */
+ cffrml_hold(dn);
+
+ rcu_read_unlock();
+
+ err = dn->transmit(dn, pkt);
+
+ cffrml_put(dn);
+ return err;
}
static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
- int phyid)
+ int phyid)
{
struct cfmuxl *muxl = container_obj(layr);
- struct list_head *node;
struct cflayer *layer;
- list_for_each(node, &muxl->srvl_list) {
- layer = list_entry(node, struct cflayer, node);
- if (cfsrvl_phyid_match(layer, phyid))
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(layer, &muxl->srvl_list, node) {
+
+ if (cfsrvl_phyid_match(layer, phyid) && layer->ctrlcmd) {
+
+ if ((ctrl == _CAIF_CTRLCMD_PHYIF_DOWN_IND ||
+ ctrl == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND) &&
+ layer->id != 0)
+ cfmuxl_remove_uplayer(layr, layer->id);
+
+ /* NOTE: ctrlcmd is not allowed to block */
layer->ctrlcmd(layer, ctrl, phyid);
+ }
}
+ rcu_read_unlock();
}
diff --git a/net/caif/cfpkt_skbuff.c b/net/caif/cfpkt_skbuff.c
index d7e865e2ff6..1be0b521ac4 100644
--- a/net/caif/cfpkt_skbuff.c
+++ b/net/caif/cfpkt_skbuff.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*/
@@ -9,6 +9,7 @@
#include <linux/string.h>
#include <linux/skbuff.h>
#include <linux/hardirq.h>
+#include <linux/export.h>
#include <net/caif/cfpkt.h>
#define PKT_PREFIX 48
@@ -42,27 +43,26 @@ struct cfpkt_priv_data {
bool erronous;
};
-inline struct cfpkt_priv_data *cfpkt_priv(struct cfpkt *pkt)
+static 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)
+static inline bool is_erronous(struct cfpkt *pkt)
{
return cfpkt_priv(pkt)->erronous;
}
-inline struct sk_buff *pkt_to_skb(struct cfpkt *pkt)
+static inline struct sk_buff *pkt_to_skb(struct cfpkt *pkt)
{
return &pkt->skb;
}
-inline struct cfpkt *skb_to_pkt(struct sk_buff *skb)
+static inline struct cfpkt *skb_to_pkt(struct sk_buff *skb)
{
return (struct cfpkt *) skb;
}
-
struct cfpkt *cfpkt_fromnative(enum caif_direction dir, void *nativepkt)
{
struct cfpkt *pkt = skb_to_pkt(nativepkt);
@@ -97,21 +97,18 @@ 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);
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)
{
@@ -123,7 +120,6 @@ int cfpkt_peek_head(struct cfpkt *pkt, void *data, u16 len)
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)
{
@@ -145,7 +141,8 @@ int cfpkt_extr_head(struct cfpkt *pkt, void *data, u16 len)
}
from = skb_pull(skb, len);
from -= len;
- memcpy(data, from, len);
+ if (data)
+ memcpy(data, from, len);
return 0;
}
EXPORT_SYMBOL(cfpkt_extr_head);
@@ -171,13 +168,11 @@ int cfpkt_extr_trail(struct cfpkt *pkt, void *dta, u16 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)
{
@@ -208,31 +203,19 @@ int cfpkt_add_body(struct cfpkt *pkt, const void *data, u16 len)
PKT_ERROR(pkt, "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_warn("Packet is non-linear\n");
- skb->len += len;
- skb->data_len += len;
- }
}
/* All set to put the last SKB and optionally write data there. */
- to = skb_put(lastskb, len);
+ to = pskb_put(skb, 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)
{
@@ -265,18 +248,16 @@ 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)
+ u16 (*iter_func)(u16, void *, u16),
+ u16 data)
{
/*
* Don't care about the performance hit of linearizing,
@@ -290,7 +271,6 @@ inline u16 cfpkt_iterate(struct cfpkt *pkt,
}
return iter_func(data, pkt->skb.data, cfpkt_getlen(pkt));
}
-EXPORT_SYMBOL(cfpkt_iterate);
int cfpkt_setlen(struct cfpkt *pkt, u16 len)
{
@@ -315,22 +295,10 @@ int cfpkt_setlen(struct cfpkt *pkt, u16 len)
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 (!pkt)
- return NULL;
- 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 cfpkt *addpkt,
+ u16 expectlen)
{
struct sk_buff *dst = pkt_to_skb(dstpkt);
struct sk_buff *add = pkt_to_skb(addpkt);
@@ -368,7 +336,6 @@ struct cfpkt *cfpkt_append(struct cfpkt *dstpkt,
dst->len += addlen;
return skb_to_pkt(dst);
}
-EXPORT_SYMBOL(cfpkt_append);
struct cfpkt *cfpkt_split(struct cfpkt *pkt, u16 pos)
{
@@ -404,176 +371,23 @@ struct cfpkt *cfpkt_split(struct cfpkt *pkt, u16 pos)
memcpy(skb2->data, split, len2nd);
skb2->tail += len2nd;
skb2->len += len2nd;
+ skb2->priority = skb->priority;
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:%ld(%ld+%ld) {%ld,%ld} data: [",
- is_erronous(pkt) ? "ERRONOUS-SKB" :
- (skb->data_len != 0 ? "COMPLEX-SKB" : "SKB"),
- skb,
- (long) skb->len,
- (long) (skb_tail_pointer(skb) - skb->data),
- (long) skb->data_len,
- (long) (skb->data - skb->head),
- (long) (skb_tail_pointer(skb) - skb->head));
- p = buf + strlen(buf);
-
- for (i = 0; i < skb_tail_pointer(skb) - 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, "skb_cow_data failed\n");
- return -EPROTO;
- }
-
- if (unlikely(skb_linearize(skb) != 0)) {
- PKT_ERROR(pkt, "linearize failed\n");
- return -EPROTO;
- }
-
- if (unlikely(skb_tailroom(skb) < buflen)) {
- PKT_ERROR(pkt, "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, "buflen too large - failed\n");
- return -EPROTO;
- }
-
- if (unlikely(buflen > skb_headlen(skb))) {
- if (unlikely(skb_linearize(skb) != 0)) {
- PKT_ERROR(pkt, "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)
+bool cfpkt_erroneous(struct cfpkt *pkt)
{
return cfpkt_priv(pkt)->erronous;
}
-EXPORT_SYMBOL(cfpkt_erroneous);
-
-struct cfpktq *cfpktq_create(void)
-{
- 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;
- return clone;
-}
-EXPORT_SYMBOL(cfpkt_clone_release);
struct caif_payload_info *cfpkt_info(struct cfpkt *pkt)
{
return (struct caif_payload_info *)&pkt_to_skb(pkt)->cb;
}
EXPORT_SYMBOL(cfpkt_info);
+
+void cfpkt_set_prio(struct cfpkt *pkt, int prio)
+{
+ pkt_to_skb(pkt)->priority = prio;
+}
+EXPORT_SYMBOL(cfpkt_set_prio);
diff --git a/net/caif/cfrfml.c b/net/caif/cfrfml.c
index e2fb5fa7579..61d7617d924 100644
--- a/net/caif/cfrfml.c
+++ b/net/caif/cfrfml.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*/
@@ -31,9 +31,9 @@ struct cfrfml {
spinlock_t sync;
};
-static void cfrfml_release(struct kref *kref)
+static void cfrfml_release(struct cflayer *layer)
{
- struct cfsrvl *srvl = container_of(kref, struct cfsrvl, ref);
+ struct cfsrvl *srvl = container_of(layer, struct cfsrvl, layer);
struct cfrfml *rfml = container_obj(&srvl->layer);
if (rfml->incomplete_frm)
@@ -43,16 +43,13 @@ static void cfrfml_release(struct kref *kref)
}
struct cflayer *cfrfml_create(u8 channel_id, struct dev_info *dev_info,
- int mtu_size)
+ int mtu_size)
{
int tmp;
- struct cfrfml *this =
- kzalloc(sizeof(struct cfrfml), GFP_ATOMIC);
+ struct cfrfml *this = kzalloc(sizeof(struct cfrfml), GFP_ATOMIC);
- if (!this) {
- pr_warn("Out of memory\n");
+ if (!this)
return NULL;
- }
cfsrvl_init(&this->serv, channel_id, dev_info, false);
this->serv.release = cfrfml_release;
@@ -72,7 +69,7 @@ struct cflayer *cfrfml_create(u8 channel_id, struct dev_info *dev_info,
}
static struct cfpkt *rfm_append(struct cfrfml *rfml, char *seghead,
- struct cfpkt *pkt, int *err)
+ struct cfpkt *pkt, int *err)
{
struct cfpkt *tmppkt;
*err = -EPROTO;
@@ -187,13 +184,18 @@ out:
rfml->serv.dev_info.id);
}
spin_unlock(&rfml->sync);
+
+ if (unlikely(err == -EAGAIN))
+ /* It is not possible to recover after drop of a fragment */
+ err = -EIO;
+
return err;
}
static int cfrfml_transmit_segment(struct cfrfml *rfml, struct cfpkt *pkt)
{
- caif_assert(cfpkt_getlen(pkt) < rfml->fragment_size);
+ caif_assert(cfpkt_getlen(pkt) < rfml->fragment_size + RFM_HEAD_SIZE);
/* Add info for MUX-layer to route the packet out. */
cfpkt_info(pkt)->channel_id = rfml->serv.layer.id;
@@ -221,7 +223,7 @@ static int cfrfml_transmit(struct cflayer *layr, struct cfpkt *pkt)
caif_assert(layr->dn->transmit != NULL);
if (!cfsrvl_ready(&rfml->serv, &err))
- return err;
+ goto out;
err = -EPROTO;
if (cfpkt_getlen(pkt) <= RFM_HEAD_SIZE-1)
@@ -254,8 +256,11 @@ static int cfrfml_transmit(struct cflayer *layr, struct cfpkt *pkt)
err = cfrfml_transmit_segment(rfml, frontpkt);
- if (err != 0)
+ if (err != 0) {
+ frontpkt = NULL;
goto out;
+ }
+
frontpkt = rearpkt;
rearpkt = NULL;
@@ -289,19 +294,8 @@ out:
if (rearpkt)
cfpkt_destroy(rearpkt);
- if (frontpkt && frontpkt != pkt) {
-
+ if (frontpkt)
cfpkt_destroy(frontpkt);
- /*
- * Socket layer will free the original packet,
- * but this packet may already be sent and
- * freed. So we have to return 0 in this case
- * to avoid socket layer to re-free this packet.
- * The return of shutdown indication will
- * cause connection to be invalidated anyhow.
- */
- err = 0;
- }
}
return err;
diff --git a/net/caif/cfserl.c b/net/caif/cfserl.c
index 9297f7dea9d..ce60f06d76d 100644
--- a/net/caif/cfserl.c
+++ b/net/caif/cfserl.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*/
@@ -25,26 +25,21 @@ struct cfserl {
spinlock_t sync;
bool usestx;
};
-#define STXLEN(layr) (layr->usestx ? 1 : 0)
static int cfserl_receive(struct cflayer *layr, struct cfpkt *pkt);
static int cfserl_transmit(struct cflayer *layr, struct cfpkt *pkt);
static void cfserl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
- int phyid);
+ int phyid);
-struct cflayer *cfserl_create(int type, int instance, bool use_stx)
+struct cflayer *cfserl_create(int instance, bool use_stx)
{
- struct cfserl *this = kmalloc(sizeof(struct cfserl), GFP_ATOMIC);
- if (!this) {
- pr_warn("Out of memory\n");
+ struct cfserl *this = kzalloc(sizeof(struct cfserl), GFP_ATOMIC);
+ if (!this)
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");
@@ -180,19 +175,14 @@ static int cfserl_receive(struct cflayer *l, struct cfpkt *newpkt)
static int cfserl_transmit(struct cflayer *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;
+ return layer->dn->transmit(layer->dn, newpkt);
}
static void cfserl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
- int phyid)
+ int phyid)
{
layr->up->ctrlcmd(layr->up, ctrl, phyid);
}
diff --git a/net/caif/cfsrvl.c b/net/caif/cfsrvl.c
index ab5e542526b..a6e11546305 100644
--- a/net/caif/cfsrvl.c
+++ b/net/caif/cfsrvl.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*/
@@ -10,9 +10,12 @@
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/pkt_sched.h>
#include <net/caif/caif_layer.h>
#include <net/caif/cfsrvl.h>
#include <net/caif/cfpkt.h>
+#include <net/caif/caif_dev.h>
#define SRVL_CTRL_PKT_SIZE 1
#define SRVL_FLOW_OFF 0x81
@@ -23,12 +26,12 @@
#define container_obj(layr) container_of(layr, struct cfsrvl, layer)
static void cfservl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
- int phyid)
+ int phyid)
{
struct cfsrvl *service = container_obj(layr);
- caif_assert(layr->up != NULL);
- caif_assert(layr->up->ctrlcmd != NULL);
+ if (layr->up == NULL || layr->up->ctrlcmd == NULL)
+ return;
switch (ctrl) {
case CAIF_CTRLCMD_INIT_RSP:
@@ -107,10 +110,8 @@ static int cfservl_modemcmd(struct cflayer *layr, enum caif_modemcmd ctrl)
struct caif_payload_info *info;
u8 flow_on = SRVL_FLOW_ON;
pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE);
- if (!pkt) {
- pr_warn("Out of memory\n");
+ if (!pkt)
return -ENOMEM;
- }
if (cfpkt_add_head(pkt, &flow_on, 1) < 0) {
pr_err("Packet is erroneous!\n");
@@ -121,6 +122,7 @@ static int cfservl_modemcmd(struct cflayer *layr, enum caif_modemcmd ctrl)
info->channel_id = service->layer.id;
info->hdr_len = 1;
info->dev_info = &service->dev_info;
+ cfpkt_set_prio(pkt, TC_PRIO_CONTROL);
return layr->dn->transmit(layr->dn, pkt);
}
case CAIF_MODEMCMD_FLOW_OFF_REQ:
@@ -129,10 +131,8 @@ static int cfservl_modemcmd(struct cflayer *layr, enum caif_modemcmd ctrl)
struct caif_payload_info *info;
u8 flow_off = SRVL_FLOW_OFF;
pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE);
- if (!pkt) {
- pr_warn("Out of memory\n");
+ if (!pkt)
return -ENOMEM;
- }
if (cfpkt_add_head(pkt, &flow_off, 1) < 0) {
pr_err("Packet is erroneous!\n");
@@ -143,6 +143,7 @@ static int cfservl_modemcmd(struct cflayer *layr, enum caif_modemcmd ctrl)
info->channel_id = service->layer.id;
info->hdr_len = 1;
info->dev_info = &service->dev_info;
+ cfpkt_set_prio(pkt, TC_PRIO_CONTROL);
return layr->dn->transmit(layr->dn, pkt);
}
default:
@@ -151,22 +152,16 @@ static int cfservl_modemcmd(struct cflayer *layr, enum caif_modemcmd ctrl)
return -EINVAL;
}
-void cfservl_destroy(struct cflayer *layer)
+static void cfsrvl_release(struct cflayer *layer)
{
- kfree(layer);
-}
-
-void cfsrvl_release(struct kref *kref)
-{
- struct cfsrvl *service = container_of(kref, struct cfsrvl, ref);
+ struct cfsrvl *service = container_of(layer, struct cfsrvl, layer);
kfree(service);
}
void cfsrvl_init(struct cfsrvl *service,
- u8 channel_id,
- struct dev_info *dev_info,
- bool supports_flowctrl
- )
+ u8 channel_id,
+ struct dev_info *dev_info,
+ bool supports_flowctrl)
{
caif_assert(offsetof(struct cfsrvl, layer) == 0);
service->open = false;
@@ -178,22 +173,17 @@ void cfsrvl_init(struct cfsrvl *service,
service->dev_info = *dev_info;
service->supports_flowctrl = supports_flowctrl;
service->release = cfsrvl_release;
- kref_init(&service->ref);
}
-
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;
+ return true;
}
+
u8 cfsrvl_getphyid(struct cflayer *layer)
{
struct cfsrvl *servl = container_obj(layer);
@@ -205,3 +195,27 @@ bool cfsrvl_phyid_match(struct cflayer *layer, int phyid)
struct cfsrvl *servl = container_obj(layer);
return servl->dev_info.id == phyid;
}
+
+void caif_free_client(struct cflayer *adap_layer)
+{
+ struct cfsrvl *servl;
+ if (adap_layer == NULL || adap_layer->dn == NULL)
+ return;
+ servl = container_obj(adap_layer->dn);
+ servl->release(&servl->layer);
+}
+EXPORT_SYMBOL(caif_free_client);
+
+void caif_client_register_refcnt(struct cflayer *adapt_layer,
+ void (*hold)(struct cflayer *lyr),
+ void (*put)(struct cflayer *lyr))
+{
+ struct cfsrvl *service;
+
+ if (WARN_ON(adapt_layer == NULL || adapt_layer->dn == NULL))
+ return;
+ service = container_of(adapt_layer->dn, struct cfsrvl, layer);
+ service->hold = hold;
+ service->put = put;
+}
+EXPORT_SYMBOL(caif_client_register_refcnt);
diff --git a/net/caif/cfutill.c b/net/caif/cfutill.c
index efad410e4c8..1728fa4471c 100644
--- a/net/caif/cfutill.c
+++ b/net/caif/cfutill.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*/
@@ -20,19 +20,16 @@
#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 cflayer *layr, struct cfpkt *pkt);
static int cfutill_transmit(struct cflayer *layr, struct cfpkt *pkt);
struct cflayer *cfutill_create(u8 channel_id, struct dev_info *dev_info)
{
- struct cfsrvl *util = kmalloc(sizeof(struct cfsrvl), GFP_ATOMIC);
- if (!util) {
- pr_warn("Out of memory\n");
+ struct cfsrvl *util = kzalloc(sizeof(struct cfsrvl), GFP_ATOMIC);
+ if (!util)
return NULL;
- }
caif_assert(offsetof(struct cfsrvl, layer) == 0);
- memset(util, 0, sizeof(struct cfsrvl));
cfsrvl_init(util, channel_id, dev_info, true);
util->layer.receive = cfutill_receive;
util->layer.transmit = cfutill_transmit;
@@ -87,8 +84,11 @@ static int cfutill_transmit(struct cflayer *layr, struct cfpkt *pkt)
caif_assert(layr != NULL);
caif_assert(layr->dn != NULL);
caif_assert(layr->dn->transmit != NULL);
- if (!cfsrvl_ready(service, &ret))
+
+ if (!cfsrvl_ready(service, &ret)) {
+ cfpkt_destroy(pkt);
return ret;
+ }
cfpkt_add_head(pkt, &zero, 1);
/* Add info for MUX-layer to route the packet out. */
@@ -100,10 +100,5 @@ static int cfutill_transmit(struct cflayer *layr, struct cfpkt *pkt)
*/
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;
+ return layr->dn->transmit(layr->dn, pkt);
}
diff --git a/net/caif/cfveil.c b/net/caif/cfveil.c
index 3b425b189a9..262224581ef 100644
--- a/net/caif/cfveil.c
+++ b/net/caif/cfveil.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*/
@@ -17,7 +17,7 @@
#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 cflayer *layr, struct cfpkt *pkt);
@@ -25,13 +25,10 @@ static int cfvei_transmit(struct cflayer *layr, struct cfpkt *pkt);
struct cflayer *cfvei_create(u8 channel_id, struct dev_info *dev_info)
{
- struct cfsrvl *vei = kmalloc(sizeof(struct cfsrvl), GFP_ATOMIC);
- if (!vei) {
- pr_warn("Out of memory\n");
+ struct cfsrvl *vei = kzalloc(sizeof(struct cfsrvl), GFP_ATOMIC);
+ if (!vei)
return NULL;
- }
caif_assert(offsetof(struct cfsrvl, layer) == 0);
- memset(vei, 0, sizeof(struct cfsrvl));
cfsrvl_init(vei, channel_id, dev_info, true);
vei->layer.receive = cfvei_receive;
vei->layer.transmit = cfvei_transmit;
@@ -82,13 +79,14 @@ static int cfvei_transmit(struct cflayer *layr, struct cfpkt *pkt)
int ret;
struct cfsrvl *service = container_obj(layr);
if (!cfsrvl_ready(service, &ret))
- return ret;
+ goto err;
caif_assert(layr->dn != NULL);
caif_assert(layr->dn->transmit != NULL);
if (cfpkt_add_head(pkt, &tmp, 1) < 0) {
pr_err("Packet is erroneous!\n");
- return -EPROTO;
+ ret = -EPROTO;
+ goto err;
}
/* Add info-> for MUX-layer to route the packet out. */
@@ -96,8 +94,8 @@ static int cfvei_transmit(struct cflayer *layr, struct cfpkt *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 layr->dn->transmit(layr->dn, pkt);
+err:
+ cfpkt_destroy(pkt);
return ret;
}
diff --git a/net/caif/cfvidl.c b/net/caif/cfvidl.c
index bf6fef2a0ef..b3b110e8a35 100644
--- a/net/caif/cfvidl.c
+++ b/net/caif/cfvidl.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*/
@@ -21,14 +21,11 @@ static int cfvidl_transmit(struct cflayer *layr, struct cfpkt *pkt);
struct cflayer *cfvidl_create(u8 channel_id, struct dev_info *dev_info)
{
- struct cfsrvl *vid = kmalloc(sizeof(struct cfsrvl), GFP_ATOMIC);
- if (!vid) {
- pr_warn("Out of memory\n");
+ struct cfsrvl *vid = kzalloc(sizeof(struct cfsrvl), GFP_ATOMIC);
+ if (!vid)
return NULL;
- }
caif_assert(offsetof(struct cfsrvl, layer) == 0);
- memset(vid, 0, sizeof(struct cfsrvl));
cfsrvl_init(vid, channel_id, dev_info, false);
vid->layer.receive = cfvidl_receive;
vid->layer.transmit = cfvidl_transmit;
@@ -53,15 +50,16 @@ static int cfvidl_transmit(struct cflayer *layr, struct cfpkt *pkt)
struct caif_payload_info *info;
u32 videoheader = 0;
int ret;
- if (!cfsrvl_ready(service, &ret))
+
+ if (!cfsrvl_ready(service, &ret)) {
+ cfpkt_destroy(pkt);
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;
+ return layr->dn->transmit(layr->dn, pkt);
}
diff --git a/net/caif/chnl_net.c b/net/caif/chnl_net.c
index 84a422c9894..4589ff67bfa 100644
--- a/net/caif/chnl_net.c
+++ b/net/caif/chnl_net.c
@@ -1,14 +1,14 @@
/*
* Copyright (C) ST-Ericsson AB 2010
- * Authors: Sjur Brendeland/sjur.brandeland@stericsson.com
- * Daniel Martensson / Daniel.Martensson@stericsson.com
+ * Authors: Sjur Brendeland
+ * Daniel Martensson
* License terms: GNU General Public License (GPL) version 2
*/
#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
-#include <linux/version.h>
#include <linux/fs.h>
+#include <linux/hardirq.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/netdevice.h>
@@ -20,7 +20,6 @@
#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>
@@ -29,6 +28,7 @@
/* 5 sec. connect timeout */
#define CONNECT_TIMEOUT (5 * HZ)
#define CAIF_NET_DEFAULT_QUEUE_LEN 500
+#define UNDEF_CONNID 0xffffffff
/*This list is protected by the rtnl lock. */
static LIST_HEAD(chnl_net_list);
@@ -73,24 +73,44 @@ static void robust_list_del(struct list_head *delete_node)
static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt)
{
struct sk_buff *skb;
- struct chnl_net *priv = container_of(layr, struct chnl_net, chnl);
+ struct chnl_net *priv;
int pktlen;
- int err = 0;
+ const u8 *ip_version;
+ u8 buf;
priv = container_of(layr, struct chnl_net, chnl);
-
if (!priv)
return -EINVAL;
+ skb = (struct sk_buff *) cfpkt_tonative(pkt);
+
/* Get length of CAIF packet. */
- pktlen = cfpkt_getlen(pkt);
+ pktlen = skb->len;
- 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);
+
+ /* check the version of IP */
+ ip_version = skb_header_pointer(skb, 0, 1, &buf);
+ if (!ip_version) {
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ switch (*ip_version >> 4) {
+ case 4:
+ skb->protocol = htons(ETH_P_IP);
+ break;
+ case 6:
+ skb->protocol = htons(ETH_P_IPV6);
+ break;
+ default:
+ kfree_skb(skb);
+ priv->netdev->stats.rx_errors++;
+ return -EINVAL;
+ }
/* If we change the header in loop mode, the checksum is corrupted. */
if (priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP)
@@ -107,7 +127,7 @@ static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt)
priv->netdev->stats.rx_packets++;
priv->netdev->stats.rx_bytes += pktlen;
- return err;
+ return 0;
}
static int delete_device(struct chnl_net *dev)
@@ -123,22 +143,31 @@ static void close_work(struct work_struct *work)
struct chnl_net *dev = NULL;
struct list_head *list_node;
struct list_head *_tmp;
- /* May be called with or without RTNL lock held */
- int islocked = rtnl_is_locked();
- if (!islocked)
- rtnl_lock();
+
+ rtnl_lock();
list_for_each_safe(list_node, _tmp, &chnl_net_list) {
dev = list_entry(list_node, struct chnl_net, list_field);
if (dev->state == CAIF_SHUTDOWN)
dev_close(dev->netdev);
}
- if (!islocked)
- rtnl_unlock();
+ rtnl_unlock();
}
static DECLARE_WORK(close_worker, close_work);
+static void chnl_hold(struct cflayer *lyr)
+{
+ struct chnl_net *priv = container_of(lyr, struct chnl_net, chnl);
+ dev_hold(priv->netdev);
+}
+
+static void chnl_put(struct cflayer *lyr)
+{
+ struct chnl_net *priv = container_of(lyr, struct chnl_net, chnl);
+ dev_put(priv->netdev);
+}
+
static void chnl_flowctrl_cb(struct cflayer *layr, enum caif_ctrlcmd flow,
- int phyid)
+ int phyid)
{
struct chnl_net *priv = container_of(layr, struct chnl_net, chnl);
pr_debug("NET flowctrl func called flow: %s\n",
@@ -174,6 +203,7 @@ static void chnl_flowctrl_cb(struct cflayer *layr, enum caif_ctrlcmd flow,
netif_wake_queue(priv->netdev);
break;
case CAIF_CTRLCMD_INIT_RSP:
+ caif_client_register_refcnt(&priv->chnl, chnl_hold, chnl_put);
priv->state = CAIF_CONNECTED;
priv->flowenabled = true;
netif_wake_queue(priv->netdev);
@@ -195,12 +225,16 @@ static int chnl_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (skb->len > priv->netdev->mtu) {
pr_warn("Size of skb exceeded MTU\n");
- return -ENOSPC;
+ kfree_skb(skb);
+ dev->stats.tx_errors++;
+ return NETDEV_TX_OK;
}
if (!priv->flowenabled) {
pr_debug("dropping packets flow off\n");
- return NETDEV_TX_BUSY;
+ kfree_skb(skb);
+ dev->stats.tx_dropped++;
+ return NETDEV_TX_OK;
}
if (priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP)
@@ -214,9 +248,8 @@ static int chnl_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* 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;
+ dev->stats.tx_dropped++;
+ return NETDEV_TX_OK;
}
/* Update statistics. */
@@ -241,8 +274,9 @@ static int chnl_net_open(struct net_device *dev)
if (priv->state != CAIF_CONNECTING) {
priv->state = CAIF_CONNECTING;
- result = caif_connect_client(&priv->conn_req, &priv->chnl,
- &llifindex, &headroom, &tailroom);
+ result = caif_connect_client(dev_net(dev), &priv->conn_req,
+ &priv->chnl, &llifindex,
+ &headroom, &tailroom);
if (result != 0) {
pr_debug("err: "
"Unable to register and open device,"
@@ -251,7 +285,7 @@ static int chnl_net_open(struct net_device *dev)
goto error;
}
- lldev = dev_get_by_index(dev_net(dev), llifindex);
+ lldev = __dev_get_by_index(dev_net(dev), llifindex);
if (lldev == NULL) {
pr_debug("no interface?\n");
@@ -273,7 +307,6 @@ static int chnl_net_open(struct net_device *dev)
mtu = min_t(int, dev->mtu, lldev->mtu - (headroom + tailroom));
mtu = min_t(int, GPRS_PDP_MTU, mtu);
dev_set_mtu(dev, mtu);
- dev_put(lldev);
if (mtu < 100) {
pr_warn("CAIF Interface MTU too small (%d)\n", mtu);
@@ -298,7 +331,7 @@ static int chnl_net_open(struct net_device *dev)
if (result == 0) {
pr_debug("connect timeout\n");
- caif_disconnect_client(&priv->chnl);
+ caif_disconnect_client(dev_net(dev), &priv->chnl);
priv->state = CAIF_DISCONNECTED;
pr_debug("state disconnected\n");
result = -ETIMEDOUT;
@@ -314,7 +347,7 @@ static int chnl_net_open(struct net_device *dev)
return 0;
error:
- caif_disconnect_client(&priv->chnl);
+ caif_disconnect_client(dev_net(dev), &priv->chnl);
priv->state = CAIF_DISCONNECTED;
pr_debug("state disconnected\n");
return result;
@@ -328,7 +361,7 @@ static int chnl_net_stop(struct net_device *dev)
ASSERT_RTNL();
priv = netdev_priv(dev);
priv->state = CAIF_DISCONNECTED;
- caif_disconnect_client(&priv->chnl);
+ caif_disconnect_client(dev_net(dev), &priv->chnl);
return 0;
}
@@ -357,11 +390,18 @@ static const struct net_device_ops netdev_ops = {
.ndo_start_xmit = chnl_net_start_xmit,
};
+static void chnl_net_destructor(struct net_device *dev)
+{
+ struct chnl_net *priv = netdev_priv(dev);
+ caif_free_client(&priv->chnl);
+ free_netdev(dev);
+}
+
static void ipcaif_net_setup(struct net_device *dev)
{
struct chnl_net *priv;
dev->netdev_ops = &netdev_ops;
- dev->destructor = free_netdev;
+ dev->destructor = chnl_net_destructor;
dev->flags |= IFF_NOARP;
dev->flags |= IFF_POINTOPOINT;
dev->mtu = GPRS_PDP_MTU;
@@ -375,12 +415,10 @@ static void ipcaif_net_setup(struct net_device *dev)
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->conn_req.sockaddr.u.dgm.connection_id = UNDEF_CONNID;
priv->flowenabled = false;
- ASSERT_RTNL();
init_waitqueue_head(&priv->netmgmt_wq);
- list_add(&priv->list_field, &chnl_net_list);
}
@@ -389,14 +427,14 @@ 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);
+ if (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))
+ goto nla_put_failure;
loop = priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP;
- NLA_PUT_U8(skb, IFLA_CAIF_LOOPBACK, loop);
-
-
+ if (nla_put_u8(skb, IFLA_CAIF_LOOPBACK, loop))
+ goto nla_put_failure;
return 0;
nla_put_failure:
return -EMSGSIZE;
@@ -404,7 +442,7 @@ nla_put_failure:
}
static void caif_netlink_parms(struct nlattr *data[],
- struct caif_connect_request *conn_req)
+ struct caif_connect_request *conn_req)
{
if (!data) {
pr_warn("no params data found\n");
@@ -437,11 +475,19 @@ static int ipcaif_newlink(struct net *src_net, struct net_device *dev,
ret = register_netdevice(dev);
if (ret)
pr_warn("device rtml registration failed\n");
+ else
+ list_add(&caifdev->list_field, &chnl_net_list);
+
+ /* Use ifindex as connection id, and use loopback channel default. */
+ if (caifdev->conn_req.sockaddr.u.dgm.connection_id == UNDEF_CONNID) {
+ caifdev->conn_req.sockaddr.u.dgm.connection_id = dev->ifindex;
+ caifdev->conn_req.protocol = CAIFPROTO_DATAGRAM_LOOP;
+ }
return ret;
}
static int ipcaif_changelink(struct net_device *dev, struct nlattr *tb[],
- struct nlattr *data[])
+ struct nlattr *data[])
{
struct chnl_net *caifdev;
ASSERT_RTNL();