Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

53
net/caif/Kconfig Normal file
View file

@ -0,0 +1,53 @@
#
# CAIF net configurations
#
menuconfig CAIF
tristate "CAIF support"
select CRC_CCITT
default n
---help---
The "Communication CPU to Application CPU Interface" (CAIF) is a packet
based connection-oriented MUX protocol developed by ST-Ericsson for use
with its modems. It is accessed from user space as sockets (PF_CAIF).
Say Y (or M) here if you build for a phone product (e.g. Android or
MeeGo ) that uses CAIF as transport, if unsure say N.
If you select to build it as module then CAIF_NETDEV also needs to be
built as modules. You will also need to say yes to any CAIF physical
devices that your platform requires.
See Documentation/networking/caif for a further explanation on how to
use and configure CAIF.
config CAIF_DEBUG
bool "Enable Debug"
depends on CAIF
default n
---help---
Enable the inclusion of debug code in the CAIF stack.
Be aware that doing this will impact performance.
If unsure say N.
config CAIF_NETDEV
tristate "CAIF GPRS Network device"
depends on CAIF
default CAIF
---help---
Say Y if you will be using a CAIF based GPRS network device.
This can be either built-in or a loadable module,
If you select to build it as a built-in then the main CAIF device must
also be a built-in.
If unsure say Y.
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.

15
net/caif/Makefile Normal file
View file

@ -0,0 +1,15 @@
ccflags-$(CONFIG_CAIF_DEBUG) := -DDEBUG
caif-y := caif_dev.o \
cfcnfg.o cfmuxl.o cfctrl.o \
cffrml.o cfveil.o cfdbgl.o\
cfserl.o cfdgml.o \
cfrfml.o cfvidl.o cfutill.o \
cfsrvl.o cfpkt_skbuff.o
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

574
net/caif/caif_dev.c Normal file
View file

@ -0,0 +1,574 @@
/*
* CAIF Interface registration.
* Copyright (C) ST-Ericsson AB 2010
* Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*
* 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/kernel.h>
#include <linux/if_arp.h>
#include <linux/net.h>
#include <linux/netdevice.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_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");
/* Used for local tracking of the CAIF net devices */
struct caif_device_entry {
struct cflayer layer;
struct list_head list;
struct net_device *netdev;
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 */
struct mutex lock;
};
struct caif_net {
struct cfcnfg *cfg;
struct caif_device_entry_list caifdevs;
};
static int caif_net_id;
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;
caifn = net_generic(net, caif_net_id);
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 *caifd;
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;
dev_hold(dev);
return caifd;
}
static struct caif_device_entry *caif_get(struct net_device *dev)
{
struct caif_device_entry_list *caifdevs =
caif_device_list(dev_net(dev));
struct caif_device_entry *caifd;
list_for_each_entry_rcu(caifd, &caifdevs->list, list) {
if (caifd->netdev == dev)
return caifd;
}
return NULL;
}
static void caif_flow_cb(struct sk_buff *skb)
{
struct caif_device_entry *caifd;
void (*dtor)(struct sk_buff *skb) = NULL;
bool send_xoff;
WARN_ON(skb->dev == NULL);
rcu_read_lock();
caifd = caif_get(skb->dev);
WARN_ON(caifd == NULL);
if (caifd == NULL)
return;
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);
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;
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;
}
/*
* 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.
*/
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);
caifd->layer.up->ctrlcmd(caifd->layer.up,
_CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND,
caifd->layer.id);
noxoff:
rcu_read_unlock_bh();
err = dev_queue_xmit(skb);
if (err > 0)
err = -EIO;
return err;
}
/*
* 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 cfpkt *pkt;
struct caif_device_entry *caifd;
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 ||
!netif_oper_up(caifd->netdev)) {
rcu_read_unlock();
kfree_skb(skb);
return NET_RX_DROP;
}
/* 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 = {
.type = cpu_to_be16(ETH_P_CAIF),
.func = receive,
};
static void dev_flowctrl(struct net_device *dev, int on)
{
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 *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct caif_device_entry *caifd = NULL;
struct caif_dev_common *caifdev;
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));
caifd = caif_get(dev);
if (caifd == NULL && dev->type != ARPHRD_CAIF)
return 0;
switch (what) {
case NETDEV_REGISTER:
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;
break;
case NETDEV_UP:
rcu_read_lock();
caifd = caif_get(dev);
if (caifd == NULL) {
rcu_read_unlock();
break;
}
caifd->xoff = 0;
cfcnfg_set_phy_state(cfg, &caifd->layer, true);
rcu_read_unlock();
break;
case NETDEV_DOWN:
rcu_read_lock();
caifd = caif_get(dev);
if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) {
rcu_read_unlock();
return -EINVAL;
}
cfcnfg_set_phy_state(cfg, &caifd->layer, false);
caifd_hold(caifd);
rcu_read_unlock();
caifd->layer.up->ctrlcmd(caifd->layer.up,
_CAIF_CTRLCMD_PHYIF_DOWN_IND,
caifd->layer.id);
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) {
mutex_unlock(&caifdevs->lock);
break;
}
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;
}
static struct notifier_block caif_device_notifier = {
.notifier_call = caif_device_notify,
.priority = 0,
};
/* 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);
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 caif_device_entry *caifd, *tmp;
struct caif_device_entry_list *caifdevs =
caif_device_list(net);
struct cfcnfg *cfg = get_cfcnfg(net);
rtnl_lock();
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();
}
static struct pernet_operations caif_net_ops = {
.init = caif_init_net,
.exit = caif_exit_net,
.id = &caif_net_id,
.size = sizeof(struct caif_net),
};
/* Initialize Caif devices list */
static int __init caif_device_init(void)
{
int result;
result = register_pernet_subsys(&caif_net_ops);
if (result)
return result;
register_netdevice_notifier(&caif_device_notifier);
dev_add_pack(&caif_packet_type);
return result;
}
static void __exit caif_device_exit(void)
{
unregister_netdevice_notifier(&caif_device_notifier);
dev_remove_pack(&caif_packet_type);
unregister_pernet_subsys(&caif_net_ops);
}
module_init(caif_device_init);
module_exit(caif_device_exit);

1125
net/caif/caif_socket.c Normal file

File diff suppressed because it is too large Load diff

203
net/caif/caif_usb.c Normal file
View file

@ -0,0 +1,203 @@
/*
* 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)
return NULL;
caif_assert(offsetof(struct cfusbl, layer) == 0);
memset(&this->layer, 0, sizeof(this->layer));
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);

611
net/caif/cfcnfg.c Normal file
View file

@ -0,0 +1,611 @@
/*
* Copyright (C) ST-Ericsson AB 2010
* Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*/
#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
#include <linux/kernel.h>
#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>
#include <net/caif/cfctrl.h>
#include <net/caif/cfmuxl.h>
#include <net/caif/cffrml.h>
#include <net/caif/cfserl.h>
#include <net/caif/cfsrvl.h>
#include <net/caif/caif_dev.h>
#define container_obj(layr) container_of(layr, struct cfcnfg, layer)
/* Information about CAIF physical interfaces held by Config Module in order
* to manage physical interfaces
*/
struct cfcnfg_phyinfo {
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 */
struct cflayer *phy_layer;
/* Unique identifier of the physical interface */
unsigned int id;
/* Preference of the physical in interface */
enum cfcnfg_phy_preference pref;
/* Information about the physical device */
struct dev_info dev_info;
/* Interface index */
int ifindex;
/* Protocol head room added for CAIF link layer */
int head_room;
/* Use Start of frame checksum */
bool use_fcs;
};
struct cfcnfg {
struct cflayer layer;
struct cflayer *ctrl;
struct cflayer *mux;
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);
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);
static void cfctrl_resp_func(void);
static void cfctrl_enum_resp(void);
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)
return NULL;
this->mux = cfmuxl_create();
if (!this->mux)
goto out_of_mem;
this->ctrl = cfctrl_create();
if (!this->ctrl)
goto out_of_mem;
/* Initiate response functions */
resp = cfctrl_get_respfuncs(this->ctrl);
resp->enum_rsp = cfctrl_enum_resp;
resp->linkerror_ind = cfctrl_resp_func;
resp->linkdestroy_rsp = cfcnfg_linkdestroy_rsp;
resp->sleep_rsp = cfctrl_resp_func;
resp->wake_rsp = cfctrl_resp_func;
resp->restart_rsp = cfctrl_resp_func;
resp->radioset_rsp = cfctrl_resp_func;
resp->linksetup_rsp = cfcnfg_linkup_rsp;
resp->reject_rsp = cfcnfg_reject_rsp;
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:
synchronize_rcu();
kfree(this->mux);
kfree(this->ctrl);
kfree(this);
return NULL;
}
void cfcnfg_remove(struct cfcnfg *cfg)
{
might_sleep();
if (cfg) {
synchronize_rcu();
kfree(cfg->mux);
cfctrl_remove(cfg->ctrl);
kfree(cfg);
}
}
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)
{
}
static struct dev_info *cfcnfg_get_phyid(struct cfcnfg *cnfg,
enum cfcnfg_phy_preference phy_pref)
{
/* Try to match with specified preference */
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 */
list_for_each_entry_rcu(phy, &cnfg->phys, node)
if (phy->up)
return &phy->dev_info;
return NULL;
}
static int cfcnfg_get_id_from_ifi(struct cfcnfg *cnfg, int ifi)
{
struct cfcnfg_phyinfo *phy;
list_for_each_entry_rcu(phy, &cnfg->phys, node)
if (phy->ifindex == ifi && phy->up)
return phy->id;
return -ENODEV;
}
int caif_disconnect_client(struct net *net, struct cflayer *adap_layer)
{
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 (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 0;
}
EXPORT_SYMBOL(caif_disconnect_client);
static void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id)
{
}
static const int protohead[CFCTRL_SRV_MASK] = {
[CFCTRL_SRV_VEI] = 4,
[CFCTRL_SRV_DATAGRAM] = 7,
[CFCTRL_SRV_UTIL] = 4,
[CFCTRL_SRV_RFM] = 3,
[CFCTRL_SRV_DBG] = 3,
};
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");
goto unlock;
}
if (adap_layer->receive == NULL) {
pr_err("adap_layer->receive is NULL\n");
goto unlock;
}
if (adap_layer->ctrlcmd == NULL) {
pr_err("adap_layer->ctrlcmd == NULL\n");
goto unlock;
}
err = -ENODEV;
frml = phy->frm_layer;
if (frml == NULL) {
pr_err("Specified PHY type does not exist!\n");
goto unlock;
}
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(cfg->ctrl, param.phyid);
return cfctrl_linkup_request(cfg->ctrl, &param, adap_layer);
unlock:
rcu_read_unlock();
return err;
}
EXPORT_SYMBOL(caif_connect_client);
static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id,
struct cflayer *adapt_layer)
{
if (adapt_layer != NULL && adapt_layer->ctrlcmd != NULL)
adapt_layer->ctrlcmd(adapt_layer,
CAIF_CTRLCMD_INIT_FAIL_RSP, 0);
}
static void
cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv,
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");
cfctrl_linkdown_req(cnfg->ctrl, channel_id, NULL);
goto unlock;
}
caif_assert(cnfg != NULL);
caif_assert(phyid != 0);
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);
adapt_layer->id = channel_id;
switch (serv) {
case CFCTRL_SRV_VEI:
servicel = cfvei_create(channel_id, &phyinfo->dev_info);
break;
case CFCTRL_SRV_DATAGRAM:
servicel = cfdgml_create(channel_id,
&phyinfo->dev_info);
break;
case CFCTRL_SRV_RFM:
netdev = phyinfo->dev_info.dev;
servicel = cfrfml_create(channel_id, &phyinfo->dev_info,
netdev->mtu);
break;
case CFCTRL_SRV_UTIL:
servicel = cfutill_create(channel_id, &phyinfo->dev_info);
break;
case CFCTRL_SRV_VIDEO:
servicel = cfvidl_create(channel_id, &phyinfo->dev_info);
break;
case CFCTRL_SRV_DBG:
servicel = cfdbgl_create(channel_id, &phyinfo->dev_info);
break;
default:
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);
rcu_read_unlock();
servicel->ctrlcmd(servicel, CAIF_CTRLCMD_INIT_RSP, 0);
return;
unlock:
rcu_read_unlock();
}
void
cfcnfg_add_phy_layer(struct cfcnfg *cnfg,
struct net_device *dev, struct cflayer *phy_layer,
enum cfcnfg_phy_preference pref,
struct cflayer *link_support,
bool fcs, int head_room)
{
struct cflayer *frml;
struct cfcnfg_phyinfo *phyinfo = NULL;
int i;
u8 phyid;
mutex_lock(&cnfg->lock);
/* 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);
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 {
layer_set_dn(frml, phy_layer);
layer_set_up(phy_layer, frml);
}
list_add_rcu(&phyinfo->node, &cnfg->phys);
out:
mutex_unlock(&cnfg->lock);
return;
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;
}
if (phyinfo->up == up) {
rcu_read_unlock();
return 0;
}
phyinfo->up = up;
if (up) {
cffrml_hold(phyinfo->frm_layer);
cfmuxl_set_dnlayer(cnfg->mux, phyinfo->frm_layer,
phy_layer->id);
} else {
cfmuxl_remove_dnlayer(cnfg->mux, phy_layer->id);
cffrml_put(phyinfo->frm_layer);
}
rcu_read_unlock();
return 0;
}
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;
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(phyinfo->frm_layer->id == phyid);
list_del_rcu(&phyinfo->node);
synchronize_rcu();
/* 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);
if (phy_layer != frml_dn) {
layer_set_up(frml_dn, NULL);
layer_set_dn(frml_dn, NULL);
}
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);

641
net/caif/cfctrl.c Normal file
View file

@ -0,0 +1,641 @@
/*
* Copyright (C) ST-Ericsson AB 2010
* Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*/
#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
#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>
#define container_obj(layr) container_of(layr, struct cfctrl, serv.layer)
#define UTILITY_NAME_LENGTH 16
#define CFPKT_CTRL_PKT_LEN 20
#ifdef CAIF_NO_LOOP
static int handle_loop(struct cfctrl *ctrl,
int cmd, struct cfpkt *pkt){
return -1;
}
#else
static int handle_loop(struct cfctrl *ctrl,
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,
int phyid);
struct cflayer *cfctrl_create(void)
{
struct dev_info dev_info;
struct cfctrl *this =
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;
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);
return &this->serv.layer;
}
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 &&
p1->priority == p2->priority &&
p1->phyid == p2->phyid &&
p1->endpoint == p2->endpoint && p1->chtype == p2->chtype;
if (!eq)
return false;
switch (p1->linktype) {
case CFCTRL_SRV_VEI:
return true;
case CFCTRL_SRV_DATAGRAM:
return p1->u.datagram.connid == p2->u.datagram.connid;
case CFCTRL_SRV_RFM:
return
p1->u.rfm.connid == p2->u.rfm.connid &&
strcmp(p1->u.rfm.volume, p2->u.rfm.volume) == 0;
case CFCTRL_SRV_UTIL:
return
p1->u.utility.fifosize_kb == p2->u.utility.fifosize_kb
&& p1->u.utility.fifosize_bufs ==
p2->u.utility.fifosize_bufs
&& strcmp(p1->u.utility.name, p2->u.utility.name) == 0
&& p1->u.utility.paramlen == p2->u.utility.paramlen
&& memcmp(p1->u.utility.params, p2->u.utility.params,
p1->u.utility.paramlen) == 0;
case CFCTRL_SRV_VIDEO:
return p1->u.video.connid == p2->u.video.connid;
case CFCTRL_SRV_DBG:
return true;
case CFCTRL_SRV_DECM:
return false;
default:
return false;
}
return false;
}
static bool cfctrl_req_eq(const struct cfctrl_request_info *r1,
const struct cfctrl_request_info *r2)
{
if (r1->cmd != r2->cmd)
return false;
if (r1->cmd == CFCTRL_CMD_LINK_SETUP)
return param_eq(&r1->param, &r2->param);
else
return r1->channel_id == r2->channel_id;
}
/* Insert request at the end */
static void cfctrl_insert_req(struct cfctrl *ctrl,
struct cfctrl_request_info *req)
{
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_bh(&ctrl->info_list_lock);
}
/* Compare and remove request */
static struct cfctrl_request_info *cfctrl_remove_req(struct cfctrl *ctrl,
struct cfctrl_request_info *req)
{
struct cfctrl_request_info *p, *tmp, *first;
first = list_first_entry(&ctrl->list, struct cfctrl_request_info, list);
list_for_each_entry_safe(p, tmp, &ctrl->list, list) {
if (cfctrl_req_eq(req, p)) {
if (p != first)
pr_warn("Requests are not received in order\n");
atomic_set(&ctrl->rsp_seq_no,
p->sequence_no);
list_del(&p->list);
goto out;
}
}
p = NULL;
out:
return p;
}
struct cfctrl_rsp *cfctrl_get_respfuncs(struct cflayer *layer)
{
struct cfctrl *this = container_obj(layer);
return &this->res;
}
static void init_info(struct caif_payload_info *info, struct cfctrl *cfctrl)
{
info->hdr_len = 0;
info->channel_id = cfctrl->serv.layer.id;
info->dev_info = &cfctrl->serv.dev_info;
}
void cfctrl_enum_req(struct cflayer *layer, u8 physlinkid)
{
struct cfpkt *pkt;
struct cfctrl *cfctrl = container_obj(layer);
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);
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 *cfctrl = container_obj(layer);
u32 tmp32;
u16 tmp16;
u8 tmp8;
struct cfctrl_request_info *req;
int ret;
char utility_name[16];
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->endpoint & 0x03);
switch (param->linktype) {
case CFCTRL_SRV_VEI:
break;
case CFCTRL_SRV_VIDEO:
cfpkt_addbdy(pkt, (u8) param->u.video.connid);
break;
case CFCTRL_SRV_DBG:
break;
case CFCTRL_SRV_DATAGRAM:
tmp32 = cpu_to_le32(param->u.datagram.connid);
cfpkt_add_body(pkt, &tmp32, 4);
break;
case CFCTRL_SRV_RFM:
/* Construct a frame, convert DatagramConnectionID to network
* format long and copy it out...
*/
tmp32 = cpu_to_le32(param->u.rfm.connid);
cfpkt_add_body(pkt, &tmp32, 4);
/* Add volume name, including zero termination... */
cfpkt_add_body(pkt, param->u.rfm.volume,
strlen(param->u.rfm.volume) + 1);
break;
case CFCTRL_SRV_UTIL:
tmp16 = cpu_to_le16(param->u.utility.fifosize_kb);
cfpkt_add_body(pkt, &tmp16, 2);
tmp16 = cpu_to_le16(param->u.utility.fifosize_bufs);
cfpkt_add_body(pkt, &tmp16, 2);
memset(utility_name, 0, sizeof(utility_name));
strncpy(utility_name, param->u.utility.name,
UTILITY_NAME_LENGTH - 1);
cfpkt_add_body(pkt, utility_name, UTILITY_NAME_LENGTH);
tmp8 = param->u.utility.paramlen;
cfpkt_add_body(pkt, &tmp8, 1);
cfpkt_add_body(pkt, param->u.utility.params,
param->u.utility.paramlen);
break;
default:
pr_warn("Request setup of bad link type = %d\n",
param->linktype);
return -EINVAL;
}
req = kzalloc(sizeof(*req), GFP_KERNEL);
if (!req)
return -ENOMEM;
req->client_layer = user_layer;
req->cmd = CFCTRL_CMD_LINK_SETUP;
req->param = *param;
cfctrl_insert_req(cfctrl, req);
init_info(cfpkt_info(pkt), cfctrl);
/*
* NOTE:Always send linkup and linkdown request on the same
* device as the payload. Otherwise old queued up payload
* might arrive with the newly allocated channel ID.
*/
cfpkt_info(pkt)->dev_info->id = param->phyid;
cfpkt_set_prio(pkt, TC_PRIO_CONTROL);
ret =
dn->transmit(dn, pkt);
if (ret < 0) {
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)
{
int ret;
struct cfpkt *pkt;
struct cfctrl *cfctrl = container_obj(layer);
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 =
dn->transmit(dn, pkt);
#ifndef CAIF_NO_LOOP
cfctrl->loop_linkused[channelid] = 0;
#endif
return ret;
}
int cfctrl_cancel_req(struct cflayer *layr, struct cflayer *adap_layer)
{
struct cfctrl_request_info *p, *tmp;
struct cfctrl *ctrl = container_obj(layr);
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) {
list_del(&p->list);
kfree(p);
found++;
}
}
spin_unlock_bh(&ctrl->info_list_lock);
return found;
}
static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt)
{
u8 cmdrsp;
u8 cmd;
int ret = -1;
u16 tmp16;
u8 len;
u8 param[255];
u8 linkid;
struct cfctrl *cfctrl = container_obj(layer);
struct cfctrl_request_info rsp, *req;
cfpkt_extr_head(pkt, &cmdrsp, 1);
cmd = cmdrsp & CFCTRL_CMD_MASK;
if (cmd != CFCTRL_CMD_LINK_ERR
&& CFCTRL_RSP_BIT != (CFCTRL_RSP_BIT & cmdrsp)
&& CFCTRL_ERR_BIT != (CFCTRL_ERR_BIT & cmdrsp)) {
if (handle_loop(cfctrl, cmd, pkt) != 0)
cmdrsp |= CFCTRL_ERR_BIT;
}
switch (cmd) {
case CFCTRL_CMD_LINK_SETUP:
{
enum cfctrl_srv serv;
enum cfctrl_srv servtype;
u8 endpoint;
u8 physlinkid;
u8 prio;
u8 tmp;
u32 tmp32;
u8 *cp;
int i;
struct cfctrl_link_param linkparam;
memset(&linkparam, 0, sizeof(linkparam));
cfpkt_extr_head(pkt, &tmp, 1);
serv = tmp & CFCTRL_SRV_MASK;
linkparam.linktype = serv;
servtype = tmp >> 4;
linkparam.chtype = servtype;
cfpkt_extr_head(pkt, &tmp, 1);
physlinkid = tmp & 0x07;
prio = tmp >> 3;
linkparam.priority = prio;
linkparam.phyid = physlinkid;
cfpkt_extr_head(pkt, &endpoint, 1);
linkparam.endpoint = endpoint & 0x03;
switch (serv) {
case CFCTRL_SRV_VEI:
case CFCTRL_SRV_DBG:
if (CFCTRL_ERR_BIT & cmdrsp)
break;
/* Link ID */
cfpkt_extr_head(pkt, &linkid, 1);
break;
case CFCTRL_SRV_VIDEO:
cfpkt_extr_head(pkt, &tmp, 1);
linkparam.u.video.connid = tmp;
if (CFCTRL_ERR_BIT & cmdrsp)
break;
/* Link ID */
cfpkt_extr_head(pkt, &linkid, 1);
break;
case CFCTRL_SRV_DATAGRAM:
cfpkt_extr_head(pkt, &tmp32, 4);
linkparam.u.datagram.connid =
le32_to_cpu(tmp32);
if (CFCTRL_ERR_BIT & cmdrsp)
break;
/* Link ID */
cfpkt_extr_head(pkt, &linkid, 1);
break;
case CFCTRL_SRV_RFM:
/* Construct a frame, convert
* DatagramConnectionID
* to network format long and copy it out...
*/
cfpkt_extr_head(pkt, &tmp32, 4);
linkparam.u.rfm.connid =
le32_to_cpu(tmp32);
cp = (u8 *) linkparam.u.rfm.volume;
for (cfpkt_extr_head(pkt, &tmp, 1);
cfpkt_more(pkt) && tmp != '\0';
cfpkt_extr_head(pkt, &tmp, 1))
*cp++ = tmp;
*cp = '\0';
if (CFCTRL_ERR_BIT & cmdrsp)
break;
/* Link ID */
cfpkt_extr_head(pkt, &linkid, 1);
break;
case CFCTRL_SRV_UTIL:
/* Construct a frame, convert
* DatagramConnectionID
* to network format long and copy it out...
*/
/* Fifosize KB */
cfpkt_extr_head(pkt, &tmp16, 2);
linkparam.u.utility.fifosize_kb =
le16_to_cpu(tmp16);
/* Fifosize bufs */
cfpkt_extr_head(pkt, &tmp16, 2);
linkparam.u.utility.fifosize_bufs =
le16_to_cpu(tmp16);
/* name */
cp = (u8 *) linkparam.u.utility.name;
caif_assert(sizeof(linkparam.u.utility.name)
>= UTILITY_NAME_LENGTH);
for (i = 0;
i < UTILITY_NAME_LENGTH
&& cfpkt_more(pkt); i++) {
cfpkt_extr_head(pkt, &tmp, 1);
*cp++ = tmp;
}
/* Length */
cfpkt_extr_head(pkt, &len, 1);
linkparam.u.utility.paramlen = len;
/* Param Data */
cp = linkparam.u.utility.params;
while (cfpkt_more(pkt) && len--) {
cfpkt_extr_head(pkt, &tmp, 1);
*cp++ = tmp;
}
if (CFCTRL_ERR_BIT & cmdrsp)
break;
/* Link ID */
cfpkt_extr_head(pkt, &linkid, 1);
/* Length */
cfpkt_extr_head(pkt, &len, 1);
/* Param Data */
cfpkt_extr_head(pkt, &param, len);
break;
default:
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");
cfctrl->res.reject_rsp(cfctrl->serv.layer.up,
0,
req ? req->client_layer
: NULL);
} else {
cfctrl->res.linksetup_rsp(cfctrl->serv.
layer.up, linkid,
serv, physlinkid,
req ? req->
client_layer : NULL);
}
kfree(req);
spin_unlock_bh(&cfctrl->info_list_lock);
}
break;
case CFCTRL_CMD_LINK_DESTROY:
cfpkt_extr_head(pkt, &linkid, 1);
cfctrl->res.linkdestroy_rsp(cfctrl->serv.layer.up, linkid);
break;
case CFCTRL_CMD_LINK_ERR:
pr_err("Frame Error Indication received\n");
cfctrl->res.linkerror_ind();
break;
case CFCTRL_CMD_ENUM:
cfctrl->res.enum_rsp();
break;
case CFCTRL_CMD_SLEEP:
cfctrl->res.sleep_rsp();
break;
case CFCTRL_CMD_WAKE:
cfctrl->res.wake_rsp();
break;
case CFCTRL_CMD_LINK_RECONF:
cfctrl->res.restart_rsp();
break;
case CFCTRL_CMD_RADIO_SET:
cfctrl->res.radioset_rsp();
break;
default:
pr_err("Unrecognized Control Frame\n");
goto error;
}
ret = 0;
error:
cfpkt_destroy(pkt);
return ret;
}
static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
int phyid)
{
struct cfctrl *this = container_obj(layr);
switch (ctrl) {
case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND:
case CAIF_CTRLCMD_FLOW_OFF_IND:
spin_lock_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_bh(&this->info_list_lock);
break;
}
default:
break;
}
}
#ifndef CAIF_NO_LOOP
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_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_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_bh(&ctrl->loop_linkid_lock);
cfpkt_peek_head(pkt, &linktype, 1);
if (linktype == CFCTRL_SRV_UTIL) {
tmp = 0x01;
cfpkt_add_trail(pkt, &tmp, 1);
cfpkt_add_trail(pkt, &tmp, 1);
}
break;
case CFCTRL_CMD_LINK_DESTROY:
spin_lock_bh(&ctrl->loop_linkid_lock);
cfpkt_peek_head(pkt, &linkid, 1);
ctrl->loop_linkused[linkid] = 0;
spin_unlock_bh(&ctrl->loop_linkid_lock);
break;
default:
break;
}
return 0;
}
#endif

55
net/caif/cfdbgl.c Normal file
View file

@ -0,0 +1,55 @@
/*
* Copyright (C) ST-Ericsson AB 2010
* Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*/
#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
#include <linux/stddef.h>
#include <linux/slab.h>
#include <net/caif/caif_layer.h>
#include <net/caif/cfsrvl.h>
#include <net/caif/cfpkt.h>
#define container_obj(layr) ((struct cfsrvl *) layr)
static int cfdbgl_receive(struct cflayer *layr, struct cfpkt *pkt);
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 = kzalloc(sizeof(struct cfsrvl), GFP_ATOMIC);
if (!dbg)
return NULL;
caif_assert(offsetof(struct cfsrvl, layer) == 0);
cfsrvl_init(dbg, channel_id, dev_info, false);
dbg->layer.receive = cfdbgl_receive;
dbg->layer.transmit = cfdbgl_transmit;
snprintf(dbg->layer.name, CAIF_LAYER_NAME_SZ - 1, "dbg%d", channel_id);
return &dbg->layer;
}
static int cfdbgl_receive(struct cflayer *layr, struct cfpkt *pkt)
{
return layr->up->receive(layr->up, pkt);
}
static int cfdbgl_transmit(struct cflayer *layr, struct cfpkt *pkt)
{
struct cfsrvl *service = container_obj(layr);
struct caif_payload_info *info;
int 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);
info->channel_id = service->layer.id;
info->dev_info = &service->dev_info;
return layr->dn->transmit(layr->dn, pkt);
}

114
net/caif/cfdgml.c Normal file
View file

@ -0,0 +1,114 @@
/*
* Copyright (C) ST-Ericsson AB 2010
* Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*/
#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
#include <linux/stddef.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <net/caif/caif_layer.h>
#include <net/caif/cfsrvl.h>
#include <net/caif/cfpkt.h>
#define container_obj(layr) ((struct cfsrvl *) layr)
#define DGM_CMD_BIT 0x80
#define DGM_FLOW_OFF 0x81
#define DGM_FLOW_ON 0x80
#define DGM_MTU 1500
static int cfdgml_receive(struct cflayer *layr, struct cfpkt *pkt);
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 = kzalloc(sizeof(struct cfsrvl), GFP_ATOMIC);
if (!dgm)
return NULL;
caif_assert(offsetof(struct cfsrvl, layer) == 0);
cfsrvl_init(dgm, channel_id, dev_info, true);
dgm->layer.receive = cfdgml_receive;
dgm->layer.transmit = cfdgml_transmit;
snprintf(dgm->layer.name, CAIF_LAYER_NAME_SZ - 1, "dgm%d", channel_id);
dgm->layer.name[CAIF_LAYER_NAME_SZ - 1] = '\0';
return &dgm->layer;
}
static int cfdgml_receive(struct cflayer *layr, struct cfpkt *pkt)
{
u8 cmd = -1;
u8 dgmhdr[3];
int ret;
caif_assert(layr->up != NULL);
caif_assert(layr->receive != NULL);
caif_assert(layr->ctrlcmd != NULL);
if (cfpkt_extr_head(pkt, &cmd, 1) < 0) {
pr_err("Packet is erroneous!\n");
cfpkt_destroy(pkt);
return -EPROTO;
}
if ((cmd & DGM_CMD_BIT) == 0) {
if (cfpkt_extr_head(pkt, &dgmhdr, 3) < 0) {
pr_err("Packet is erroneous!\n");
cfpkt_destroy(pkt);
return -EPROTO;
}
ret = layr->up->receive(layr->up, pkt);
return ret;
}
switch (cmd) {
case DGM_FLOW_OFF: /* FLOW OFF */
layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_OFF_IND, 0);
cfpkt_destroy(pkt);
return 0;
case DGM_FLOW_ON: /* FLOW ON */
layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_ON_IND, 0);
cfpkt_destroy(pkt);
return 0;
default:
cfpkt_destroy(pkt);
pr_info("Unknown datagram control %d (0x%x)\n", cmd, cmd);
return -EPROTO;
}
}
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)) {
cfpkt_destroy(pkt);
return ret;
}
/* STE Modem cannot handle more than 1500 bytes datagrams */
if (cfpkt_getlen(pkt) > DGM_MTU) {
cfpkt_destroy(pkt);
return -EMSGSIZE;
}
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);
info->channel_id = service->layer.id;
/* To optimize alignment, we add up the size of CAIF header
* before payload.
*/
info->hdr_len = 4;
info->dev_info = &service->dev_info;
return layr->dn->transmit(layr->dn, pkt);
}

197
net/caif/cffrml.c Normal file
View file

@ -0,0 +1,197 @@
/*
* CAIF Framing Layer.
*
* Copyright (C) ST-Ericsson AB 2010
* Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*/
#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
#include <linux/stddef.h>
#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>
#define container_obj(layr) container_of(layr, struct cffrml, layer)
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);
static u32 cffrml_rcv_error;
static u32 cffrml_rcv_checsum_error;
struct cflayer *cffrml_create(u16 phyid, bool use_fcs)
{
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);
this->layer.receive = cffrml_receive;
this->layer.transmit = cffrml_transmit;
this->layer.ctrlcmd = cffrml_ctrlcmd;
snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "frm%d", phyid);
this->dofcs = use_fcs;
this->layer.id = phyid;
return (struct 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;
}
void cffrml_set_dnlayer(struct cflayer *this, struct cflayer *dn)
{
this->dn = dn;
}
static u16 cffrml_checksum(u16 chks, void *buf, u16 len)
{
/* FIXME: FCS should be moved to glue in order to use OS-Specific
* solutions
*/
return crc_ccitt(chks, buf, len);
}
static int cffrml_receive(struct cflayer *layr, struct cfpkt *pkt)
{
u16 tmp;
u16 len;
u16 hdrchks;
u16 pktchks;
struct cffrml *this;
this = container_obj(layr);
cfpkt_extr_head(pkt, &tmp, 2);
len = le16_to_cpu(tmp);
/* Subtract for FCS on length if FCS is not used. */
if (!this->dofcs)
len -= 2;
if (cfpkt_setlen(pkt, len) < 0) {
++cffrml_rcv_error;
pr_err("Framing length error (%d)\n", len);
cfpkt_destroy(pkt);
return -EPROTO;
}
/*
* Don't do extract if FCS is false, rather do setlen - then we don't
* get a cache-miss.
*/
if (this->dofcs) {
cfpkt_extr_trail(pkt, &tmp, 2);
hdrchks = le16_to_cpu(tmp);
pktchks = cfpkt_iterate(pkt, cffrml_checksum, 0xffff);
if (pktchks != hdrchks) {
cfpkt_add_trail(pkt, &tmp, 2);
++cffrml_rcv_error;
++cffrml_rcv_checsum_error;
pr_info("Frame checksum error (0x%x != 0x%x)\n",
hdrchks, pktchks);
return -EILSEQ;
}
}
if (cfpkt_erroneous(pkt)) {
++cffrml_rcv_error;
pr_err("Packet is erroneous!\n");
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)
{
u16 chks;
u16 len;
__le16 data;
struct cffrml *this = container_obj(layr);
if (this->dofcs) {
chks = cfpkt_iterate(pkt, cffrml_checksum, 0xffff);
data = cpu_to_le16(chks);
cfpkt_add_trail(pkt, &data, 2);
} else {
cfpkt_pad_trail(pkt, 2);
}
len = cfpkt_getlen(pkt);
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;
}
if (layr->dn == NULL) {
cfpkt_destroy(pkt);
return -ENODEV;
}
return layr->dn->transmit(layr->dn, pkt);
}
static void cffrml_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
int phyid)
{
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;
}

267
net/caif/cfmuxl.c Normal file
View file

@ -0,0 +1,267 @@
/*
* Copyright (C) ST-Ericsson AB 2010
* Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*/
#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
#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>
#include <net/caif/cffrml.h>
#define container_obj(layr) container_of(layr, struct cfmuxl, layer)
#define CAIF_CTRL_CHANNEL 0
#define UP_CACHE_SIZE 8
#define DN_CACHE_SIZE 8
struct cfmuxl {
struct cflayer layer;
struct list_head srvl_list;
struct list_head frml_list;
struct cflayer *up_cache[UP_CACHE_SIZE];
struct cflayer *dn_cache[DN_CACHE_SIZE];
/*
* Set when inserting or removing downwards layers.
*/
spinlock_t transmit_lock;
/*
* Set when inserting or removing upwards layers.
*/
spinlock_t receive_lock;
};
static int cfmuxl_receive(struct 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);
static struct cflayer *get_up(struct cfmuxl *muxl, u16 id);
struct cflayer *cfmuxl_create(void)
{
struct cfmuxl *this = kzalloc(sizeof(struct cfmuxl), GFP_ATOMIC);
if (!this)
return NULL;
this->layer.receive = cfmuxl_receive;
this->layer.transmit = cfmuxl_transmit;
this->layer.ctrlcmd = cfmuxl_ctrlcmd;
INIT_LIST_HEAD(&this->srvl_list);
INIT_LIST_HEAD(&this->frml_list);
spin_lock_init(&this->transmit_lock);
spin_lock_init(&this->receive_lock);
snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "mux");
return &this->layer;
}
int cfmuxl_set_dnlayer(struct cflayer *layr, struct cflayer *dn, u8 phyid)
{
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;
}
static struct cflayer *get_from_id(struct list_head *list, u16 id)
{
struct cflayer *lyr;
list_for_each_entry_rcu(lyr, list, node) {
if (lyr->id == id)
return lyr;
}
return NULL;
}
int cfmuxl_set_uplayer(struct cflayer *layr, struct cflayer *up, u8 linkid)
{
struct cfmuxl *muxl = container_obj(layr);
struct cflayer *old;
spin_lock_bh(&muxl->receive_lock);
/* 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;
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)
goto out;
list_del_rcu(&dn->node);
caif_assert(dn != NULL);
out:
spin_unlock_bh(&muxl->transmit_lock);
return dn;
}
static struct cflayer *get_up(struct cfmuxl *muxl, u16 id)
{
struct cflayer *up;
int idx = id % UP_CACHE_SIZE;
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);
rcu_assign_pointer(muxl->up_cache[idx], up);
spin_unlock_bh(&muxl->receive_lock);
}
return up;
}
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 = 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);
rcu_assign_pointer(muxl->dn_cache[idx], dn);
spin_unlock_bh(&muxl->transmit_lock);
}
return dn;
}
struct cflayer *cfmuxl_remove_uplayer(struct cflayer *layr, u8 id)
{
struct cflayer *up;
struct cfmuxl *muxl = container_obj(layr);
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;
RCU_INIT_POINTER(muxl->up_cache[idx], NULL);
list_del_rcu(&up->node);
out:
spin_unlock_bh(&muxl->receive_lock);
return up;
}
static int cfmuxl_receive(struct cflayer *layr, struct cfpkt *pkt)
{
int ret;
struct cfmuxl *muxl = container_obj(layr);
u8 id;
struct cflayer *up;
if (cfpkt_extr_head(pkt, &id, 1) < 0) {
pr_err("erroneous Caif Packet\n");
cfpkt_destroy(pkt);
return -EPROTO;
}
rcu_read_lock();
up = get_up(muxl, id);
if (up == NULL) {
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)
{
struct cfmuxl *muxl = container_obj(layr);
int err;
u8 linkid;
struct cflayer *dn;
struct caif_payload_info *info = cfpkt_info(pkt);
BUG_ON(!info);
rcu_read_lock();
dn = get_dn(muxl, info->dev_info);
if (dn == NULL) {
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);
/* 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)
{
struct cfmuxl *muxl = container_obj(layr);
struct cflayer *layer;
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();
}

393
net/caif/cfpkt_skbuff.c Normal file
View file

@ -0,0 +1,393 @@
/*
* Copyright (C) ST-Ericsson AB 2010
* Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*/
#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
#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
#define PKT_POSTFIX 2
#define PKT_LEN_WHEN_EXTENDING 128
#define PKT_ERROR(pkt, errmsg) \
do { \
cfpkt_priv(pkt)->erronous = true; \
skb_reset_tail_pointer(&pkt->skb); \
pr_warn(errmsg); \
} while (0)
struct cfpktq {
struct sk_buff_head head;
atomic_t count;
/* Lock protects count updates */
spinlock_t lock;
};
/*
* net/caif/ is generic and does not
* understand SKB, so we do this typecast
*/
struct cfpkt {
struct sk_buff skb;
};
/* Private data inside SKB */
struct cfpkt_priv_data {
struct dev_info dev_info;
bool erronous;
};
static inline struct cfpkt_priv_data *cfpkt_priv(struct cfpkt *pkt)
{
return (struct cfpkt_priv_data *) pkt->skb.cb;
}
static inline bool is_erronous(struct cfpkt *pkt)
{
return cfpkt_priv(pkt)->erronous;
}
static inline struct sk_buff *pkt_to_skb(struct cfpkt *pkt)
{
return &pkt->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);
cfpkt_priv(pkt)->erronous = false;
return pkt;
}
EXPORT_SYMBOL(cfpkt_fromnative);
void *cfpkt_tonative(struct cfpkt *pkt)
{
return (void *) pkt;
}
EXPORT_SYMBOL(cfpkt_tonative);
static struct cfpkt *cfpkt_create_pfx(u16 len, u16 pfx)
{
struct sk_buff *skb;
if (likely(in_interrupt()))
skb = alloc_skb(len + pfx, GFP_ATOMIC);
else
skb = alloc_skb(len + pfx, GFP_KERNEL);
if (unlikely(skb == NULL))
return NULL;
skb_reserve(skb, pfx);
return skb_to_pkt(skb);
}
inline struct cfpkt *cfpkt_create(u16 len)
{
return cfpkt_create_pfx(len + PKT_POSTFIX, PKT_PREFIX);
}
void cfpkt_destroy(struct cfpkt *pkt)
{
struct sk_buff *skb = pkt_to_skb(pkt);
kfree_skb(skb);
}
inline bool cfpkt_more(struct cfpkt *pkt)
{
struct sk_buff *skb = pkt_to_skb(pkt);
return skb->len > 0;
}
int cfpkt_peek_head(struct cfpkt *pkt, void *data, u16 len)
{
struct sk_buff *skb = pkt_to_skb(pkt);
if (skb_headlen(skb) >= len) {
memcpy(data, skb->data, len);
return 0;
}
return !cfpkt_extr_head(pkt, data, len) &&
!cfpkt_add_head(pkt, data, len);
}
int cfpkt_extr_head(struct cfpkt *pkt, void *data, u16 len)
{
struct sk_buff *skb = pkt_to_skb(pkt);
u8 *from;
if (unlikely(is_erronous(pkt)))
return -EPROTO;
if (unlikely(len > skb->len)) {
PKT_ERROR(pkt, "read beyond end of packet\n");
return -EPROTO;
}
if (unlikely(len > skb_headlen(skb))) {
if (unlikely(skb_linearize(skb) != 0)) {
PKT_ERROR(pkt, "linearize failed\n");
return -EPROTO;
}
}
from = skb_pull(skb, len);
from -= len;
if (data)
memcpy(data, from, len);
return 0;
}
EXPORT_SYMBOL(cfpkt_extr_head);
int cfpkt_extr_trail(struct cfpkt *pkt, void *dta, u16 len)
{
struct sk_buff *skb = pkt_to_skb(pkt);
u8 *data = dta;
u8 *from;
if (unlikely(is_erronous(pkt)))
return -EPROTO;
if (unlikely(skb_linearize(skb) != 0)) {
PKT_ERROR(pkt, "linearize failed\n");
return -EPROTO;
}
if (unlikely(skb->data + len > skb_tail_pointer(skb))) {
PKT_ERROR(pkt, "read beyond end of packet\n");
return -EPROTO;
}
from = skb_tail_pointer(skb) - len;
skb_trim(skb, skb->len - len);
memcpy(data, from, len);
return 0;
}
int cfpkt_pad_trail(struct cfpkt *pkt, u16 len)
{
return cfpkt_add_body(pkt, NULL, len);
}
int cfpkt_add_body(struct cfpkt *pkt, const void *data, u16 len)
{
struct sk_buff *skb = pkt_to_skb(pkt);
struct sk_buff *lastskb;
u8 *to;
u16 addlen = 0;
if (unlikely(is_erronous(pkt)))
return -EPROTO;
lastskb = skb;
/* Check whether we need to add space at the tail */
if (unlikely(skb_tailroom(skb) < len)) {
if (likely(len < PKT_LEN_WHEN_EXTENDING))
addlen = PKT_LEN_WHEN_EXTENDING;
else
addlen = len;
}
/* Check whether we need to change the SKB before writing to the tail */
if (unlikely((addlen > 0) || skb_cloned(skb) || skb_shared(skb))) {
/* Make sure data is writable */
if (unlikely(skb_cow_data(skb, addlen, &lastskb) < 0)) {
PKT_ERROR(pkt, "cow failed\n");
return -EPROTO;
}
}
/* All set to put the last SKB and optionally write data there. */
to = pskb_put(skb, lastskb, len);
if (likely(data))
memcpy(to, data, len);
return 0;
}
inline int cfpkt_addbdy(struct cfpkt *pkt, u8 data)
{
return cfpkt_add_body(pkt, &data, 1);
}
int cfpkt_add_head(struct cfpkt *pkt, const void *data2, u16 len)
{
struct sk_buff *skb = pkt_to_skb(pkt);
struct sk_buff *lastskb;
u8 *to;
const u8 *data = data2;
int ret;
if (unlikely(is_erronous(pkt)))
return -EPROTO;
if (unlikely(skb_headroom(skb) < len)) {
PKT_ERROR(pkt, "no headroom\n");
return -EPROTO;
}
/* Make sure data is writable */
ret = skb_cow_data(skb, 0, &lastskb);
if (unlikely(ret < 0)) {
PKT_ERROR(pkt, "cow failed\n");
return ret;
}
to = skb_push(skb, len);
memcpy(to, data, len);
return 0;
}
EXPORT_SYMBOL(cfpkt_add_head);
inline int cfpkt_add_trail(struct cfpkt *pkt, const void *data, u16 len)
{
return cfpkt_add_body(pkt, data, len);
}
inline u16 cfpkt_getlen(struct cfpkt *pkt)
{
struct sk_buff *skb = pkt_to_skb(pkt);
return skb->len;
}
inline u16 cfpkt_iterate(struct cfpkt *pkt,
u16 (*iter_func)(u16, void *, u16),
u16 data)
{
/*
* Don't care about the performance hit of linearizing,
* Checksum should not be used on high-speed interfaces anyway.
*/
if (unlikely(is_erronous(pkt)))
return -EPROTO;
if (unlikely(skb_linearize(&pkt->skb) != 0)) {
PKT_ERROR(pkt, "linearize failed\n");
return -EPROTO;
}
return iter_func(data, pkt->skb.data, cfpkt_getlen(pkt));
}
int cfpkt_setlen(struct cfpkt *pkt, u16 len)
{
struct sk_buff *skb = pkt_to_skb(pkt);
if (unlikely(is_erronous(pkt)))
return -EPROTO;
if (likely(len <= skb->len)) {
if (unlikely(skb->data_len))
___pskb_trim(skb, len);
else
skb_trim(skb, len);
return cfpkt_getlen(pkt);
}
/* Need to expand SKB */
if (unlikely(!cfpkt_pad_trail(pkt, len - skb->len)))
PKT_ERROR(pkt, "skb_pad_trail failed\n");
return cfpkt_getlen(pkt);
}
struct cfpkt *cfpkt_append(struct cfpkt *dstpkt,
struct cfpkt *addpkt,
u16 expectlen)
{
struct sk_buff *dst = pkt_to_skb(dstpkt);
struct sk_buff *add = pkt_to_skb(addpkt);
u16 addlen = skb_headlen(add);
u16 neededtailspace;
struct sk_buff *tmp;
u16 dstlen;
u16 createlen;
if (unlikely(is_erronous(dstpkt) || is_erronous(addpkt))) {
return dstpkt;
}
if (expectlen > addlen)
neededtailspace = expectlen;
else
neededtailspace = addlen;
if (dst->tail + neededtailspace > dst->end) {
/* Create a dumplicate of 'dst' with more tail space */
struct cfpkt *tmppkt;
dstlen = skb_headlen(dst);
createlen = dstlen + neededtailspace;
tmppkt = cfpkt_create(createlen + PKT_PREFIX + PKT_POSTFIX);
if (tmppkt == NULL)
return NULL;
tmp = pkt_to_skb(tmppkt);
skb_set_tail_pointer(tmp, dstlen);
tmp->len = dstlen;
memcpy(tmp->data, dst->data, dstlen);
cfpkt_destroy(dstpkt);
dst = tmp;
}
memcpy(skb_tail_pointer(dst), add->data, skb_headlen(add));
cfpkt_destroy(addpkt);
dst->tail += addlen;
dst->len += addlen;
return skb_to_pkt(dst);
}
struct cfpkt *cfpkt_split(struct cfpkt *pkt, u16 pos)
{
struct sk_buff *skb2;
struct sk_buff *skb = pkt_to_skb(pkt);
struct cfpkt *tmppkt;
u8 *split = skb->data + pos;
u16 len2nd = skb_tail_pointer(skb) - split;
if (unlikely(is_erronous(pkt)))
return NULL;
if (skb->data + pos > skb_tail_pointer(skb)) {
PKT_ERROR(pkt, "trying to split beyond end of packet\n");
return NULL;
}
/* Create a new packet for the second part of the data */
tmppkt = cfpkt_create_pfx(len2nd + PKT_PREFIX + PKT_POSTFIX,
PKT_PREFIX);
if (tmppkt == NULL)
return NULL;
skb2 = pkt_to_skb(tmppkt);
if (skb2 == NULL)
return NULL;
/* Reduce the length of the original packet */
skb_set_tail_pointer(skb, pos);
skb->len = pos;
memcpy(skb2->data, split, len2nd);
skb2->tail += len2nd;
skb2->len += len2nd;
skb2->priority = skb->priority;
return skb_to_pkt(skb2);
}
bool cfpkt_erroneous(struct cfpkt *pkt)
{
return cfpkt_priv(pkt)->erronous;
}
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);

302
net/caif/cfrfml.c Normal file
View file

@ -0,0 +1,302 @@
/*
* Copyright (C) ST-Ericsson AB 2010
* Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*/
#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
#include <linux/stddef.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
#include <net/caif/caif_layer.h>
#include <net/caif/cfsrvl.h>
#include <net/caif/cfpkt.h>
#define container_obj(layr) container_of(layr, struct cfrfml, serv.layer)
#define RFM_SEGMENTATION_BIT 0x01
#define RFM_HEAD_SIZE 7
static int cfrfml_receive(struct cflayer *layr, struct cfpkt *pkt);
static int cfrfml_transmit(struct cflayer *layr, struct cfpkt *pkt);
struct cfrfml {
struct cfsrvl serv;
struct cfpkt *incomplete_frm;
int fragment_size;
u8 seghead[6];
u16 pdu_size;
/* Protects serialized processing of packets */
spinlock_t sync;
};
static void cfrfml_release(struct cflayer *layer)
{
struct cfsrvl *srvl = container_of(layer, struct cfsrvl, layer);
struct cfrfml *rfml = container_obj(&srvl->layer);
if (rfml->incomplete_frm)
cfpkt_destroy(rfml->incomplete_frm);
kfree(srvl);
}
struct cflayer *cfrfml_create(u8 channel_id, struct dev_info *dev_info,
int mtu_size)
{
int tmp;
struct cfrfml *this = kzalloc(sizeof(struct cfrfml), GFP_ATOMIC);
if (!this)
return NULL;
cfsrvl_init(&this->serv, channel_id, dev_info, false);
this->serv.release = cfrfml_release;
this->serv.layer.receive = cfrfml_receive;
this->serv.layer.transmit = cfrfml_transmit;
/* Round down to closest multiple of 16 */
tmp = (mtu_size - RFM_HEAD_SIZE - 6) / 16;
tmp *= 16;
this->fragment_size = tmp;
spin_lock_init(&this->sync);
snprintf(this->serv.layer.name, CAIF_LAYER_NAME_SZ,
"rfm%d", channel_id);
return &this->serv.layer;
}
static struct cfpkt *rfm_append(struct cfrfml *rfml, char *seghead,
struct cfpkt *pkt, int *err)
{
struct cfpkt *tmppkt;
*err = -EPROTO;
/* n-th but not last segment */
if (cfpkt_extr_head(pkt, seghead, 6) < 0)
return NULL;
/* Verify correct header */
if (memcmp(seghead, rfml->seghead, 6) != 0)
return NULL;
tmppkt = cfpkt_append(rfml->incomplete_frm, pkt,
rfml->pdu_size + RFM_HEAD_SIZE);
/* If cfpkt_append failes input pkts are not freed */
*err = -ENOMEM;
if (tmppkt == NULL)
return NULL;
*err = 0;
return tmppkt;
}
static int cfrfml_receive(struct cflayer *layr, struct cfpkt *pkt)
{
u8 tmp;
bool segmented;
int err;
u8 seghead[6];
struct cfrfml *rfml;
struct cfpkt *tmppkt = NULL;
caif_assert(layr->up != NULL);
caif_assert(layr->receive != NULL);
rfml = container_obj(layr);
spin_lock(&rfml->sync);
err = -EPROTO;
if (cfpkt_extr_head(pkt, &tmp, 1) < 0)
goto out;
segmented = tmp & RFM_SEGMENTATION_BIT;
if (segmented) {
if (rfml->incomplete_frm == NULL) {
/* Initial Segment */
if (cfpkt_peek_head(pkt, rfml->seghead, 6) < 0)
goto out;
rfml->pdu_size = get_unaligned_le16(rfml->seghead+4);
if (cfpkt_erroneous(pkt))
goto out;
rfml->incomplete_frm = pkt;
pkt = NULL;
} else {
tmppkt = rfm_append(rfml, seghead, pkt, &err);
if (tmppkt == NULL)
goto out;
if (cfpkt_erroneous(tmppkt))
goto out;
rfml->incomplete_frm = tmppkt;
if (cfpkt_erroneous(tmppkt))
goto out;
}
err = 0;
goto out;
}
if (rfml->incomplete_frm) {
/* Last Segment */
tmppkt = rfm_append(rfml, seghead, pkt, &err);
if (tmppkt == NULL)
goto out;
if (cfpkt_erroneous(tmppkt))
goto out;
rfml->incomplete_frm = NULL;
pkt = tmppkt;
tmppkt = NULL;
/* Verify that length is correct */
err = EPROTO;
if (rfml->pdu_size != cfpkt_getlen(pkt) - RFM_HEAD_SIZE + 1)
goto out;
}
err = rfml->serv.layer.up->receive(rfml->serv.layer.up, pkt);
out:
if (err != 0) {
if (tmppkt)
cfpkt_destroy(tmppkt);
if (pkt)
cfpkt_destroy(pkt);
if (rfml->incomplete_frm)
cfpkt_destroy(rfml->incomplete_frm);
rfml->incomplete_frm = NULL;
pr_info("Connection error %d triggered on RFM link\n", err);
/* Trigger connection error upon failure.*/
layr->up->ctrlcmd(layr->up, CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND,
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 + RFM_HEAD_SIZE);
/* Add info for MUX-layer to route the packet out. */
cfpkt_info(pkt)->channel_id = rfml->serv.layer.id;
/*
* To optimize alignment, we add up the size of CAIF header before
* payload.
*/
cfpkt_info(pkt)->hdr_len = RFM_HEAD_SIZE;
cfpkt_info(pkt)->dev_info = &rfml->serv.dev_info;
return rfml->serv.layer.dn->transmit(rfml->serv.layer.dn, pkt);
}
static int cfrfml_transmit(struct cflayer *layr, struct cfpkt *pkt)
{
int err;
u8 seg;
u8 head[6];
struct cfpkt *rearpkt = NULL;
struct cfpkt *frontpkt = pkt;
struct cfrfml *rfml = container_obj(layr);
caif_assert(layr->dn != NULL);
caif_assert(layr->dn->transmit != NULL);
if (!cfsrvl_ready(&rfml->serv, &err))
goto out;
err = -EPROTO;
if (cfpkt_getlen(pkt) <= RFM_HEAD_SIZE-1)
goto out;
err = 0;
if (cfpkt_getlen(pkt) > rfml->fragment_size + RFM_HEAD_SIZE)
err = cfpkt_peek_head(pkt, head, 6);
if (err < 0)
goto out;
while (cfpkt_getlen(frontpkt) > rfml->fragment_size + RFM_HEAD_SIZE) {
seg = 1;
err = -EPROTO;
if (cfpkt_add_head(frontpkt, &seg, 1) < 0)
goto out;
/*
* On OOM error cfpkt_split returns NULL.
*
* NOTE: Segmented pdu is not correctly aligned.
* This has negative performance impact.
*/
rearpkt = cfpkt_split(frontpkt, rfml->fragment_size);
if (rearpkt == NULL)
goto out;
err = cfrfml_transmit_segment(rfml, frontpkt);
if (err != 0) {
frontpkt = NULL;
goto out;
}
frontpkt = rearpkt;
rearpkt = NULL;
err = -ENOMEM;
if (frontpkt == NULL)
goto out;
err = -EPROTO;
if (cfpkt_add_head(frontpkt, head, 6) < 0)
goto out;
}
seg = 0;
err = -EPROTO;
if (cfpkt_add_head(frontpkt, &seg, 1) < 0)
goto out;
err = cfrfml_transmit_segment(rfml, frontpkt);
frontpkt = NULL;
out:
if (err != 0) {
pr_info("Connection error %d triggered on RFM link\n", err);
/* Trigger connection error upon failure.*/
layr->up->ctrlcmd(layr->up, CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND,
rfml->serv.dev_info.id);
if (rearpkt)
cfpkt_destroy(rearpkt);
if (frontpkt)
cfpkt_destroy(frontpkt);
}
return err;
}

188
net/caif/cfserl.c Normal file
View file

@ -0,0 +1,188 @@
/*
* Copyright (C) ST-Ericsson AB 2010
* Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*/
#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
#include <linux/stddef.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <net/caif/caif_layer.h>
#include <net/caif/cfpkt.h>
#include <net/caif/cfserl.h>
#define container_obj(layr) ((struct cfserl *) layr)
#define CFSERL_STX 0x02
#define SERIAL_MINIUM_PACKET_SIZE 4
#define SERIAL_MAX_FRAMESIZE 4096
struct cfserl {
struct cflayer layer;
struct cfpkt *incomplete_frm;
/* Protects parallel processing of incoming packets */
spinlock_t sync;
bool usestx;
};
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);
struct cflayer *cfserl_create(int instance, bool use_stx)
{
struct cfserl *this = kzalloc(sizeof(struct cfserl), GFP_ATOMIC);
if (!this)
return NULL;
caif_assert(offsetof(struct cfserl, layer) == 0);
this->layer.receive = cfserl_receive;
this->layer.transmit = cfserl_transmit;
this->layer.ctrlcmd = cfserl_ctrlcmd;
this->usestx = use_stx;
spin_lock_init(&this->sync);
snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "ser1");
return &this->layer;
}
static int cfserl_receive(struct cflayer *l, struct cfpkt *newpkt)
{
struct cfserl *layr = container_obj(l);
u16 pkt_len;
struct cfpkt *pkt = NULL;
struct cfpkt *tail_pkt = NULL;
u8 tmp8;
u16 tmp;
u8 stx = CFSERL_STX;
int ret;
u16 expectlen = 0;
caif_assert(newpkt != NULL);
spin_lock(&layr->sync);
if (layr->incomplete_frm != NULL) {
layr->incomplete_frm =
cfpkt_append(layr->incomplete_frm, newpkt, expectlen);
pkt = layr->incomplete_frm;
if (pkt == NULL) {
spin_unlock(&layr->sync);
return -ENOMEM;
}
} else {
pkt = newpkt;
}
layr->incomplete_frm = NULL;
do {
/* Search for STX at start of pkt if STX is used */
if (layr->usestx) {
cfpkt_extr_head(pkt, &tmp8, 1);
if (tmp8 != CFSERL_STX) {
while (cfpkt_more(pkt)
&& tmp8 != CFSERL_STX) {
cfpkt_extr_head(pkt, &tmp8, 1);
}
if (!cfpkt_more(pkt)) {
cfpkt_destroy(pkt);
layr->incomplete_frm = NULL;
spin_unlock(&layr->sync);
return -EPROTO;
}
}
}
pkt_len = cfpkt_getlen(pkt);
/*
* pkt_len is the accumulated length of the packet data
* we have received so far.
* Exit if frame doesn't hold length.
*/
if (pkt_len < 2) {
if (layr->usestx)
cfpkt_add_head(pkt, &stx, 1);
layr->incomplete_frm = pkt;
spin_unlock(&layr->sync);
return 0;
}
/*
* Find length of frame.
* expectlen is the length we need for a full frame.
*/
cfpkt_peek_head(pkt, &tmp, 2);
expectlen = le16_to_cpu(tmp) + 2;
/*
* Frame error handling
*/
if (expectlen < SERIAL_MINIUM_PACKET_SIZE
|| expectlen > SERIAL_MAX_FRAMESIZE) {
if (!layr->usestx) {
if (pkt != NULL)
cfpkt_destroy(pkt);
layr->incomplete_frm = NULL;
expectlen = 0;
spin_unlock(&layr->sync);
return -EPROTO;
}
continue;
}
if (pkt_len < expectlen) {
/* Too little received data */
if (layr->usestx)
cfpkt_add_head(pkt, &stx, 1);
layr->incomplete_frm = pkt;
spin_unlock(&layr->sync);
return 0;
}
/*
* Enough data for at least one frame.
* Split the frame, if too long
*/
if (pkt_len > expectlen)
tail_pkt = cfpkt_split(pkt, expectlen);
else
tail_pkt = NULL;
/* Send the first part of packet upwards.*/
spin_unlock(&layr->sync);
ret = layr->layer.up->receive(layr->layer.up, pkt);
spin_lock(&layr->sync);
if (ret == -EILSEQ) {
if (layr->usestx) {
if (tail_pkt != NULL)
pkt = cfpkt_append(pkt, tail_pkt, 0);
/* Start search for next STX if frame failed */
continue;
} else {
cfpkt_destroy(pkt);
pkt = NULL;
}
}
pkt = tail_pkt;
} while (pkt != NULL);
spin_unlock(&layr->sync);
return 0;
}
static int cfserl_transmit(struct cflayer *layer, struct cfpkt *newpkt)
{
struct cfserl *layr = container_obj(layer);
u8 tmp8 = CFSERL_STX;
if (layr->usestx)
cfpkt_add_head(newpkt, &tmp8, 1);
return layer->dn->transmit(layer->dn, newpkt);
}
static void cfserl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
int phyid)
{
layr->up->ctrlcmd(layr->up, ctrl, phyid);
}

221
net/caif/cfsrvl.c Normal file
View file

@ -0,0 +1,221 @@
/*
* Copyright (C) ST-Ericsson AB 2010
* Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*/
#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
#include <linux/kernel.h>
#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
#define SRVL_FLOW_ON 0x80
#define SRVL_SET_PIN 0x82
#define SRVL_CTRL_PKT_SIZE 1
#define container_obj(layr) container_of(layr, struct cfsrvl, layer)
static void cfservl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
int phyid)
{
struct cfsrvl *service = container_obj(layr);
if (layr->up == NULL || layr->up->ctrlcmd == NULL)
return;
switch (ctrl) {
case CAIF_CTRLCMD_INIT_RSP:
service->open = true;
layr->up->ctrlcmd(layr->up, ctrl, phyid);
break;
case CAIF_CTRLCMD_DEINIT_RSP:
case CAIF_CTRLCMD_INIT_FAIL_RSP:
service->open = false;
layr->up->ctrlcmd(layr->up, ctrl, phyid);
break;
case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND:
if (phyid != service->dev_info.id)
break;
if (service->modem_flow_on)
layr->up->ctrlcmd(layr->up,
CAIF_CTRLCMD_FLOW_OFF_IND, phyid);
service->phy_flow_on = false;
break;
case _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND:
if (phyid != service->dev_info.id)
return;
if (service->modem_flow_on) {
layr->up->ctrlcmd(layr->up,
CAIF_CTRLCMD_FLOW_ON_IND,
phyid);
}
service->phy_flow_on = true;
break;
case CAIF_CTRLCMD_FLOW_OFF_IND:
if (service->phy_flow_on) {
layr->up->ctrlcmd(layr->up,
CAIF_CTRLCMD_FLOW_OFF_IND, phyid);
}
service->modem_flow_on = false;
break;
case CAIF_CTRLCMD_FLOW_ON_IND:
if (service->phy_flow_on) {
layr->up->ctrlcmd(layr->up,
CAIF_CTRLCMD_FLOW_ON_IND, phyid);
}
service->modem_flow_on = true;
break;
case _CAIF_CTRLCMD_PHYIF_DOWN_IND:
/* In case interface is down, let's fake a remove shutdown */
layr->up->ctrlcmd(layr->up,
CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND, phyid);
break;
case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND:
layr->up->ctrlcmd(layr->up, ctrl, phyid);
break;
default:
pr_warn("Unexpected ctrl in cfsrvl (%d)\n", ctrl);
/* We have both modem and phy flow on, send flow on */
layr->up->ctrlcmd(layr->up, ctrl, phyid);
service->phy_flow_on = true;
break;
}
}
static int cfservl_modemcmd(struct cflayer *layr, enum caif_modemcmd ctrl)
{
struct cfsrvl *service = container_obj(layr);
caif_assert(layr != NULL);
caif_assert(layr->dn != NULL);
caif_assert(layr->dn->transmit != NULL);
if (!service->supports_flowctrl)
return 0;
switch (ctrl) {
case CAIF_MODEMCMD_FLOW_ON_REQ:
{
struct cfpkt *pkt;
struct caif_payload_info *info;
u8 flow_on = SRVL_FLOW_ON;
pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE);
if (!pkt)
return -ENOMEM;
if (cfpkt_add_head(pkt, &flow_on, 1) < 0) {
pr_err("Packet is erroneous!\n");
cfpkt_destroy(pkt);
return -EPROTO;
}
info = cfpkt_info(pkt);
info->channel_id = service->layer.id;
info->hdr_len = 1;
info->dev_info = &service->dev_info;
cfpkt_set_prio(pkt, TC_PRIO_CONTROL);
return layr->dn->transmit(layr->dn, pkt);
}
case CAIF_MODEMCMD_FLOW_OFF_REQ:
{
struct cfpkt *pkt;
struct caif_payload_info *info;
u8 flow_off = SRVL_FLOW_OFF;
pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE);
if (!pkt)
return -ENOMEM;
if (cfpkt_add_head(pkt, &flow_off, 1) < 0) {
pr_err("Packet is erroneous!\n");
cfpkt_destroy(pkt);
return -EPROTO;
}
info = cfpkt_info(pkt);
info->channel_id = service->layer.id;
info->hdr_len = 1;
info->dev_info = &service->dev_info;
cfpkt_set_prio(pkt, TC_PRIO_CONTROL);
return layr->dn->transmit(layr->dn, pkt);
}
default:
break;
}
return -EINVAL;
}
static void cfsrvl_release(struct cflayer *layer)
{
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)
{
caif_assert(offsetof(struct cfsrvl, layer) == 0);
service->open = false;
service->modem_flow_on = true;
service->phy_flow_on = true;
service->layer.id = channel_id;
service->layer.ctrlcmd = cfservl_ctrlcmd;
service->layer.modemcmd = cfservl_modemcmd;
service->dev_info = *dev_info;
service->supports_flowctrl = supports_flowctrl;
service->release = cfsrvl_release;
}
bool cfsrvl_ready(struct cfsrvl *service, int *err)
{
if (!service->open) {
*err = -ENOTCONN;
return false;
}
return true;
}
u8 cfsrvl_getphyid(struct cflayer *layer)
{
struct cfsrvl *servl = container_obj(layer);
return servl->dev_info.id;
}
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);

104
net/caif/cfutill.c Normal file
View file

@ -0,0 +1,104 @@
/*
* Copyright (C) ST-Ericsson AB 2010
* Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*/
#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <net/caif/caif_layer.h>
#include <net/caif/cfsrvl.h>
#include <net/caif/cfpkt.h>
#define container_obj(layr) ((struct cfsrvl *) layr)
#define UTIL_PAYLOAD 0x00
#define UTIL_CMD_BIT 0x80
#define UTIL_REMOTE_SHUTDOWN 0x82
#define UTIL_FLOW_OFF 0x81
#define UTIL_FLOW_ON 0x80
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 = kzalloc(sizeof(struct cfsrvl), GFP_ATOMIC);
if (!util)
return NULL;
caif_assert(offsetof(struct cfsrvl, layer) == 0);
cfsrvl_init(util, channel_id, dev_info, true);
util->layer.receive = cfutill_receive;
util->layer.transmit = cfutill_transmit;
snprintf(util->layer.name, CAIF_LAYER_NAME_SZ - 1, "util1");
return &util->layer;
}
static int cfutill_receive(struct cflayer *layr, struct cfpkt *pkt)
{
u8 cmd = -1;
struct cfsrvl *service = container_obj(layr);
caif_assert(layr != NULL);
caif_assert(layr->up != NULL);
caif_assert(layr->up->receive != NULL);
caif_assert(layr->up->ctrlcmd != NULL);
if (cfpkt_extr_head(pkt, &cmd, 1) < 0) {
pr_err("Packet is erroneous!\n");
cfpkt_destroy(pkt);
return -EPROTO;
}
switch (cmd) {
case UTIL_PAYLOAD:
return layr->up->receive(layr->up, pkt);
case UTIL_FLOW_OFF:
layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_OFF_IND, 0);
cfpkt_destroy(pkt);
return 0;
case UTIL_FLOW_ON:
layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_ON_IND, 0);
cfpkt_destroy(pkt);
return 0;
case UTIL_REMOTE_SHUTDOWN: /* Remote Shutdown Request */
pr_err("REMOTE SHUTDOWN REQUEST RECEIVED\n");
layr->ctrlcmd(layr, CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND, 0);
service->open = false;
cfpkt_destroy(pkt);
return 0;
default:
cfpkt_destroy(pkt);
pr_warn("Unknown service control %d (0x%x)\n", cmd, cmd);
return -EPROTO;
}
}
static int cfutill_transmit(struct cflayer *layr, struct cfpkt *pkt)
{
u8 zero = 0;
struct caif_payload_info *info;
int ret;
struct cfsrvl *service = container_obj(layr);
caif_assert(layr != NULL);
caif_assert(layr->dn != NULL);
caif_assert(layr->dn->transmit != NULL);
if (!cfsrvl_ready(service, &ret)) {
cfpkt_destroy(pkt);
return ret;
}
cfpkt_add_head(pkt, &zero, 1);
/* Add info for MUX-layer to route the packet out. */
info = cfpkt_info(pkt);
info->channel_id = service->layer.id;
/*
* To optimize alignment, we add up the size of CAIF header before
* payload.
*/
info->hdr_len = 1;
info->dev_info = &service->dev_info;
return layr->dn->transmit(layr->dn, pkt);
}

101
net/caif/cfveil.c Normal file
View file

@ -0,0 +1,101 @@
/*
* Copyright (C) ST-Ericsson AB 2010
* Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*/
#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
#include <linux/stddef.h>
#include <linux/slab.h>
#include <net/caif/caif_layer.h>
#include <net/caif/cfsrvl.h>
#include <net/caif/cfpkt.h>
#define VEI_PAYLOAD 0x00
#define VEI_CMD_BIT 0x80
#define VEI_FLOW_OFF 0x81
#define VEI_FLOW_ON 0x80
#define VEI_SET_PIN 0x82
#define container_obj(layr) container_of(layr, struct cfsrvl, layer)
static int cfvei_receive(struct cflayer *layr, struct cfpkt *pkt);
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 = kzalloc(sizeof(struct cfsrvl), GFP_ATOMIC);
if (!vei)
return NULL;
caif_assert(offsetof(struct cfsrvl, layer) == 0);
cfsrvl_init(vei, channel_id, dev_info, true);
vei->layer.receive = cfvei_receive;
vei->layer.transmit = cfvei_transmit;
snprintf(vei->layer.name, CAIF_LAYER_NAME_SZ - 1, "vei%d", channel_id);
return &vei->layer;
}
static int cfvei_receive(struct cflayer *layr, struct cfpkt *pkt)
{
u8 cmd;
int ret;
caif_assert(layr->up != NULL);
caif_assert(layr->receive != NULL);
caif_assert(layr->ctrlcmd != NULL);
if (cfpkt_extr_head(pkt, &cmd, 1) < 0) {
pr_err("Packet is erroneous!\n");
cfpkt_destroy(pkt);
return -EPROTO;
}
switch (cmd) {
case VEI_PAYLOAD:
ret = layr->up->receive(layr->up, pkt);
return ret;
case VEI_FLOW_OFF:
layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_OFF_IND, 0);
cfpkt_destroy(pkt);
return 0;
case VEI_FLOW_ON:
layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_ON_IND, 0);
cfpkt_destroy(pkt);
return 0;
case VEI_SET_PIN: /* SET RS232 PIN */
cfpkt_destroy(pkt);
return 0;
default: /* SET RS232 PIN */
pr_warn("Unknown VEI control packet %d (0x%x)!\n", cmd, cmd);
cfpkt_destroy(pkt);
return -EPROTO;
}
}
static int cfvei_transmit(struct cflayer *layr, struct cfpkt *pkt)
{
u8 tmp = 0;
struct caif_payload_info *info;
int ret;
struct cfsrvl *service = container_obj(layr);
if (!cfsrvl_ready(service, &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");
ret = -EPROTO;
goto err;
}
/* Add info-> for MUX-layer to route the packet out. */
info = cfpkt_info(pkt);
info->channel_id = service->layer.id;
info->hdr_len = 1;
info->dev_info = &service->dev_info;
return layr->dn->transmit(layr->dn, pkt);
err:
cfpkt_destroy(pkt);
return ret;
}

65
net/caif/cfvidl.c Normal file
View file

@ -0,0 +1,65 @@
/*
* Copyright (C) ST-Ericsson AB 2010
* Author: Sjur Brendeland
* License terms: GNU General Public License (GPL) version 2
*/
#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <net/caif/caif_layer.h>
#include <net/caif/cfsrvl.h>
#include <net/caif/cfpkt.h>
#define container_obj(layr) ((struct cfsrvl *) layr)
static int cfvidl_receive(struct cflayer *layr, struct cfpkt *pkt);
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 = kzalloc(sizeof(struct cfsrvl), GFP_ATOMIC);
if (!vid)
return NULL;
caif_assert(offsetof(struct cfsrvl, layer) == 0);
cfsrvl_init(vid, channel_id, dev_info, false);
vid->layer.receive = cfvidl_receive;
vid->layer.transmit = cfvidl_transmit;
snprintf(vid->layer.name, CAIF_LAYER_NAME_SZ - 1, "vid1");
return &vid->layer;
}
static int cfvidl_receive(struct cflayer *layr, struct cfpkt *pkt)
{
u32 videoheader;
if (cfpkt_extr_head(pkt, &videoheader, 4) < 0) {
pr_err("Packet is erroneous!\n");
cfpkt_destroy(pkt);
return -EPROTO;
}
return layr->up->receive(layr->up, pkt);
}
static int cfvidl_transmit(struct cflayer *layr, struct cfpkt *pkt)
{
struct cfsrvl *service = container_obj(layr);
struct caif_payload_info *info;
u32 videoheader = 0;
int 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;
return layr->dn->transmit(layr->dn, pkt);
}

553
net/caif/chnl_net.c Normal file
View file

@ -0,0 +1,553 @@
/*
* Copyright (C) ST-Ericsson AB 2010
* 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/fs.h>
#include <linux/hardirq.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/if_ether.h>
#include <linux/moduleparam.h>
#include <linux/ip.h>
#include <linux/sched.h>
#include <linux/sockios.h>
#include <linux/caif/if_caif.h>
#include <net/rtnetlink.h>
#include <net/caif/caif_layer.h>
#include <net/caif/cfpkt.h>
#include <net/caif/caif_dev.h>
/* GPRS PDP connection has MTU to 1500 */
#define GPRS_PDP_MTU 1500
/* 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);
MODULE_LICENSE("GPL");
MODULE_ALIAS_RTNL_LINK("caif");
enum caif_states {
CAIF_CONNECTED = 1,
CAIF_CONNECTING,
CAIF_DISCONNECTED,
CAIF_SHUTDOWN
};
struct chnl_net {
struct cflayer chnl;
struct net_device_stats stats;
struct caif_connect_request conn_req;
struct list_head list_field;
struct net_device *netdev;
char name[256];
wait_queue_head_t netmgmt_wq;
/* Flow status to remember and control the transmission. */
bool flowenabled;
enum caif_states state;
};
static void robust_list_del(struct list_head *delete_node)
{
struct list_head *list_node;
struct list_head *n;
ASSERT_RTNL();
list_for_each_safe(list_node, n, &chnl_net_list) {
if (list_node == delete_node) {
list_del(list_node);
return;
}
}
WARN_ON(1);
}
static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt)
{
struct sk_buff *skb;
struct chnl_net *priv;
int pktlen;
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 = skb->len;
/* Pass some minimum information and
* send the packet to the net stack.
*/
skb->dev = priv->netdev;
/* 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)
skb->ip_summed = CHECKSUM_UNNECESSARY;
else
skb->ip_summed = CHECKSUM_NONE;
if (in_interrupt())
netif_rx(skb);
else
netif_rx_ni(skb);
/* Update statistics. */
priv->netdev->stats.rx_packets++;
priv->netdev->stats.rx_bytes += pktlen;
return 0;
}
static int delete_device(struct chnl_net *dev)
{
ASSERT_RTNL();
if (dev->netdev)
unregister_netdevice(dev->netdev);
return 0;
}
static void close_work(struct work_struct *work)
{
struct chnl_net *dev = NULL;
struct list_head *list_node;
struct list_head *_tmp;
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);
}
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)
{
struct chnl_net *priv = container_of(layr, struct chnl_net, chnl);
pr_debug("NET flowctrl func called flow: %s\n",
flow == CAIF_CTRLCMD_FLOW_ON_IND ? "ON" :
flow == CAIF_CTRLCMD_INIT_RSP ? "INIT" :
flow == CAIF_CTRLCMD_FLOW_OFF_IND ? "OFF" :
flow == CAIF_CTRLCMD_DEINIT_RSP ? "CLOSE/DEINIT" :
flow == CAIF_CTRLCMD_INIT_FAIL_RSP ? "OPEN_FAIL" :
flow == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND ?
"REMOTE_SHUTDOWN" : "UKNOWN CTRL COMMAND");
switch (flow) {
case CAIF_CTRLCMD_FLOW_OFF_IND:
priv->flowenabled = false;
netif_stop_queue(priv->netdev);
break;
case CAIF_CTRLCMD_DEINIT_RSP:
priv->state = CAIF_DISCONNECTED;
break;
case CAIF_CTRLCMD_INIT_FAIL_RSP:
priv->state = CAIF_DISCONNECTED;
wake_up_interruptible(&priv->netmgmt_wq);
break;
case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND:
priv->state = CAIF_SHUTDOWN;
netif_tx_disable(priv->netdev);
schedule_work(&close_worker);
break;
case CAIF_CTRLCMD_FLOW_ON_IND:
priv->flowenabled = true;
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);
wake_up_interruptible(&priv->netmgmt_wq);
break;
default:
break;
}
}
static int chnl_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct chnl_net *priv;
struct cfpkt *pkt = NULL;
int len;
int result = -1;
/* Get our private data. */
priv = netdev_priv(dev);
if (skb->len > priv->netdev->mtu) {
pr_warn("Size of skb exceeded MTU\n");
kfree_skb(skb);
dev->stats.tx_errors++;
return NETDEV_TX_OK;
}
if (!priv->flowenabled) {
pr_debug("dropping packets flow off\n");
kfree_skb(skb);
dev->stats.tx_dropped++;
return NETDEV_TX_OK;
}
if (priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP)
swap(ip_hdr(skb)->saddr, ip_hdr(skb)->daddr);
/* Store original SKB length. */
len = skb->len;
pkt = cfpkt_fromnative(CAIF_DIR_OUT, (void *) skb);
/* Send the packet down the stack. */
result = priv->chnl.dn->transmit(priv->chnl.dn, pkt);
if (result) {
dev->stats.tx_dropped++;
return NETDEV_TX_OK;
}
/* Update statistics. */
dev->stats.tx_packets++;
dev->stats.tx_bytes += len;
return NETDEV_TX_OK;
}
static int chnl_net_open(struct net_device *dev)
{
struct chnl_net *priv = NULL;
int result = -1;
int llifindex, headroom, tailroom, mtu;
struct net_device *lldev;
ASSERT_RTNL();
priv = netdev_priv(dev);
if (!priv) {
pr_debug("chnl_net_open: no priv\n");
return -ENODEV;
}
if (priv->state != CAIF_CONNECTING) {
priv->state = CAIF_CONNECTING;
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,"
" Err:%d\n",
result);
goto error;
}
lldev = __dev_get_by_index(dev_net(dev), llifindex);
if (lldev == NULL) {
pr_debug("no interface?\n");
result = -ENODEV;
goto error;
}
dev->needed_tailroom = tailroom + lldev->needed_tailroom;
dev->hard_header_len = headroom + lldev->hard_header_len +
lldev->needed_tailroom;
/*
* MTU, head-room etc is not know before we have a
* CAIF link layer device available. MTU calculation may
* override initial RTNL configuration.
* MTU is minimum of current mtu, link layer mtu pluss
* CAIF head and tail, and PDP GPRS contexts max MTU.
*/
mtu = min_t(int, dev->mtu, lldev->mtu - (headroom + tailroom));
mtu = min_t(int, GPRS_PDP_MTU, mtu);
dev_set_mtu(dev, mtu);
if (mtu < 100) {
pr_warn("CAIF Interface MTU too small (%d)\n", mtu);
result = -ENODEV;
goto error;
}
}
rtnl_unlock(); /* Release RTNL lock during connect wait */
result = wait_event_interruptible_timeout(priv->netmgmt_wq,
priv->state != CAIF_CONNECTING,
CONNECT_TIMEOUT);
rtnl_lock();
if (result == -ERESTARTSYS) {
pr_debug("wait_event_interruptible woken by a signal\n");
result = -ERESTARTSYS;
goto error;
}
if (result == 0) {
pr_debug("connect timeout\n");
caif_disconnect_client(dev_net(dev), &priv->chnl);
priv->state = CAIF_DISCONNECTED;
pr_debug("state disconnected\n");
result = -ETIMEDOUT;
goto error;
}
if (priv->state != CAIF_CONNECTED) {
pr_debug("connect failed\n");
result = -ECONNREFUSED;
goto error;
}
pr_debug("CAIF Netdevice connected\n");
return 0;
error:
caif_disconnect_client(dev_net(dev), &priv->chnl);
priv->state = CAIF_DISCONNECTED;
pr_debug("state disconnected\n");
return result;
}
static int chnl_net_stop(struct net_device *dev)
{
struct chnl_net *priv;
ASSERT_RTNL();
priv = netdev_priv(dev);
priv->state = CAIF_DISCONNECTED;
caif_disconnect_client(dev_net(dev), &priv->chnl);
return 0;
}
static int chnl_net_init(struct net_device *dev)
{
struct chnl_net *priv;
ASSERT_RTNL();
priv = netdev_priv(dev);
strncpy(priv->name, dev->name, sizeof(priv->name));
return 0;
}
static void chnl_net_uninit(struct net_device *dev)
{
struct chnl_net *priv;
ASSERT_RTNL();
priv = netdev_priv(dev);
robust_list_del(&priv->list_field);
}
static const struct net_device_ops netdev_ops = {
.ndo_open = chnl_net_open,
.ndo_stop = chnl_net_stop,
.ndo_init = chnl_net_init,
.ndo_uninit = chnl_net_uninit,
.ndo_start_xmit = chnl_net_start_xmit,
};
static void 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 = chnl_net_destructor;
dev->flags |= IFF_NOARP;
dev->flags |= IFF_POINTOPOINT;
dev->mtu = GPRS_PDP_MTU;
dev->tx_queue_len = CAIF_NET_DEFAULT_QUEUE_LEN;
priv = netdev_priv(dev);
priv->chnl.receive = chnl_recv_cb;
priv->chnl.ctrlcmd = chnl_flowctrl_cb;
priv->netdev = dev;
priv->conn_req.protocol = CAIFPROTO_DATAGRAM;
priv->conn_req.link_selector = CAIF_LINK_HIGH_BANDW;
priv->conn_req.priority = CAIF_PRIO_LOW;
/* Insert illegal value */
priv->conn_req.sockaddr.u.dgm.connection_id = UNDEF_CONNID;
priv->flowenabled = false;
init_waitqueue_head(&priv->netmgmt_wq);
}
static int ipcaif_fill_info(struct sk_buff *skb, const struct net_device *dev)
{
struct chnl_net *priv;
u8 loop;
priv = netdev_priv(dev);
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;
if (nla_put_u8(skb, IFLA_CAIF_LOOPBACK, loop))
goto nla_put_failure;
return 0;
nla_put_failure:
return -EMSGSIZE;
}
static void caif_netlink_parms(struct nlattr *data[],
struct caif_connect_request *conn_req)
{
if (!data) {
pr_warn("no params data found\n");
return;
}
if (data[IFLA_CAIF_IPV4_CONNID])
conn_req->sockaddr.u.dgm.connection_id =
nla_get_u32(data[IFLA_CAIF_IPV4_CONNID]);
if (data[IFLA_CAIF_IPV6_CONNID])
conn_req->sockaddr.u.dgm.connection_id =
nla_get_u32(data[IFLA_CAIF_IPV6_CONNID]);
if (data[IFLA_CAIF_LOOPBACK]) {
if (nla_get_u8(data[IFLA_CAIF_LOOPBACK]))
conn_req->protocol = CAIFPROTO_DATAGRAM_LOOP;
else
conn_req->protocol = CAIFPROTO_DATAGRAM;
}
}
static int ipcaif_newlink(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[])
{
int ret;
struct chnl_net *caifdev;
ASSERT_RTNL();
caifdev = netdev_priv(dev);
caif_netlink_parms(data, &caifdev->conn_req);
dev_net_set(caifdev->netdev, src_net);
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 chnl_net *caifdev;
ASSERT_RTNL();
caifdev = netdev_priv(dev);
caif_netlink_parms(data, &caifdev->conn_req);
netdev_state_change(dev);
return 0;
}
static size_t ipcaif_get_size(const struct net_device *dev)
{
return
/* IFLA_CAIF_IPV4_CONNID */
nla_total_size(4) +
/* IFLA_CAIF_IPV6_CONNID */
nla_total_size(4) +
/* IFLA_CAIF_LOOPBACK */
nla_total_size(2) +
0;
}
static const struct nla_policy ipcaif_policy[IFLA_CAIF_MAX + 1] = {
[IFLA_CAIF_IPV4_CONNID] = { .type = NLA_U32 },
[IFLA_CAIF_IPV6_CONNID] = { .type = NLA_U32 },
[IFLA_CAIF_LOOPBACK] = { .type = NLA_U8 }
};
static struct rtnl_link_ops ipcaif_link_ops __read_mostly = {
.kind = "caif",
.priv_size = sizeof(struct chnl_net),
.setup = ipcaif_net_setup,
.maxtype = IFLA_CAIF_MAX,
.policy = ipcaif_policy,
.newlink = ipcaif_newlink,
.changelink = ipcaif_changelink,
.get_size = ipcaif_get_size,
.fill_info = ipcaif_fill_info,
};
static int __init chnl_init_module(void)
{
return rtnl_link_register(&ipcaif_link_ops);
}
static void __exit chnl_exit_module(void)
{
struct chnl_net *dev = NULL;
struct list_head *list_node;
struct list_head *_tmp;
rtnl_link_unregister(&ipcaif_link_ops);
rtnl_lock();
list_for_each_safe(list_node, _tmp, &chnl_net_list) {
dev = list_entry(list_node, struct chnl_net, list_field);
list_del(list_node);
delete_device(dev);
}
rtnl_unlock();
}
module_init(chnl_init_module);
module_exit(chnl_exit_module);