diff options
author | Kim Lilliestierna XX <kim.xx.lilliestierna@stericsson.com> | 2010-05-10 14:27:31 +0200 |
---|---|---|
committer | John Rigby <john.rigby@linaro.org> | 2010-09-02 22:45:24 -0600 |
commit | 965cffd58fcc75418e40ded7552acb9fcd11baf9 (patch) | |
tree | 722b2363ae326fac87d82cf6f0c3c2f72b64e1de /net | |
parent | 0f900c630625fe380b3ea7fc81edebc5ff4dff78 (diff) |
CAIF: Latest changes
Change-Id: If0e8ec8d8961825b440d2e872fb29422a66780f9
Diffstat (limited to 'net')
-rw-r--r-- | net/caif/Kconfig | 41 | ||||
-rw-r--r-- | net/caif/Makefile | 28 | ||||
-rw-r--r-- | net/caif/caif_chnlif.c | 36 | ||||
-rw-r--r-- | net/caif/caif_chr.c | 74 | ||||
-rw-r--r-- | net/caif/caif_config_util.c | 104 | ||||
-rw-r--r-- | net/caif/caif_dev.c | 118 | ||||
-rw-r--r-- | net/caif/caif_socket.c | 1969 | ||||
-rw-r--r-- | net/caif/cfdbgl.c | 40 | ||||
-rw-r--r-- | net/caif/cfpkt_skbuff.c | 193 | ||||
-rw-r--r-- | net/caif/chnl_chr.c | 54 | ||||
-rw-r--r-- | net/caif/chnl_net.c | 398 |
11 files changed, 1542 insertions, 1513 deletions
diff --git a/net/caif/Kconfig b/net/caif/Kconfig index 6707ef59d3b..f32ba7d1cd0 100644 --- a/net/caif/Kconfig +++ b/net/caif/Kconfig @@ -11,14 +11,14 @@ menuconfig CAIF ---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. + with its modems. It is accessed from user space as sockets (PF_CAIF). - Say Y (or M) here if you build for a phone product (e.g. Android) that - uses CAIF as transport, if unsure say N. + 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_SOCK and CAIF_NETDEV also - needs to be built as modules. You will also need to say yes to any CAIF - physical devices that your platform requires. + 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. @@ -33,15 +33,6 @@ config CAIF_DEBUG Be aware that doing this will impact performance. If unsure say N. -config CAIF_SOCK - tristate "CAIF Sockets" - default CAIF - ---help--- - Say Y if you will be using CAIF Sockets. - 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_NETDEV tristate "CAIF GPRS Network device" @@ -53,20 +44,30 @@ config CAIF_NETDEV also be a built-in. If unsure say Y. -#official-kernel-patch-cut-here +#deprecated-functionality-below config CAIF_CHARDEV tristate "CAIF character device" default CAIF ---help--- - Say Y if you will be using the CAIF AT type character devices. + Say Y if you will be using the CAIF character devices. 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 + If you select to build it as a built-in then the main CAIF module must also be a built-in. If unsure say Y. +config CAIF_KERNEL_API + tristate "CAIF Kernel Access API" + default CAIF + ---help--- + Say Y if you will be using the CAIF Kernel API. + This can be either built-in or a loadable module, + If you select to build it as a built-in then the main CAIF module must + also be a built-in. + If unsure say N. + # Include physical drivers source "drivers/net/caif/Kconfig" -#official-kernel-patch-resume-here +#deprecated-functionality-above endif -#endmenu +#endmenu
\ No newline at end of file diff --git a/net/caif/Makefile b/net/caif/Makefile index 13e66956c55..0631ac73c97 100644 --- a/net/caif/Makefile +++ b/net/caif/Makefile @@ -4,31 +4,31 @@ endif ccflags-y := $(CAIF_FLAGS) $(CAIF_DBG_FLAGS) -caif-objs := caif_dev.o caif_chnlif.o \ - generic/cfcnfg.o generic/cfmuxl.o generic/cfctrl.o \ - generic/cffrml.o generic/cfveil.o generic/cflist.o \ - generic/cfserl.o generic/cfdgml.o \ - generic/cfrfml.o generic/cfvidl.o generic/cfutill.o \ - generic/cfsrvl.o generic/cfpkt_skbuff.o caif_config_util.o - +caif-objs := caif_dev.o \ + cfcnfg.o cfmuxl.o cfctrl.o \ + cffrml.o cfveil.o cfdbgl.o\ + cfserl.o cfdgml.o \ + cfrfml.o cfvidl.o cfutill.o \ + cfsrvl.o cfpkt_skbuff.o caif_config_util.o clean-dirs:= .tmp_versions clean-files:= \ Module.symvers \ modules.order \ *.cmd \ - *~ \ - generic/*.o \ - generic/*~ + *.o \ + *~ obj-$(CONFIG_CAIF) += caif.o obj-$(CONFIG_CAIF_NETDEV) += chnl_net.o -obj-$(CONFIG_CAIF_SOCK) += caif_socket.o +obj-$(CONFIG_CAIF) += caif_socket.o export-objs := caif.o - -#official-kernel-patch-cut-here +#deprecated-functionality-below # CAIF character device -obj-$(CONFIG_CAIF_CHARDEV) += chnl_chr.o caif_chr.o +obj-$(CONFIG_CAIF_CHARDEV) += caif_chr.o +obj-$(CONFIG_CAIF_CHARDEV) += chnl_chr.o + +obj-$(CONFIG_CAIF_KERNEL_API) += caif_chnlif.o diff --git a/net/caif/caif_chnlif.c b/net/caif/caif_chnlif.c index cd337c8ce1d..c614b9aa004 100644 --- a/net/caif/caif_chnlif.c +++ b/net/caif/caif_chnlif.c @@ -1,18 +1,17 @@ /* - * Copyright (C) ST-Ericsson AB 2009 - * Author: Daniel Martensson / Daniel.Martensson@stericsson.com + * Copyright (C) ST-Ericsson AB 2010 + * Author: Sjur Brendeland sjur.brandeland@stericsson.com * License terms: GNU General Public License (GPL) version 2 */ #include <linux/skbuff.h> #include <net/caif/caif_kernel.h> -#include <net/caif/generic/caif_layer.h> -#include <net/caif/generic/cfpkt.h> -#include <net/caif/generic/cfcnfg.h> -#include <net/caif/generic/cfglue.h> +#include <net/caif/caif_layer.h> +#include <net/caif/cfpkt.h> +#include <net/caif/cfcnfg.h> #include <net/caif/caif_dev.h> struct caif_kernelif { - struct layer layer; + struct cflayer layer; struct caif_device *dev; struct cfctrl_link_param param; }; @@ -48,7 +47,7 @@ int caif_extract_and_destroy_skb(struct sk_buff *skb, unsigned char *data, * if we fail here we are NOT freeing the skb. */ if (!skb_linearize(skb) || skb->len > max_length) - return CFGLU_EOVERFLOW; + return -EOVERFLOW; memcpy(data, skb->data, skb->len); kfree_skb(skb); return len; @@ -89,17 +88,17 @@ int caif_flow_control(struct caif_device *dev, enum caif_flowctrl flow) } EXPORT_SYMBOL(caif_flow_control); -static int chnlif_receive(struct layer *layr, struct cfpkt *cfpkt) +static int chnlif_receive(struct cflayer *layr, struct cfpkt *cfpkt) { struct caif_kernelif *chnl = container_of(layr, struct caif_kernelif, layer); struct sk_buff *skb; skb = (struct sk_buff *) cfpkt_tonative(cfpkt); chnl->dev->receive_cb(chnl->dev, skb); - return CFGLU_EOK; + return 0; } -static void chnlif_flowctrl(struct layer *layr, enum caif_ctrlcmd ctrl, +static void chnlif_flowctrl(struct cflayer *layr, enum caif_ctrlcmd ctrl, int phyid) { struct caif_kernelif *chnl = (struct caif_kernelif *) layr; @@ -120,7 +119,7 @@ static void chnlif_flowctrl(struct layer *layr, enum caif_ctrlcmd ctrl, chnl->dev->_caif_handle = NULL; chnl->dev->control_cb(chnl->dev, ctl); memset(chnl, 0, sizeof(chnl)); - cfglu_free(chnl); + kfree(chnl); return; case CAIF_CTRLCMD_INIT_RSP: @@ -138,7 +137,8 @@ static void chnlif_flowctrl(struct layer *layr, enum caif_ctrlcmd ctrl, int caif_add_device(struct caif_device *dev) { int ret; - struct caif_kernelif *chnl = cfglu_alloc(sizeof(struct caif_kernelif)); + struct caif_kernelif *chnl = kmalloc(sizeof(struct caif_kernelif), + GFP_ATOMIC); if (!chnl) return -ENOMEM; chnl->dev = dev; @@ -148,21 +148,21 @@ int caif_add_device(struct caif_device *dev) channel_config_2_link_param(get_caif_conf(), &dev->caif_config, &chnl->param); if (ret < 0) { - ret = CFGLU_EBADPARAM; + ret = -EINVAL; goto error; } if (cfcnfg_add_adaptation_layer(get_caif_conf(), &chnl->param, &chnl->layer)) { - ret = CFGLU_ENOTCONN; + ret = -ENOTCONN; goto error; } dev->_caif_handle = chnl; - return CFGLU_EOK; + return 0; error: chnl->dev->_caif_handle = NULL; memset(chnl, 0, sizeof(chnl)); - cfglu_free(chnl); + kfree(chnl); return ret; } EXPORT_SYMBOL(caif_add_device); @@ -172,6 +172,6 @@ int caif_remove_device(struct caif_device *caif_dev) struct caif_kernelif *chnl = container_of(caif_dev->_caif_handle, struct caif_kernelif, layer); - return cfcnfg_del_adapt_layer(get_caif_conf(), &chnl->layer); + return cfcnfg_disconn_adapt_layer(get_caif_conf(), &chnl->layer); } EXPORT_SYMBOL(caif_remove_device); diff --git a/net/caif/caif_chr.c b/net/caif/caif_chr.c index fc9bc833c4b..a304e3c691a 100644 --- a/net/caif/caif_chr.c +++ b/net/caif/caif_chr.c @@ -1,5 +1,5 @@ /* - * Copyright (C) ST-Ericsson AB 2009 + * Copyright (C) ST-Ericsson AB 2010 * Author: Daniel Martensson / Daniel.Martensson@stericsson.com * License terms: GNU General Public License (GPL) version 2 */ @@ -20,11 +20,12 @@ #include <net/caif/caif_actions.h> #include <net/caif/caif_dev.h> #include <net/caif/caif_chr.h> -#include <net/caif/generic/cfloopcfg.h> -#include <net/caif/generic/cfpkt.h> -#include <net/caif/generic/cfcnfg.h> +#include <net/caif/cfloopcfg.h> +#include <net/caif/cfpkt.h> +#include <net/caif/cfcnfg.h> #include <linux/caif/caif_ioctl.h> #include <linux/caif/if_caif.h> +#include <linux/version.h> MODULE_LICENSE("GPL"); @@ -70,13 +71,13 @@ static struct caif_chr caifdev; int caifdev_open(struct inode *inode, struct file *filp) { - pr_warning("CAIF: %s(): Entered\n", __func__); + pr_debug("CAIF: %s(): Entered\n", __func__); return 0; } int caifdev_release(struct inode *inode, struct file *filp) { - pr_warning("CAIF: %s(): Entered\n", __func__); + pr_debug("CAIF: %s(): Entered\n", __func__); return 0; } @@ -91,10 +92,14 @@ static int netdev_create(struct caif_channel_create_action *action) static int netdev_remove(char *name) { +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)) struct ifreq ifreq; strncpy(ifreq.ifr_name, name, sizeof(ifreq.ifr_name)); ifreq.ifr_name[sizeof(ifreq.ifr_name)-1] = '\0'; return caif_ioctl(SIOCCAIFNETREMOVE, (unsigned long) &ifreq, false); +#else + return -EINVAL; +#endif } static int caifdev_ioctl(struct inode *inode, struct file *filp, @@ -158,13 +163,15 @@ static int caifdev_ioctl(struct inode *inode, struct file *filp, pr_info("CAIF: %s(): device type CAIF_DEV_CHR\n", __func__); if (chrdev_mgmt_func == NULL) { printk(KERN_WARNING - "caifdev_ioctl:DevType CHR is not registered\n"); + "caifdev_ioctl:" + "DevType CHR is not registered\n"); return -EINVAL; - } + } ret = (*chrdev_mgmt_func)(operation, ¶m); if (ret < 0) { printk(KERN_INFO - "caifdev_ioctl: error performing device operation\n"); + "caifdev_ioctl:" + "error performing device operation\n"); return ret; } @@ -244,43 +251,28 @@ err_misc_register_failed: return -ENODEV; } -int caifdev_phy_register(struct layer *phyif, enum cfcnfg_phy_type phy_type, - enum cfcnfg_phy_preference phy_pref, - bool fcs,bool stx) +int caifdev_phy_register(struct cflayer *phyif, enum cfcnfg_phy_type phy_type, + enum cfcnfg_phy_preference phy_pref, + bool fcs, bool stx) { - uint16 phyid; + u16 phyid; /* Hook up the physical interface. * Right now we are not using the returned ID. */ cfcnfg_add_phy_layer(get_caif_conf(), phy_type, NULL, phyif, &phyid, - phy_pref,fcs,stx); + phy_pref, fcs, stx); - pr_warning("CAIF: caifdev_phy_register: phyif:%p phyid:%d == phyif->id:%d\n", - (void *)phyif, phyid, phyif->id); + pr_debug("CAIF: caifdev_phy_register: " + "phyif:%p phyid:%d == phyif->id:%d\n", + (void *)phyif, phyid, phyif->id); return 0; } EXPORT_SYMBOL(caifdev_phy_register); -#if 0 -int caifdev_phy_loop_register(struct layer *phyif, - enum cfcnfg_phy_type phy_type, - bool fcs, bool stx) +int caifdev_phy_unregister(struct cflayer *phyif) { - /* Create the loop stack. */ - caifdev.loop = cfloopcfg_create(); - - /* Hook up the loop layer. */ - cfloopcfg_add_phy_layer(caifdev.loop, phy_type, phyif); - - return 0; -} -EXPORT_SYMBOL(caifdev_phy_loop_register); -#endif - -int caifdev_phy_unregister(struct layer *phyif) -{ - pr_warning("CAIF: caifdev_phy_unregister: phy:%p id:%d\n", + pr_debug("CAIF: caifdev_phy_unregister: phy:%p id:%d\n", phyif, phyif->id); cfcnfg_del_phy_layer(get_caif_conf(), phyif); return 0; @@ -301,6 +293,20 @@ void caif_unregister_chrdev() } EXPORT_SYMBOL(caif_unregister_chrdev); +int add_adaptation_layer(struct caif_channel_config *config, + struct cflayer *adap_layer) +{ + struct cfctrl_link_param param; + + if (channel_config_2_link_param(get_caif_conf(), config, ¶m) == 0) + /* Hook up the adaptation layer. */ + return cfcnfg_add_adaptation_layer(get_caif_conf(), + ¶m, adap_layer); + + return -EINVAL; +} +EXPORT_SYMBOL(add_adaptation_layer); + struct caif_packet_funcs cfcnfg_get_packet_funcs() { struct caif_packet_funcs f; diff --git a/net/caif/caif_config_util.c b/net/caif/caif_config_util.c index 9403711d932..e56c0a7f77a 100644 --- a/net/caif/caif_config_util.c +++ b/net/caif/caif_config_util.c @@ -1,14 +1,17 @@ /* - * Copyright (C) ST-Ericsson AB 2009 - * Author: Daniel Martensson / Daniel.Martensson@stericsson.com + * Copyright (C) ST-Ericsson AB 2010 + * Author: Sjur Brendeland sjur.brandeland@stericsson.com * License terms: GNU General Public License (GPL) version 2 */ -#include <net/caif/generic/cfglue.h> -#include <net/caif/generic/cfctrl.h> -#include <net/caif/generic/cfcnfg.h> -#include <linux/caif/caif_config.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <net/caif/cfctrl.h> +#include <net/caif/cfcnfg.h> +#include <net/caif/caif_dev.h> +//deprecated-functionality-below +#include <linux/caif/caif_config.h> int channel_config_2_link_param(struct cfcnfg *cnfg, struct caif_channel_config *s, struct cfctrl_link_param *l) @@ -34,11 +37,11 @@ channel_config_2_link_param(struct cfcnfg *cnfg, pref = CFPHYPREF_LOOP; break; default: - return -CFGLU_ENODEV; + return -ENODEV; } dev_info = cfcnfg_get_phyid(cnfg, pref); if (dev_info == NULL) - return -CFGLU_ENODEV; + return -ENODEV; l->phyid = dev_info->id; } switch (s->type) { @@ -114,8 +117,91 @@ channel_config_2_link_param(struct cfcnfg *cnfg, l->u.utility.paramlen); break; default: - return -CFGLU_EINVAL; + return -EINVAL; } return 0; } EXPORT_SYMBOL(channel_config_2_link_param); +//deprecated-functionality-above +int connect_req_to_link_param(struct cfcnfg *cnfg, + struct caif_connect_request *s, + struct cfctrl_link_param *l) +{ + struct dev_info *dev_info; + enum cfcnfg_phy_preference pref; + memset(l, 0, sizeof(*l)); + l->priority = s->priority; + + if (s->link_name[0] != '\0') + l->phyid = cfcnfg_get_named(cnfg, s->link_name); + else { + switch (s->link_selector) { + case CAIF_LINK_HIGH_BANDW: + pref = CFPHYPREF_HIGH_BW; + break; + case CAIF_LINK_LOW_LATENCY: + pref = CFPHYPREF_LOW_LAT; + break; + default: + return -EINVAL; + } + dev_info = cfcnfg_get_phyid(cnfg, pref); + if (dev_info == NULL) + return -ENODEV; + l->phyid = dev_info->id; + } + switch (s->protocol) { + case CAIFPROTO_AT: + l->linktype = CFCTRL_SRV_VEI; + if (s->sockaddr.u.at.type == CAIF_ATTYPE_PLAIN) + l->chtype = 0x02; + else + l->chtype = s->sockaddr.u.at.type; + l->endpoint = 0x00; + break; + case CAIFPROTO_DATAGRAM: + l->linktype = CFCTRL_SRV_DATAGRAM; + l->chtype = 0x00; + l->u.datagram.connid = s->sockaddr.u.dgm.connection_id; + break; + case CAIFPROTO_DATAGRAM_LOOP: + l->linktype = CFCTRL_SRV_DATAGRAM; + l->chtype = 0x03; + l->endpoint = 0x00; + l->u.datagram.connid = s->sockaddr.u.dgm.connection_id; + break; + case CAIFPROTO_RFM: + l->linktype = CFCTRL_SRV_RFM; + l->u.datagram.connid = s->sockaddr.u.rfm.connection_id; + strncpy(l->u.rfm.volume, s->sockaddr.u.rfm.volume, + sizeof(l->u.rfm.volume)-1); + l->u.rfm.volume[sizeof(l->u.rfm.volume)-1] = 0; + break; + case CAIFPROTO_UTIL: + l->linktype = CFCTRL_SRV_UTIL; + l->endpoint = 0x00; + l->chtype = 0x00; + strncpy(l->u.utility.name, s->sockaddr.u.util.service, + sizeof(l->u.utility.name)-1); + l->u.utility.name[sizeof(l->u.utility.name)-1] = 0; + caif_assert(sizeof(l->u.utility.name) > 10); + l->u.utility.paramlen = s->param.size; + if (l->u.utility.paramlen > sizeof(l->u.utility.params)) + l->u.utility.paramlen = sizeof(l->u.utility.params); + + memcpy(l->u.utility.params, s->param.data, + l->u.utility.paramlen); + + break; +//deprecated-functionality-below + case CAIFPROTO_DEBUG: + l->linktype = CFCTRL_SRV_DBG; + l->endpoint = s->sockaddr.u.dbg.service; + l->chtype = s->sockaddr.u.dbg.type; + break; +//deprecated-functionality-above + default: + return -EINVAL; + } + return 0; +} diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c index 3d55daca266..8c01f7dfbb4 100644 --- a/net/caif/caif_dev.c +++ b/net/caif/caif_dev.c @@ -1,6 +1,6 @@ /* * CAIF Interface registration. - * Copyright (C) ST-Ericsson AB 2009 + * Copyright (C) ST-Ericsson AB 2010 * Author: Sjur Brendeland/sjur.brandeland@stericsson.com * License terms: GNU General Public License (GPL) version 2 * @@ -23,26 +23,27 @@ #include <net/pkt_sched.h> #include <net/caif/caif_device.h> #include <net/caif/caif_dev.h> -#include <net/caif/generic/caif_layer.h> -#include <net/caif/generic/cfpkt.h> -#include <net/caif/generic/cfcnfg.h> +#include <net/caif/caif_layer.h> +#include <net/caif/cfpkt.h> +#include <net/caif/cfcnfg.h> MODULE_LICENSE("GPL"); -#define TIMEOUT (HZ*1000) +#define TIMEOUT (HZ*5) /* Used for local tracking of the CAIF net devices */ struct caif_device_entry { - struct layer layer; + struct cflayer layer; struct list_head list; atomic_t in_use; atomic_t state; - uint16 phyid; + u16 phyid; struct net_device *netdev; wait_queue_head_t event; }; struct caif_device_entry_list { struct list_head list; + /* Protects simulanous deletes in list */ spinlock_t lock; }; @@ -50,10 +51,10 @@ struct caif_net { struct caif_device_entry_list caifdevs; }; -int caif_net_id; -struct cfcnfg *cfg; +static int caif_net_id; +static struct cfcnfg *cfg; -struct caif_device_entry_list *caif_device_list(struct net *net) +static struct caif_device_entry_list *caif_device_list(struct net *net) { struct caif_net *caifn; BUG_ON(!net); @@ -114,7 +115,7 @@ static void caif_device_destroy(struct net_device *dev) return; } -static int transmit(struct layer *layer, struct cfpkt *pkt) +static int transmit(struct cflayer *layer, struct cfpkt *pkt) { struct caif_device_entry *caifd = container_of(layer, struct caif_device_entry, layer); @@ -141,7 +142,7 @@ static int transmit(struct layer *layer, struct cfpkt *pkt) return 0; } -static int modemcmd(struct layer *layr, enum caif_modemcmd ctrl) +static int modemcmd(struct cflayer *layr, enum caif_modemcmd ctrl) { struct caif_device_entry *caifd; struct caif_dev_common *caifdev; @@ -205,8 +206,8 @@ static int caif_device_notify(struct notifier_block *me, unsigned long what, struct net_device *dev = arg; struct caif_device_entry *caifd = NULL; struct caif_dev_common *caifdev; + enum cfcnfg_phy_preference pref; int res = -EINVAL; - enum caif_phy_preference phy_pref; enum cfcnfg_phy_type phy_type; if (dev->type != ARPHRD_CAIF) @@ -238,22 +239,30 @@ static int caif_device_notify(struct notifier_block *me, unsigned long what, atomic_set(&caifd->state, what); caifd->layer.transmit = transmit; caifd->layer.modemcmd = modemcmd; - if (caifdev->link_select == CAIF_PHYPREF_LOW_LAT) - phy_pref = CAIF_LINK_LOW_LATENCY; - else - phy_pref = CAIF_LINK_HIGH_BANDW; if (caifdev->use_frag) phy_type = CFPHYTYPE_FRAG; else phy_type = CFPHYTYPE_CAIF; + switch (caifdev->link_select) { + case CAIF_LINK_HIGH_BANDW: + pref = CFPHYPREF_HIGH_BW; + break; + case CAIF_LINK_LOW_LATENCY: + pref = CFPHYPREF_LOW_LAT; + break; + default: + pref = CFPHYPREF_HIGH_BW; + break; + } + cfcnfg_add_phy_layer(get_caif_conf(), phy_type, dev, &caifd->layer, &caifd->phyid, - phy_pref, + pref, caifdev->use_fcs, caifdev->use_stx); strncpy(caifd->layer.name, dev->name, @@ -266,6 +275,11 @@ static int caif_device_notify(struct notifier_block *me, unsigned long what, if (caifd == NULL) break; pr_info("CAIF: %s():going down %s\n", __func__, dev->name); + + if (atomic_read(&caifd->state) == NETDEV_GOING_DOWN || + atomic_read(&caifd->state) == NETDEV_DOWN) + break; + atomic_set(&caifd->state, what); if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) return -EINVAL; @@ -311,7 +325,7 @@ struct cfcnfg *get_caif_conf(void) return cfg; } EXPORT_SYMBOL(get_caif_conf); - +//deprecated-functionality-below static int (*caif_ioctl_func)(unsigned int cmd, unsigned long arg, bool); void caif_register_ioctl( @@ -331,29 +345,37 @@ int caif_ioctl(unsigned int cmd, unsigned long arg, bool from_user_land) } EXPORT_SYMBOL(caif_ioctl); -int caifdev_adapt_register(struct caif_channel_config *config, - struct layer *adap_layer) +//deprecated-functionality-above + +int caif_connect_client(struct caif_connect_request *conn_req, + struct cflayer *client_layer) { struct cfctrl_link_param param; + int ret; + ret = connect_req_to_link_param(get_caif_conf(), conn_req, ¶m); + if (ret) + return ret; + /* Hook up the adaptation layer. */ + return cfcnfg_add_adaptation_layer(get_caif_conf(), + ¶m, client_layer); +} +EXPORT_SYMBOL(caif_connect_client); - if (channel_config_2_link_param(get_caif_conf(), config, ¶m) == 0) - /* Hook up the adaptation layer. */ - return cfcnfg_add_adaptation_layer(get_caif_conf(), - ¶m, adap_layer); - - return -EINVAL; +int caif_disconnect_client(struct cflayer *adap_layer) +{ + return cfcnfg_disconn_adapt_layer(get_caif_conf(), adap_layer); } -EXPORT_SYMBOL(caifdev_adapt_register); +EXPORT_SYMBOL(caif_disconnect_client); -int caifdev_adapt_unregister(struct layer *adap_layer) +void caif_release_client(struct cflayer *adap_layer) { - return cfcnfg_del_adapt_layer(get_caif_conf(), adap_layer); + cfcnfg_release_adap_layer(adap_layer); } -EXPORT_SYMBOL(caifdev_adapt_unregister); +EXPORT_SYMBOL(caif_release_client); -//official-kernel-patch-cut-here +//deprecated-functionality-below #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)) -//official-kernel-patch-resume-here +//deprecated-functionality-above /* Per-namespace Caif devices handling */ static int caif_init_net(struct net *net) { @@ -383,7 +405,7 @@ static struct pernet_operations caif_net_ops = { .id = &caif_net_id, .size = sizeof(struct caif_net), }; -//official-kernel-patch-cut-here +//deprecated-functionality-below #else /* Per-namespace Caif devices handling */ @@ -425,10 +447,10 @@ static struct pernet_operations caif_net_ops = { }; #endif -//official-kernel-patch-resume-here +//deprecated-functionality-above /* Initialize Caif devices list */ -int __init caif_device_init(void) +static int __init caif_device_init(void) { int result; cfg = cfcnfg_create(); @@ -436,15 +458,15 @@ int __init caif_device_init(void) pr_warning("CAIF: %s(): can't create cfcnfg.\n", __func__); goto err_cfcnfg_create_failed; } -//official-kernel-patch-cut-here +//deprecated-functionality-below #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)) -//official-kernel-patch-resume-here +//deprecated-functionality-above result = register_pernet_device(&caif_net_ops); -//official-kernel-patch-cut-here +//deprecated-functionality-below #else - result = register_pernet_gen_device(&caif_net_id,&caif_net_ops); + result = register_pernet_gen_device(&caif_net_id, &caif_net_ops); #endif -//official-kernel-patch-resume-here +//deprecated-functionality-above if (result) { kfree(cfg); @@ -459,20 +481,20 @@ err_cfcnfg_create_failed: return -ENODEV; } -void __exit caif_device_exit(void) +static void __exit caif_device_exit(void) { - cfcnfg_remove(cfg); dev_remove_pack(&caif_packet_type); -//official-kernel-patch-cut-here +//deprecated-functionality-below #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)) -//official-kernel-patch-resume-here +//deprecated-functionality-above unregister_pernet_device(&caif_net_ops); -//official-kernel-patch-cut-here +//deprecated-functionality-below #else - unregister_pernet_gen_device(caif_net_id,&caif_net_ops); + unregister_pernet_gen_device(caif_net_id, &caif_net_ops); #endif -//official-kernel-patch-resume-here +//deprecated-functionality-above unregister_netdevice_notifier(&caif_device_notifier); + cfcnfg_remove(cfg); } module_init(caif_device_init); diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c index 85d38b65b28..d247b60496e 100644 --- a/net/caif/caif_socket.c +++ b/net/caif/caif_socket.c @@ -1,7 +1,6 @@ /* - * Copyright (C) ST-Ericsson AB 2009 - * Author: Sjur Brendeland sjur.brandlenad@stericsson.com - * Per Sigmond per.sigmond@stericsson.com + * Copyright (C) ST-Ericsson AB 2010 + * Author: Sjur Brendeland sjur.brandeland@stericsson.com * License terms: GNU General Public License (GPL) version 2 */ @@ -16,88 +15,74 @@ #include <linux/poll.h> #include <linux/tcp.h> #include <linux/uaccess.h> -#include <asm/atomic.h> - +#include <linux/mutex.h> +#include <linux/debugfs.h> #include <linux/caif/caif_socket.h> -#include <linux/caif/caif_config.h> -#include <net/caif/generic/caif_layer.h> +#include <asm/atomic.h> +#include <net/sock.h> +#include <net/tcp_states.h> +#include <net/caif/caif_layer.h> #include <net/caif/caif_dev.h> -#include <net/caif/generic/cfpkt.h> +#include <net/caif/cfpkt.h> -//official-kernel-patch-cut-here +MODULE_LICENSE("GPL"); +MODULE_ALIAS_NETPROTO(AF_CAIF); +//deprecated-functionality-below +#include <linux/version.h> #if !defined(trace_printk) #define trace_printk(fmt, ...) #endif -//official-kernel-patch-resume-here -MODULE_LICENSE("GPL"); +#include <linux/version.h> +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)) +static inline wait_queue_head_t *sk_sleep(struct sock *sk) +{ + return sk->sk_sleep; +} +#endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) + +/* Backward compability: copied from sock.h from 2.6.31 */ +static inline void sock_poll_wait(struct file *filp, + wait_queue_head_t *wait_address, poll_table * p) +{ + if (p && wait_address) { + poll_wait(filp, wait_address, p); + smp_mb(); + } +} +#endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)) +#define wake_up_interruptible_poll(x, m) \ + wake_up_interruptible_all(x) +#endif +//deprecated-functionality-above + +#define CAIF_DEF_SNDBUF (CAIF_MAX_PAYLOAD_SIZE*10) +#define CAIF_DEF_RCVBUF (CAIF_MAX_PAYLOAD_SIZE*100) + +/* + * CAIF state is re-using the TCP socket states. + * caif_states stored in sk_state reflect the state as reported by + * the CAIF stack, while sk_socket->state is the state of the socket. + */ +enum caif_states { + CAIF_CONNECTED = TCP_ESTABLISHED, + CAIF_CONNECTING = TCP_SYN_SENT, + CAIF_DISCONNECTED = TCP_CLOSE +}; + +#define TX_FLOW_ON_BIT 1 +#define RX_FLOW_ON_BIT 2 -#define CHNL_SKT_READ_QUEUE_HIGH 2000 -#define CHNL_SKT_READ_QUEUE_LOW 100 - -int caif_sockbuf_size = 40000; -static struct kmem_cache *caif_sk_cachep; -static atomic_t caif_nr_socks = ATOMIC_INIT(0); - -#define CONN_STATE_OPEN_BIT 1 -#define CONN_STATE_PENDING_BIT 2 -#define CONN_STATE_PEND_DESTROY_BIT 3 -#define CONN_REMOTE_SHUTDOWN_BIT 4 - -#define TX_FLOW_ON_BIT 1 -#define RX_FLOW_ON_BIT 2 - -#define STATE_IS_OPEN(cf_sk) test_bit(CONN_STATE_OPEN_BIT,\ - (void *) &(cf_sk)->conn_state) -#define STATE_IS_REMOTE_SHUTDOWN(cf_sk) test_bit(CONN_REMOTE_SHUTDOWN_BIT,\ - (void *) &(cf_sk)->conn_state) -#define STATE_IS_PENDING(cf_sk) test_bit(CONN_STATE_PENDING_BIT,\ - (void *) &(cf_sk)->conn_state) -#define STATE_IS_PENDING_DESTROY(cf_sk) test_bit(CONN_STATE_PEND_DESTROY_BIT,\ - (void *) &(cf_sk)->conn_state) - -#define SET_STATE_PENDING_DESTROY(cf_sk) set_bit(CONN_STATE_PEND_DESTROY_BIT,\ - (void *) &(cf_sk)->conn_state) -#define SET_STATE_OPEN(cf_sk) set_bit(CONN_STATE_OPEN_BIT,\ - (void *) &(cf_sk)->conn_state) -#define SET_STATE_CLOSED(cf_sk) clear_bit(CONN_STATE_OPEN_BIT,\ - (void *) &(cf_sk)->conn_state) -#define SET_PENDING_ON(cf_sk) set_bit(CONN_STATE_PENDING_BIT,\ - (void *) &(cf_sk)->conn_state) -#define SET_PENDING_OFF(cf_sk) clear_bit(CONN_STATE_PENDING_BIT,\ - (void *) &(cf_sk)->conn_state) -#define SET_REMOTE_SHUTDOWN(cf_sk) set_bit(CONN_REMOTE_SHUTDOWN_BIT,\ - (void *) &(cf_sk)->conn_state) - -#define SET_REMOTE_SHUTDOWN_OFF(dev) clear_bit(CONN_REMOTE_SHUTDOWN_BIT,\ - (void *) &(dev)->conn_state) -#define RX_FLOW_IS_ON(cf_sk) test_bit(RX_FLOW_ON_BIT,\ - (void *) &(cf_sk)->flow_state) -#define TX_FLOW_IS_ON(cf_sk) test_bit(TX_FLOW_ON_BIT,\ - (void *) &(cf_sk)->flow_state) - -#define SET_RX_FLOW_OFF(cf_sk) clear_bit(RX_FLOW_ON_BIT,\ - (void *) &(cf_sk)->flow_state) -#define SET_RX_FLOW_ON(cf_sk) set_bit(RX_FLOW_ON_BIT,\ - (void *) &(cf_sk)->flow_state) -#define SET_TX_FLOW_OFF(cf_sk) clear_bit(TX_FLOW_ON_BIT,\ - (void *) &(cf_sk)->flow_state) -#define SET_TX_FLOW_ON(cf_sk) set_bit(TX_FLOW_ON_BIT,\ - (void *) &(cf_sk)->flow_state) - -#define SKT_READ_FLAG 0x01 -#define SKT_WRITE_FLAG 0x02 static struct dentry *debugfsdir; -#include <linux/debugfs.h> #ifdef CONFIG_DEBUG_FS struct debug_fs_counter { - atomic_t num_open; - atomic_t num_close; - atomic_t num_init; - atomic_t num_init_resp; - atomic_t num_init_fail_resp; - atomic_t num_deinit; - atomic_t num_deinit_resp; + atomic_t caif_nr_socks; + atomic_t num_connect_req; + atomic_t num_connect_resp; + atomic_t num_connect_fail_resp; + atomic_t num_disconnect; atomic_t num_remote_shutdown_ind; atomic_t num_tx_flow_off_ind; atomic_t num_tx_flow_on_ind; @@ -106,1080 +91,999 @@ struct debug_fs_counter { }; struct debug_fs_counter cnt; #define dbfs_atomic_inc(v) atomic_inc(v) +#define dbfs_atomic_dec(v) atomic_dec(v) #else #define dbfs_atomic_inc(v) +#define dbfs_atomic_dec(v) #endif -/* The AF_CAIF socket */ struct caifsock { - /* NOTE: sk has to be the first member */ - struct sock sk; - struct layer layer; - char name[CAIF_LAYER_NAME_SZ]; - u32 conn_state; + struct sock sk; /* must be first member */ + struct cflayer layer; + char name[CAIF_LAYER_NAME_SZ]; /* Used for debugging */ u32 flow_state; - struct cfpktq *pktq; - int file_mode; - struct caif_channel_config config; - int read_queue_len; - spinlock_t read_queue_len_lock; + struct caif_connect_request conn_req; + struct mutex readlock; struct dentry *debugfs_socket_dir; }; -static void drain_queue(struct caifsock *cf_sk); +static int rx_flow_is_on(struct caifsock *cf_sk) +{ + return test_bit(RX_FLOW_ON_BIT, + (void *) &cf_sk->flow_state); +} -/* Packet Receive Callback function called from CAIF Stack */ -static int caif_sktrecv_cb(struct layer *layr, struct cfpkt *pkt) +static int tx_flow_is_on(struct caifsock *cf_sk) { - struct caifsock *cf_sk; - int read_queue_high; - cf_sk = container_of(layr, struct caifsock, layer); + return test_bit(TX_FLOW_ON_BIT, + (void *) &cf_sk->flow_state); +} - if (!STATE_IS_OPEN(cf_sk)) { - /*FIXME: This should be allowed finally!*/ - pr_debug("CAIF: %s(): called after close request\n", __func__); - cfpkt_destroy(pkt); - return 0; - } - /* NOTE: This function may be called in Tasklet context! */ +static void set_rx_flow_off(struct caifsock *cf_sk) +{ + clear_bit(RX_FLOW_ON_BIT, + (void *) &cf_sk->flow_state); +} + +static void set_rx_flow_on(struct caifsock *cf_sk) +{ + set_bit(RX_FLOW_ON_BIT, + (void *) &cf_sk->flow_state); +} - /* The queue has its own lock */ - cfpkt_queue(cf_sk->pktq, pkt, 0); +static void set_tx_flow_off(struct caifsock *cf_sk) +{ + clear_bit(TX_FLOW_ON_BIT, + (void *) &cf_sk->flow_state); +} - spin_lock(&cf_sk->read_queue_len_lock); - cf_sk->read_queue_len++; +static void set_tx_flow_on(struct caifsock *cf_sk) +{ + set_bit(TX_FLOW_ON_BIT, + (void *) &cf_sk->flow_state); +} - read_queue_high = (cf_sk->read_queue_len > CHNL_SKT_READ_QUEUE_HIGH); - spin_unlock(&cf_sk->read_queue_len_lock); +static void caif_read_lock(struct sock *sk) +{ + struct caifsock *cf_sk; + cf_sk = container_of(sk, struct caifsock, sk); + mutex_lock(&cf_sk->readlock); +} - if (RX_FLOW_IS_ON(cf_sk) && read_queue_high) { - dbfs_atomic_inc(&cnt.num_rx_flow_off); - SET_RX_FLOW_OFF(cf_sk); +static void caif_read_unlock(struct sock *sk) +{ + struct caifsock *cf_sk; + cf_sk = container_of(sk, struct caifsock, sk); + mutex_unlock(&cf_sk->readlock); +} + +int sk_rcvbuf_lowwater(struct caifsock *cf_sk) +{ + /* A quarter of full buffer is used a low water mark */ + return cf_sk->sk.sk_rcvbuf / 4; +} + +void caif_flow_ctrl(struct sock *sk, int mode) +{ + struct caifsock *cf_sk; + cf_sk = container_of(sk, struct caifsock, sk); + if (cf_sk->layer.dn) + cf_sk->layer.dn->modemcmd(cf_sk->layer.dn, mode); +} + +/* + * Copied from sock.c:sock_queue_rcv_skb(), but changed so packets are + * not dropped, but CAIF is sending flow off instead. + */ +int caif_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) +{ + int err; + int skb_len; + unsigned long flags; + struct sk_buff_head *list = &sk->sk_receive_queue; + struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - /* Send flow off (NOTE: must not sleep) */ - pr_debug("CAIF: %s():" - " sending flow OFF (queue len = %d)\n", + if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >= + (unsigned)sk->sk_rcvbuf && rx_flow_is_on(cf_sk)) { + trace_printk("CAIF: %s():" + " sending flow OFF (queue len = %d %d)\n", __func__, - cf_sk->read_queue_len); - caif_assert(cf_sk->layer.dn); - caif_assert(cf_sk->layer.dn->ctrlcmd); + atomic_read(&cf_sk->sk.sk_rmem_alloc), + sk_rcvbuf_lowwater(cf_sk)); + set_rx_flow_off(cf_sk); + if (cf_sk->layer.dn) + cf_sk->layer.dn->modemcmd(cf_sk->layer.dn, + CAIF_MODEMCMD_FLOW_OFF_REQ); + } - (void) cf_sk->layer.dn->modemcmd(cf_sk->layer.dn, - CAIF_MODEMCMD_FLOW_OFF_REQ); + err = sk_filter(sk, skb); + if (err) + return err; + if (!sk_rmem_schedule(sk, skb->truesize) && rx_flow_is_on(cf_sk)) { + set_rx_flow_off(cf_sk); + trace_printk("CAIF: %s():" + " sending flow OFF due to rmem_schedule\n", + __func__); + if (cf_sk->layer.dn) + cf_sk->layer.dn->modemcmd(cf_sk->layer.dn, + CAIF_MODEMCMD_FLOW_OFF_REQ); } + skb->dev = NULL; + skb_set_owner_r(skb, sk); + /* Cache the SKB length before we tack it onto the receive + * queue. Once it is added it no longer belongs to us and + * may be freed by other threads of control pulling packets + * from the queue. + */ + skb_len = skb->len; + spin_lock_irqsave(&list->lock, flags); + if (!sock_flag(sk, SOCK_DEAD)) + __skb_queue_tail(list, skb); + spin_unlock_irqrestore(&list->lock, flags); + + if (!sock_flag(sk, SOCK_DEAD)) + sk->sk_data_ready(sk, skb_len); + else + kfree_skb(skb); + return 0; +} - /* Signal reader that data is available. */ +/* Packet Receive Callback function called from CAIF Stack */ +static int caif_sktrecv_cb(struct cflayer *layr, struct cfpkt *pkt) +{ + struct caifsock *cf_sk; + struct sk_buff *skb; - wake_up_interruptible(cf_sk->sk.sk_sleep); + cf_sk = container_of(layr, struct caifsock, layer); + skb = cfpkt_tonative(pkt); + if (unlikely(cf_sk->sk.sk_state != CAIF_CONNECTED)) { + cfpkt_destroy(pkt); + return 0; + } + caif_queue_rcv_skb(&cf_sk->sk, skb); return 0; } -/* Packet Flow Control Callback function called from CAIF */ -static void caif_sktflowctrl_cb(struct layer *layr, +/* Packet Control Callback function called from CAIF */ +static void caif_ctrl_cb(struct cflayer *layr, enum caif_ctrlcmd flow, int phyid) { - struct caifsock *cf_sk; - - /* NOTE: This function may be called in Tasklet context! */ - trace_printk("CAIF: %s(): flowctrl func called: %s.\n", - __func__, - flow == CAIF_CTRLCMD_FLOW_ON_IND ? "ON" : - flow == CAIF_CTRLCMD_FLOW_OFF_IND ? "OFF" : - flow == CAIF_CTRLCMD_INIT_RSP ? "INIT_RSP" : - flow == CAIF_CTRLCMD_DEINIT_RSP ? "DEINIT_RSP" : - flow == CAIF_CTRLCMD_INIT_FAIL_RSP ? "INIT_FAIL_RSP" : - flow == - CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND ? "REMOTE_SHUTDOWN" : - "UKNOWN CTRL COMMAND"); - - cf_sk = container_of(layr, struct caifsock, layer); + struct caifsock *cf_sk = container_of(layr, struct caifsock, layer); switch (flow) { case CAIF_CTRLCMD_FLOW_ON_IND: + /* OK from modem to start sending again */ dbfs_atomic_inc(&cnt.num_tx_flow_on_ind); - /* Signal reader that data is available. */ - SET_TX_FLOW_ON(cf_sk); - wake_up_interruptible(cf_sk->sk.sk_sleep); + set_tx_flow_on(cf_sk); + cf_sk->sk.sk_state_change(&cf_sk->sk); break; case CAIF_CTRLCMD_FLOW_OFF_IND: + /* Modem asks us to shut up */ dbfs_atomic_inc(&cnt.num_tx_flow_off_ind); - SET_TX_FLOW_OFF(cf_sk); + set_tx_flow_off(cf_sk); + cf_sk->sk.sk_state_change(&cf_sk->sk); break; case CAIF_CTRLCMD_INIT_RSP: - dbfs_atomic_inc(&cnt.num_init_resp); - /* Signal reader that data is available. */ - caif_assert(STATE_IS_OPEN(cf_sk)); - SET_PENDING_OFF(cf_sk); - SET_TX_FLOW_ON(cf_sk); - wake_up_interruptible(cf_sk->sk.sk_sleep); + /* We're now connected */ + dbfs_atomic_inc(&cnt.num_connect_resp); + cf_sk->sk.sk_state = CAIF_CONNECTED; + set_tx_flow_on(cf_sk); + cf_sk->sk.sk_state_change(&cf_sk->sk); break; case CAIF_CTRLCMD_DEINIT_RSP: - dbfs_atomic_inc(&cnt.num_deinit_resp); - caif_assert(!STATE_IS_OPEN(cf_sk)); - SET_PENDING_OFF(cf_sk); - if (!STATE_IS_PENDING_DESTROY(cf_sk)) { - if (cf_sk->sk.sk_sleep != NULL) - wake_up_interruptible(cf_sk->sk.sk_sleep); - } - sock_put(&cf_sk->sk); + /* We're now disconnected */ + cf_sk->sk.sk_state = CAIF_DISCONNECTED; + cf_sk->sk.sk_state_change(&cf_sk->sk); + cfcnfg_release_adap_layer(&cf_sk->layer); break; case CAIF_CTRLCMD_INIT_FAIL_RSP: - dbfs_atomic_inc(&cnt.num_init_fail_resp); - caif_assert(STATE_IS_OPEN(cf_sk)); - SET_STATE_CLOSED(cf_sk); - SET_PENDING_OFF(cf_sk); - SET_TX_FLOW_OFF(cf_sk); - wake_up_interruptible(cf_sk->sk.sk_sleep); + /* Connect request failed */ + dbfs_atomic_inc(&cnt.num_connect_fail_resp); + cf_sk->sk.sk_err = ECONNREFUSED; + cf_sk->sk.sk_state = CAIF_DISCONNECTED; + cf_sk->sk.sk_shutdown = SHUTDOWN_MASK; + /* + * Socket "standards" seems to require POLLOUT to + * be set at connect failure. + */ + set_tx_flow_on(cf_sk); + cf_sk->sk.sk_state_change(&cf_sk->sk); break; case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND: + /* Modem has closed this connection, or device is down. */ dbfs_atomic_inc(&cnt.num_remote_shutdown_ind); - caif_assert(STATE_IS_OPEN(cf_sk)); - caif_assert(STATE_IS_PENDING(cf_sk)); - SET_REMOTE_SHUTDOWN(cf_sk); - SET_TX_FLOW_OFF(cf_sk); - drain_queue(cf_sk); - SET_RX_FLOW_ON(cf_sk); - cf_sk->file_mode = 0; - wake_up_interruptible(cf_sk->sk.sk_sleep); + cf_sk->sk.sk_shutdown = SHUTDOWN_MASK; + cf_sk->sk.sk_err = ECONNRESET; + set_rx_flow_on(cf_sk); + cf_sk->sk.sk_error_report(&cf_sk->sk); break; default: pr_debug("CAIF: %s(): Unexpected flow command %d\n", - __func__, flow); + __func__, flow); } } - - -static struct sk_buff *caif_alloc_send_skb(struct sock *sk, - unsigned long data_len, - int *err) +static void caif_check_flow_release(struct sock *sk) { - struct sk_buff *skb; - unsigned int sk_allocation = sk->sk_allocation; - - sk->sk_allocation |= GFP_KERNEL; - - /* Allocate a buffer structure to hold the signal. */ - skb = sock_alloc_send_skb(sk, data_len, 1, err); + struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - sk->sk_allocation = sk_allocation; + if (cf_sk->layer.dn == NULL || cf_sk->layer.dn->modemcmd == NULL) + return; + if (rx_flow_is_on(cf_sk)) + return; - return skb; + if (atomic_read(&sk->sk_rmem_alloc) <= sk_rcvbuf_lowwater(cf_sk)) { + dbfs_atomic_inc(&cnt.num_rx_flow_on); + set_rx_flow_on(cf_sk); + cf_sk->layer.dn->modemcmd(cf_sk->layer.dn, + CAIF_MODEMCMD_FLOW_ON_REQ); + } } +/* + * Copied from sock.c:sock_queue_rcv_skb(), and added check that user buffer + * has sufficient size. + */ -static int caif_recvmsg(struct kiocb *iocb, struct socket *sock, +static int caif_seqpkt_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m, size_t buf_len, int flags) { struct sock *sk = sock->sk; - struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - struct cfpkt *pkt = NULL; - size_t len; - int result; struct sk_buff *skb; - ssize_t ret = -EIO; - int read_queue_low; - - if (cf_sk == NULL) { - pr_debug("CAIF: %s(): private_data not set!\n", - __func__); - ret = -EBADFD; - goto read_error; - } - - /* Don't do multiple iovec entries yet */ - if (m->msg_iovlen != 1) - return -EOPNOTSUPP; + int ret = 0; + int len; if (unlikely(!buf_len)) return -EINVAL; - lock_sock(&(cf_sk->sk)); - - caif_assert(cf_sk->pktq); - - if (!STATE_IS_OPEN(cf_sk)) { - /* Socket is closed or closing. */ - if (!STATE_IS_PENDING(cf_sk)) { - pr_debug("CAIF: %s(): socket is closed (by remote)\n", - __func__); - ret = -EPIPE; - } else { - pr_debug("CAIF: %s(): socket is closing..\n", __func__); - ret = -EBADF; - } + skb = skb_recv_datagram(sk, flags, 0 , &ret); + if (!skb) goto read_error; - } - - /* Socket is open or opening. */ - if (STATE_IS_PENDING(cf_sk)) { - pr_debug("CAIF: %s(): socket is opening...\n", __func__); - if (flags & MSG_DONTWAIT) { - /* We can't block. */ - pr_debug("CAIF: %s():state pending and MSG_DONTWAIT\n", - __func__); - ret = -EAGAIN; - goto read_error; - } + len = skb->len; + if (skb && skb->len > buf_len && !(flags & MSG_PEEK)) { + len = buf_len; /* - * Blocking mode; state is pending and we need to wait - * for its conclusion. + * Push skb back on receive queue if buffer too small. + * This has a built-in race where multi-threaded receive + * may get packet in wrong order, but multiple read does + * not really guarantee ordered delivery anyway. + * Let's optimize for speed without taking locks. */ - release_sock(&cf_sk->sk); - - result = - wait_event_interruptible(*cf_sk->sk.sk_sleep, - !STATE_IS_PENDING(cf_sk)); - - lock_sock(&(cf_sk->sk)); - - if (result == -ERESTARTSYS) { - pr_debug("CAIF: %s(): wait_event_interruptible" - " woken by a signal (1)", __func__); - ret = -ERESTARTSYS; - goto read_error; - } - } - - if (STATE_IS_REMOTE_SHUTDOWN(cf_sk)) { - pr_debug("CAIF: %s(): received remote_shutdown indication\n", - __func__); - ret = -ESHUTDOWN; - goto read_error; - } - - /* - * Block if we don't have any received buffers. - * The queue has its own lock. - */ - while ((pkt = cfpkt_qpeek(cf_sk->pktq)) == NULL) { - - if (flags & MSG_DONTWAIT) { - pr_debug("CAIF: %s(): MSG_DONTWAIT\n", __func__); - ret = -EAGAIN; - goto read_error; - } - pr_debug("CAIF: %s() wait_event\n", __func__); - /* Let writers in. */ - release_sock(&cf_sk->sk); - - /* Block reader until data arrives or socket is closed. */ - if (wait_event_interruptible(*cf_sk->sk.sk_sleep, - cfpkt_qpeek(cf_sk->pktq) - || STATE_IS_REMOTE_SHUTDOWN(cf_sk) - || !STATE_IS_OPEN(cf_sk)) == - -ERESTARTSYS) { - pr_debug("CAIF: %s():" - " wait_event_interruptible woken by " - "a signal, signal_pending(current) = %d\n", - __func__, - signal_pending(current)); - return -ERESTARTSYS; - } - - pr_debug("CAIF: %s() awake\n", __func__); - if (STATE_IS_REMOTE_SHUTDOWN(cf_sk)) { - pr_debug("CAIF: %s(): " - "received remote_shutdown indication\n", - __func__); - ret = -ESHUTDOWN; - goto read_error_no_unlock; - } - - /* I want to be alone on cf_sk (except status and queue). */ - lock_sock(&(cf_sk->sk)); - - if (!STATE_IS_OPEN(cf_sk)) { - /* Someone closed the link, report error. */ - pr_debug("CAIF: %s(): remote end shutdown!\n", - __func__); - ret = -EPIPE; - goto read_error; - } - } - - /* The queue has its own lock. */ - len = cfpkt_getlen(pkt); - - /* Check max length that can be copied. */ - if (len > buf_len) { - pr_debug("CAIF: %s(): user buffer too small\n", __func__); - ret = -EINVAL; + skb_queue_head(&sk->sk_receive_queue, skb); + ret = -EMSGSIZE; goto read_error; } - /* - * Get packet from queue. - * The queue has its own lock. - */ - pkt = cfpkt_dequeue(cf_sk->pktq); - - spin_lock(&cf_sk->read_queue_len_lock); - cf_sk->read_queue_len--; - read_queue_low = (cf_sk->read_queue_len < CHNL_SKT_READ_QUEUE_LOW); - spin_unlock(&cf_sk->read_queue_len_lock); - - if (!RX_FLOW_IS_ON(cf_sk) && read_queue_low) { - dbfs_atomic_inc(&cnt.num_rx_flow_on); - SET_RX_FLOW_ON(cf_sk); - - /* Send flow on. */ - pr_debug("CAIF: %s(): sending flow ON (queue len = %d)\n", - __func__, cf_sk->read_queue_len); - caif_assert(cf_sk->layer.dn); - caif_assert(cf_sk->layer.dn->ctrlcmd); - (void) cf_sk->layer.dn->modemcmd(cf_sk->layer.dn, - CAIF_MODEMCMD_FLOW_ON_REQ); - - caif_assert(cf_sk->read_queue_len >= 0); - } - skb = cfpkt_tonative(pkt); - result = skb_copy_datagram_iovec(skb, 0, m->msg_iov, len); - if (result) { - pr_debug("CAIF: %s(): cfpkt_raw_extract failed\n", __func__); - cfpkt_destroy(pkt); - ret = -EFAULT; + ret = skb_copy_datagram_iovec(skb, 0, m->msg_iov, len); + if (ret) goto read_error; - } - if (unlikely(buf_len < len)) { - len = buf_len; - m->msg_flags |= MSG_TRUNC; - } - /* Free packet. */ skb_free_datagram(sk, skb); - /* Let the others in. */ - release_sock(&cf_sk->sk); + caif_check_flow_release(sk); + return len; read_error: - release_sock(&cf_sk->sk); -read_error_no_unlock: return ret; } -/* Send a signal as a consequence of sendmsg, sendto or caif_sendmsg. */ -static int caif_sendmsg(struct kiocb *kiocb, struct socket *sock, - struct msghdr *msg, size_t len) -{ - struct sock *sk = sock->sk; - struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - void *payload; - size_t payload_size = msg->msg_iov->iov_len; - struct cfpkt *pkt = NULL; - struct payload_info info; - unsigned char *txbuf; - int err = 0; - ssize_t ret = -EIO; - int result; - struct sk_buff *skb; - caif_assert(msg->msg_iovlen == 1); +/* Copied from unix_stream_wait_data, identical except for lock call. */ +static long caif_stream_data_wait(struct sock *sk, long timeo) +{ + DEFINE_WAIT(wait); + lock_sock(sk); + + for (;;) { + prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); + + if (!skb_queue_empty(&sk->sk_receive_queue) || + sk->sk_err || + sk->sk_state != CAIF_CONNECTED || + sock_flag(sk, SOCK_DEAD) || + (sk->sk_shutdown & RCV_SHUTDOWN) || + signal_pending(current) || + !timeo) + break; - if (cf_sk == NULL) { - pr_debug("CAIF: %s(): private_data not set!\n", - __func__); - ret = -EBADFD; - goto write_error_no_unlock; + set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags); + release_sock(sk); + timeo = schedule_timeout(timeo); + lock_sock(sk); + clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags); } - if (unlikely(msg->msg_iov->iov_base == NULL)) { - pr_warning("CAIF: %s(): Buffer is NULL.\n", __func__); - ret = -EINVAL; - goto write_error_no_unlock; - } + finish_wait(sk_sleep(sk), &wait); + release_sock(sk); + return timeo; +} - payload = msg->msg_iov->iov_base; - if (payload_size > CAIF_MAX_PAYLOAD_SIZE) { - pr_debug("CAIF: %s(): buffer too long\n", __func__); - ret = -EINVAL; - goto write_error_no_unlock; - } - /* I want to be alone on cf_sk (except status and queue) */ - lock_sock(&(cf_sk->sk)); - caif_assert(cf_sk->pktq); +/* + * Copied from unix_stream_recvmsg, but removed credit checks, + * changed locking calls, changed address handling. + */ +static int caif_stream_recvmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t size, + int flags) +{ + struct sock *sk = sock->sk; + int copied = 0; + int target; + int err = 0; + long timeo; - if (!STATE_IS_OPEN(cf_sk)) { - /* Socket is closed or closing */ - if (!STATE_IS_PENDING(cf_sk)) { - pr_debug("CAIF: %s(): socket is closed (by remote)\n", - __func__); - ret = -EPIPE; - } else { - pr_debug("CAIF: %s(): socket is closing...\n", - __func__); - ret = -EBADF; - } - goto write_error; - } + err = -EOPNOTSUPP; + if (flags&MSG_OOB) + goto out; - /* Socket is open or opening */ - if (STATE_IS_PENDING(cf_sk)) { - pr_debug("CAIF: %s(): socket is opening...\n", __func__); + msg->msg_namelen = 0; - if (msg->msg_flags & MSG_DONTWAIT) { - /* We can't block */ - trace_printk("CAIF: %s():state pending:" - "state=MSG_DONTWAIT\n", __func__); - ret = -EAGAIN; - goto write_error; - } + /* + * Lock the socket to prevent queue disordering + * while sleeps in memcpy_tomsg + */ + err = -EAGAIN; + if (sk->sk_state == CAIF_CONNECTING) + goto out; - /* Let readers in */ - release_sock(&cf_sk->sk); + caif_read_lock(sk); + target = sock_rcvlowat(sk, flags&MSG_WAITALL, size); + timeo = sock_rcvtimeo(sk, flags&MSG_DONTWAIT); - /* - * Blocking mode; state is pending and we need to wait - * for its conclusion. - */ - result = - wait_event_interruptible(*cf_sk->sk.sk_sleep, - !STATE_IS_PENDING(cf_sk)); + do { + int chunk; + struct sk_buff *skb; - /* I want to be alone on cf_sk (except status and queue) */ - lock_sock(&(cf_sk->sk)); + lock_sock(sk); + skb = skb_dequeue(&sk->sk_receive_queue); + caif_check_flow_release(sk); - if (result == -ERESTARTSYS) { - pr_debug("CAIF: %s(): wait_event_interruptible" - " woken by a signal (1)", __func__); - ret = -ERESTARTSYS; - goto write_error; - } - } + if (skb == NULL) { + if (copied >= target) + goto unlock; + /* + * POSIX 1003.1g mandates this order. + */ + err = sock_error(sk); + if (err) + goto unlock; + err = -ECONNRESET; + if (sk->sk_shutdown & RCV_SHUTDOWN) + goto unlock; - if (STATE_IS_REMOTE_SHUTDOWN(cf_sk)) { - pr_debug("CAIF: %s(): received remote_shutdown indication\n", - __func__); - ret = -ESHUTDOWN; - goto write_error; - } + err = -EPIPE; + if (sk->sk_state != CAIF_CONNECTED) + goto unlock; + if (sock_flag(sk, SOCK_DEAD)) + goto unlock; - if (!TX_FLOW_IS_ON(cf_sk)) { + release_sock(sk); - /* Flow is off. Check non-block flag */ - if (msg->msg_flags & MSG_DONTWAIT) { - trace_printk("CAIF: %s(): MSG_DONTWAIT and tx flow off", - __func__); - ret = -EAGAIN; - goto write_error; - } + err = -EAGAIN; + if (!timeo) + break; - /* release lock before waiting */ - release_sock(&cf_sk->sk); + caif_read_unlock(sk); - /* Wait until flow is on or socket is closed */ - if (wait_event_interruptible(*cf_sk->sk.sk_sleep, - TX_FLOW_IS_ON(cf_sk) - || !STATE_IS_OPEN(cf_sk) - || STATE_IS_REMOTE_SHUTDOWN(cf_sk) - ) == -ERESTARTSYS) { - pr_debug("CAIF: %s():" - " wait_event_interruptible woken by a signal", - __func__); - ret = -ERESTARTSYS; - goto write_error_no_unlock; - } + timeo = caif_stream_data_wait(sk, timeo); - /* I want to be alone on cf_sk (except status and queue) */ - lock_sock(&(cf_sk->sk)); - - if (!STATE_IS_OPEN(cf_sk)) { - /* someone closed the link, report error */ - pr_debug("CAIF: %s(): remote end shutdown!\n", - __func__); - ret = -EPIPE; - goto write_error; + if (signal_pending(current)) { + err = sock_intr_errno(timeo); + goto out; + } + caif_read_lock(sk); + continue; +unlock: + release_sock(sk); + break; } - - if (STATE_IS_REMOTE_SHUTDOWN(cf_sk)) { - pr_debug("CAIF: %s(): " - "received remote_shutdown indication\n", - __func__); - ret = -ESHUTDOWN; - goto write_error; + release_sock(sk); + chunk = min_t(unsigned int, skb->len, size); + if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) { + skb_queue_head(&sk->sk_receive_queue, skb); + if (copied == 0) + copied = -EFAULT; + break; } - } + copied += chunk; + size -= chunk; - /* Create packet, buf=NULL means no copying */ - skb = caif_alloc_send_skb(sk, - payload_size + CAIF_NEEDED_HEADROOM + - CAIF_NEEDED_TAILROOM, - &err); - - if (skb == NULL) { - pr_debug("CAIF: %s(): caif_alloc_send_skb returned NULL\n", - __func__); - ret = -ENOMEM; - goto write_error; - } + /* Mark read part of skb as used */ + if (!(flags & MSG_PEEK)) { + skb_pull(skb, chunk); - skb_reserve(skb, CAIF_NEEDED_HEADROOM); - pkt = cfpkt_fromnative(CAIF_DIR_OUT, skb); - caif_assert((void *)pkt == (void *)skb); + /* put the skb back if we didn't use it up. */ + if (skb->len) { + skb_queue_head(&sk->sk_receive_queue, skb); + break; + } + kfree_skb(skb); - if (cfpkt_raw_append(pkt, (void **) &txbuf, payload_size) < 0) { - pr_debug("CAIF: %s(): cfpkt_raw_append failed\n", __func__); - cfpkt_destroy(pkt); - ret = -EINVAL; - goto write_error; - } + } else { + /* + * It is questionable, see note in unix_dgram_recvmsg. + */ + /* put message back and return */ + skb_queue_head(&sk->sk_receive_queue, skb); + break; + } + } while (size); + caif_read_unlock(sk); - /* Copy data into buffer. */ - if (copy_from_user(txbuf, payload, payload_size)) { - pr_debug("CAIF: %s(): copy_from_user returned non zero.\n", - __func__); - cfpkt_destroy(pkt); - ret = -EINVAL; - goto write_error; +out: + return copied ? : err; +} + +/* + * Copied from sock.c:sock_wait_for_wmem, but change to wait for + * CAIF flow-on and sock_writable. + */ +static long caif_wait_for_flow_on(struct caifsock *cf_sk, + int wait_writeable, long timeo, int *err) +{ + struct sock *sk = &cf_sk->sk; + DEFINE_WAIT(wait); + for (;;) { + *err = 0; + if (tx_flow_is_on(cf_sk) && + (!wait_writeable || sock_writeable(&cf_sk->sk))) + break; + *err = -ETIMEDOUT; + if (!timeo) + break; + *err = -ERESTARTSYS; + if (signal_pending(current)) + break; + prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); + *err = -ECONNRESET; + if (sk->sk_shutdown & SHUTDOWN_MASK) + break; + *err = -sk->sk_err; + if (sk->sk_err) + break; + *err = -EPIPE; + if (cf_sk->sk.sk_state != CAIF_CONNECTED) + break; + timeo = schedule_timeout(timeo); } - memset(&info, 0, sizeof(info)); + finish_wait(sk_sleep(sk), &wait); + return timeo; +} - /* Send the packet down the stack. */ - caif_assert(cf_sk->layer.dn); - caif_assert(cf_sk->layer.dn->transmit); +/* + * Transmit a SKB. The device may temporarily request re-transmission + * by returning EAGAIN. + */ +static int transmit_skb(struct sk_buff *skb, struct caifsock *cf_sk, + int noblock, long timeo) +{ + struct cfpkt *pkt; + int ret, loopcnt = 0; + pkt = cfpkt_fromnative(CAIF_DIR_OUT, skb); + memset(cfpkt_info(pkt), 0, sizeof(struct caif_payload_info)); do { - ret = cf_sk->layer.dn->transmit(cf_sk->layer.dn, pkt); - if (likely((ret >= 0) || (ret != -EAGAIN))) - break; + ret = -ETIMEDOUT; - /* EAGAIN - retry */ - if (msg->msg_flags & MSG_DONTWAIT) { - pr_debug("CAIF: %s(): NONBLOCK and transmit failed," - " error = %d\n", __func__, ret); - ret = -EAGAIN; - goto write_error; + /* Slight paranoia, probably not needed. */ + if (unlikely(loopcnt++ > 1000)) { + pr_warning("CAIF: %s(): transmit retries failed," + " error = %d\n", __func__, ret); + break; } - /* Let readers in */ - release_sock(&cf_sk->sk); - - /* Wait until flow is on or socket is closed */ - if (wait_event_interruptible(*cf_sk->sk.sk_sleep, - TX_FLOW_IS_ON(cf_sk) - || !STATE_IS_OPEN(cf_sk) - || STATE_IS_REMOTE_SHUTDOWN(cf_sk) - ) == -ERESTARTSYS) { - pr_debug("CAIF: %s(): wait_event_interruptible" - " woken by a signal", __func__); - ret = -ERESTARTSYS; - goto write_error_no_unlock; + if (cf_sk->layer.dn != NULL) + ret = cf_sk->layer.dn->transmit(cf_sk->layer.dn, pkt); + if (likely(ret >= 0)) + break; + /* if transmit return -EAGAIN, then retry */ + if (noblock && ret == -EAGAIN) + break; + timeo = caif_wait_for_flow_on(cf_sk, 0, timeo, &ret); + if (signal_pending(current)) { + ret = sock_intr_errno(timeo); + break; + } + if (ret) + break; + if (cf_sk->sk.sk_state != CAIF_CONNECTED || + sock_flag(&cf_sk->sk, SOCK_DEAD) || + (cf_sk->sk.sk_shutdown & RCV_SHUTDOWN)) { + ret = -EPIPE; + cf_sk->sk.sk_err = EPIPE; + break; } - - /* I want to be alone on cf_sk (except status and queue) */ - lock_sock(&(cf_sk->sk)); - } while (ret == -EAGAIN); + return ret; +} - if (ret < 0) { - cfpkt_destroy(pkt); - pr_debug("CAIF: %s(): transmit failed, error = %d\n", - __func__, ret); - - goto write_error; - } +/* Copied from af_unix:unix_dgram_sendmsg, and adapted to CAIF */ +static int caif_seqpkt_sendmsg(struct kiocb *kiocb, struct socket *sock, + struct msghdr *msg, size_t len) +{ + struct sock *sk = sock->sk; + struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); + int buffer_size; + ssize_t ret = 0; + struct sk_buff *skb = NULL; + int noblock; + long timeo; + caif_assert(cf_sk); + ret = sock_error(sk); + if (ret) + goto err; + + ret = -EOPNOTSUPP; + if (msg->msg_flags&MSG_OOB) + goto err; + + ret = -EOPNOTSUPP; + if (msg->msg_namelen) + goto err; + + ret = -EINVAL; + if (unlikely(msg->msg_iov->iov_base == NULL)) + goto err; + noblock = msg->msg_flags & MSG_DONTWAIT; + + buffer_size = len + CAIF_NEEDED_HEADROOM + CAIF_NEEDED_TAILROOM; + + ret = -EMSGSIZE; + if (buffer_size > CAIF_MAX_PAYLOAD_SIZE) + goto err; + + timeo = sock_sndtimeo(sk, noblock); + timeo = caif_wait_for_flow_on(container_of(sk, struct caifsock, sk), + 1, timeo, &ret); + + ret = -EPIPE; + if (cf_sk->sk.sk_state != CAIF_CONNECTED || + sock_flag(sk, SOCK_DEAD) || + (sk->sk_shutdown & RCV_SHUTDOWN)) + goto err; + + ret = -ENOMEM; + skb = sock_alloc_send_skb(sk, buffer_size, noblock, &ret); + if (!skb) + goto err; + skb_reserve(skb, CAIF_NEEDED_HEADROOM); - release_sock(&cf_sk->sk); - return payload_size; + ret = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); -write_error: - release_sock(&cf_sk->sk); -write_error_no_unlock: + if (ret) + goto err; + ret = transmit_skb(skb, cf_sk, noblock, timeo); + if (ret < 0) + goto err; + return len; +err: + kfree_skb(skb); return ret; } -static unsigned int caif_poll(struct file *file, struct socket *sock, - poll_table *wait) +/* + * Copied from unix_stream_sendmsg and adapted to CAIF: + * Changed removed permission handling and added waiting for flow on + * and other minor adaptations. + */ +static int caif_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, + struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - u32 mask = 0; + int err, size; + struct sk_buff *skb; + int sent = 0; + long timeo; - poll_wait(file, sk->sk_sleep, wait); - lock_sock(&(cf_sk->sk)); - if (cfpkt_qpeek(cf_sk->pktq) != NULL) - mask |= (POLLIN | POLLRDNORM); - if (!STATE_IS_OPEN(cf_sk)) - mask |= POLLHUP; - else if (TX_FLOW_IS_ON(cf_sk)) - mask |= (POLLOUT | POLLWRNORM); - release_sock(&cf_sk->sk); - trace_printk("CAIF: %s(): poll mask=0x%04x...\n", - __func__, mask); - return mask; -} + err = -EOPNOTSUPP; -static void drain_queue(struct caifsock *cf_sk) -{ - struct cfpkt *pkt = NULL; + if (unlikely(msg->msg_flags&MSG_OOB)) + goto out_err; - /* Empty the queue */ - do { - /* The queue has its own lock */ - pkt = cfpkt_dequeue(cf_sk->pktq); - if (!pkt) - break; - pr_debug("CAIF: %s(): freeing packet from read queue\n", - __func__); - cfpkt_destroy(pkt); + if (unlikely(msg->msg_namelen)) + goto out_err; - } while (1); + timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT); + timeo = caif_wait_for_flow_on(cf_sk, 1, timeo, &err); - spin_lock(&cf_sk->read_queue_len_lock); - cf_sk->read_queue_len = 0; - spin_unlock(&cf_sk->read_queue_len_lock); -} + if (unlikely(sk->sk_shutdown & SEND_SHUTDOWN)) + goto pipe_err; + + while (sent < len) { + size = len-sent; + + if (size > CAIF_MAX_PAYLOAD_SIZE) + size = CAIF_MAX_PAYLOAD_SIZE; + + /* If size is more than half of sndbuf, chop up message */ + if (size > ((sk->sk_sndbuf >> 1) - 64)) + size = (sk->sk_sndbuf >> 1) - 64; + + if (size > SKB_MAX_ALLOC) + size = SKB_MAX_ALLOC; + + skb = sock_alloc_send_skb(sk, + size + CAIF_NEEDED_HEADROOM + + CAIF_NEEDED_TAILROOM, + msg->msg_flags&MSG_DONTWAIT, + &err); + if (skb == NULL) + goto out_err; + + skb_reserve(skb, CAIF_NEEDED_HEADROOM); + /* + * If you pass two values to the sock_alloc_send_skb + * it tries to grab the large buffer with GFP_NOFS + * (which can fail easily), and if it fails grab the + * fallback size buffer which is under a page and will + * succeed. [Alan] + */ + size = min_t(int, size, skb_tailroom(skb)); + + err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); + if (err) { + kfree_skb(skb); + goto out_err; + } + err = transmit_skb(skb, cf_sk, + msg->msg_flags&MSG_DONTWAIT, timeo); + if (err < 0) { + kfree_skb(skb); + goto pipe_err; + } + sent += size; + } + + return sent; + +pipe_err: + if (sent == 0 && !(msg->msg_flags&MSG_NOSIGNAL)) + send_sig(SIGPIPE, current, 0); + err = -EPIPE; +out_err: + return sent ? : err; +} static int setsockopt(struct socket *sock, int lvl, int opt, char __user *ov, unsigned int ol) { struct sock *sk = sock->sk; struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - struct caif_channel_opt confopt; - int res; + int prio, linksel; + struct ifreq ifreq; - if (lvl != SOL_CAIF) { - pr_debug("CAIF: %s(): setsockopt bad level\n", __func__); + if (cf_sk->sk.sk_socket->state != SS_UNCONNECTED) return -ENOPROTOOPT; - } switch (opt) { - case CAIFSO_CHANNEL_CONFIG: - if (ol < sizeof(struct caif_channel_opt)) { - pr_debug("CAIF: %s(): setsockopt" - " CAIFSO_CHANNEL_CONFIG bad size\n", __func__); + case CAIFSO_LINK_SELECT: + if (ol < sizeof(int)) + return -EINVAL; + if (lvl != SOL_CAIF) + goto bad_sol; + if (copy_from_user(&linksel, ov, sizeof(int))) return -EINVAL; - } - res = copy_from_user(&confopt, ov, sizeof(confopt)); - if (res) - return res; lock_sock(&(cf_sk->sk)); - cf_sk->config.priority = confopt.priority; - cf_sk->config.phy_pref = confopt.link_selector; - strncpy(cf_sk->config.phy_name, confopt.link_name, - sizeof(cf_sk->config.phy_name)); - pr_debug("CAIF: %s(): Setting sockopt pri=%d pref=%d name=%s\n", - __func__, - cf_sk->config.priority, - cf_sk->config.phy_pref, - cf_sk->config.phy_name); + cf_sk->conn_req.link_selector = linksel; release_sock(&cf_sk->sk); return 0; -/* TODO: Implement the remaining options: - * case CAIF_REQ_PARAM_OPT: - * case CAIF_RSP_PARAM_OPT: - * case CAIF_UTIL_FLOW_OPT: - * case CAIF_CONN_INFO_OPT: - * case CAIF_CONN_ID_OPT: - */ - default: - pr_debug("CAIF: %s(): unhandled option %d\n", __func__, opt); - return -EINVAL; - } - - -} -static int getsockopt(struct socket *sock, - int lvl, int opt, char __user *ov, int __user *ol) -{ - return -EINVAL; -} - -static int caif_channel_config(struct caifsock *cf_sk, - struct sockaddr *sock_addr, int len, - struct caif_channel_config *config) -{ - struct sockaddr_caif *addr = (struct sockaddr_caif *)sock_addr; + case SO_PRIORITY: + if (lvl != SOL_SOCKET) + goto bad_sol; + if (ol < sizeof(int)) + return -EINVAL; + if (copy_from_user(&prio, ov, sizeof(int))) + return -EINVAL; + lock_sock(&(cf_sk->sk)); + cf_sk->conn_req.priority = prio; + release_sock(&cf_sk->sk); + return 0; + case SO_BINDTODEVICE: + if (lvl != SOL_SOCKET) + goto bad_sol; + if (ol < sizeof(struct ifreq)) + return -EINVAL; + if (copy_from_user(&ifreq, ov, sizeof(ifreq))) + return -EFAULT; + lock_sock(&(cf_sk->sk)); + strncpy(cf_sk->conn_req.link_name, ifreq.ifr_name, + sizeof(cf_sk->conn_req.link_name)); + cf_sk->conn_req.link_name + [sizeof(cf_sk->conn_req.link_name)-1] = 0; + release_sock(&cf_sk->sk); + return 0; - if (len != sizeof(struct sockaddr_caif)) { - pr_debug("CAIF: %s(): Bad address len (%d,%d)\n", - __func__, len, sizeof(struct sockaddr_caif)); + case CAIFSO_REQ_PARAM: + if (lvl != SOL_CAIF) + goto bad_sol; + if (cf_sk->sk.sk_protocol != CAIFPROTO_UTIL) + return -ENOPROTOOPT; + lock_sock(&(cf_sk->sk)); + cf_sk->conn_req.param.size = ol; + if (ol > sizeof(cf_sk->conn_req.param.data) || + copy_from_user(&cf_sk->conn_req.param.data, ov, ol)) { + release_sock(&cf_sk->sk); return -EINVAL; - } - if (sock_addr->sa_family != AF_CAIF) { - pr_debug("CAIF: %s(): Bad address family (%d)\n", - __func__, sock_addr->sa_family); - return -EAFNOSUPPORT; - } + } + release_sock(&cf_sk->sk); + return 0; - switch (cf_sk->sk.sk_protocol) { - case CAIFPROTO_AT: - config->type = CAIF_CHTY_AT; - break; - case CAIFPROTO_DATAGRAM: - config->type = CAIF_CHTY_DATAGRAM; - break; - case CAIFPROTO_DATAGRAM_LOOP: - config->type = CAIF_CHTY_DATAGRAM_LOOP; - config->u.dgm.connection_id = addr->u.dgm.connection_id; - break; - case CAIFPROTO_UTIL: - config->type = CAIF_CHTY_UTILITY; - strncpy(config->u.utility.name, addr->u.util.service, - sizeof(config->u.utility.name)); - /* forcing the end of string to be null-terminated */ - config->u.utility.name[sizeof(config->u.utility.name)-1] = '\0'; - break; - case CAIFPROTO_RFM: - config->type = CAIF_CHTY_RFM; - config->u.rfm.connection_id = addr->u.rfm.connection_id; - strncpy(config->u.rfm.volume, addr->u.rfm.volume, - sizeof(config->u.rfm.volume)); - /* forcing the end of string to be null-terminated */ - config->u.rfm.volume[sizeof(config->u.rfm.volume)-1] = '\0'; - break; default: - pr_debug("CAIF: %s(): Bad caif protocol type (%d)\n", - __func__, cf_sk->sk.sk_protocol); - return -EINVAL; + return -ENOPROTOOPT; } - trace_printk("CAIF: %s(): Setting connect param PROTO=%s\n", - __func__, - (cf_sk->sk.sk_protocol == CAIFPROTO_AT) ? - "CAIFPROTO_AT" : - (cf_sk->sk.sk_protocol == CAIFPROTO_DATAGRAM) ? - "CAIFPROTO_DATAGRAM" : - (cf_sk->sk.sk_protocol == CAIFPROTO_DATAGRAM_LOOP) ? - "CAIFPROTO_DATAGRAM_LOOP" : - (cf_sk->sk.sk_protocol == CAIFPROTO_UTIL) ? - "CAIFPROTO_UTIL" : - (cf_sk->sk.sk_protocol == CAIFPROTO_RFM) ? - "CAIFPROTO_RFM" : "ERROR"); + return 0; -} +bad_sol: + return -ENOPROTOOPT; +} -int caif_connect(struct socket *sock, struct sockaddr *uservaddr, - int sockaddr_len, int flags) +/* + * caif_connect() - Connect a CAIF Socket + * Copied and modified af_irda.c:irda_connect(). + * + * Note : by consulting "errno", the user space caller may learn the cause + * of the failure. Most of them are visible in the function, others may come + * from subroutines called and are listed here : + * o -EAFNOSUPPORT: bad socket family or type. + * o -ESOCKTNOSUPPORT: bad socket type or protocol + * o -EINVAL: bad socket address, or CAIF link type + * o -ECONNREFUSED: remote end refused the connection. + * o -EINPROGRESS: connect request sent but timed out (or non-blocking) + * o -EISCONN: already connected. + * o -ETIMEDOUT: Connection timed out (send timeout) + * o -ENODEV: No link layer to send request + * o -ECONNRESET: Received Shutdown indication or lost link layer + * o -ENOMEM: Out of memory + * + * State Strategy: + * o sk_state: holds the CAIF_* protocol state, it's updated by + * caif_ctrl_cb. + * o sock->state: holds the SS_* socket state and is updated by connect and + * disconnect. + */ +static int caif_connect(struct socket *sock, struct sockaddr *uaddr, + int addr_len, int flags) { - struct caifsock *cf_sk = NULL; - int result = -1; - int mode = 0; - int ret = -EIO; struct sock *sk = sock->sk; + struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); + long timeo; + int err; + lock_sock(sk); - BUG_ON(sk == NULL); - - cf_sk = container_of(sk, struct caifsock, sk); - - trace_printk("CAIF: %s(): cf_sk=%p OPEN=%d, TX_FLOW=%d, RX_FLOW=%d\n", - __func__, cf_sk, - STATE_IS_OPEN(cf_sk), - TX_FLOW_IS_ON(cf_sk), RX_FLOW_IS_ON(cf_sk)); - - sk->sk_state = TCP_CLOSE; - sock->state = SS_UNCONNECTED; - - if (sock->type == SOCK_SEQPACKET) { - sock->state = SS_CONNECTED; - sk->sk_state = TCP_ESTABLISHED; - } else + err = -EAFNOSUPPORT; + if (uaddr->sa_family != AF_CAIF) goto out; - /* I want to be alone on cf_sk (except status and queue) */ - lock_sock(&(cf_sk->sk)); - - ret = caif_channel_config(cf_sk, uservaddr, sockaddr_len, - &cf_sk->config); - if (ret) { - pr_debug("CAIF: %s(): Cannot set socket address\n", - __func__); - goto open_error; - } - - dbfs_atomic_inc(&cnt.num_open); - mode = SKT_READ_FLAG | SKT_WRITE_FLAG; - - /* If socket is not open, make sure socket is in fully closed state */ - if (!STATE_IS_OPEN(cf_sk)) { - /* Has link close response been received (if we ever sent it)?*/ - if (STATE_IS_PENDING(cf_sk)) { - /* - * Still waiting for close response from remote. - * If opened non-blocking, report "would block" - */ - if (flags & MSG_DONTWAIT) { - pr_debug("CAIF: %s(): MSG_DONTWAIT" - " && close pending\n", __func__); - ret = -EAGAIN; - goto open_error; - } - - pr_debug("CAIF: %s(): Wait for close response" - " from remote...\n", __func__); - - release_sock(&cf_sk->sk); - - /* - * Blocking mode; close is pending and we need to wait - * for its conclusion. - */ - result = - wait_event_interruptible(*cf_sk->sk.sk_sleep, - !STATE_IS_PENDING(cf_sk)); - - lock_sock(&(cf_sk->sk)); - if (result == -ERESTARTSYS) { - pr_debug("CAIF: %s(): wait_event_interruptible" - "woken by a signal (1)", __func__); - ret = -ERESTARTSYS; - goto open_error; - } - } - } - - /* socket is now either closed, pending open or open */ - if (STATE_IS_OPEN(cf_sk) && !STATE_IS_PENDING(cf_sk)) { - /* Open */ - pr_debug("CAIF: %s(): Socket is already opened (cf_sk=%p)" - " check access f_flags = 0x%x file_mode = 0x%x\n", - __func__, cf_sk, mode, cf_sk->file_mode); - - if (mode & cf_sk->file_mode) { - pr_debug("CAIF: %s(): Access mode already in use" - "0x%x\n", - __func__, mode); - ret = -EBUSY; - goto open_error; - } - } else { - /* We are closed or pending open. - * If closed: send link setup - * If pending open: link setup already sent (we could have been - * interrupted by a signal last time) - */ - if (!STATE_IS_OPEN(cf_sk)) { - /* First opening of file; connect lower layers: */ - /* Drain queue (very unlikely) */ - drain_queue(cf_sk); - - cf_sk->layer.receive = caif_sktrecv_cb; - - SET_STATE_OPEN(cf_sk); - SET_PENDING_ON(cf_sk); - - /* Register this channel. */ - result = - caifdev_adapt_register(&cf_sk->config, - &cf_sk->layer); - if (result < 0) { - pr_debug("CAIF: %s(): can't register channel\n", - __func__); - ret = -EIO; - SET_STATE_CLOSED(cf_sk); - SET_PENDING_OFF(cf_sk); - goto open_error; - } - dbfs_atomic_inc(&cnt.num_init); + err = -ESOCKTNOSUPPORT; + if (unlikely(!(sk->sk_type == SOCK_STREAM && + cf_sk->sk.sk_protocol == CAIFPROTO_AT) && + sk->sk_type != SOCK_SEQPACKET)) + goto out; + switch (sock->state) { + case SS_UNCONNECTED: + /* Normal case, a fresh connect */ + caif_assert(sk->sk_state == CAIF_DISCONNECTED); + break; + case SS_CONNECTING: + switch (sk->sk_state) { + case CAIF_CONNECTED: + sock->state = SS_CONNECTED; + err = -EISCONN; + goto out; + case CAIF_DISCONNECTED: + /* Reconnect allowed */ + break; + case CAIF_CONNECTING: + err = -EALREADY; + if (flags & O_NONBLOCK) + goto out; + goto wait_connect; } - - /* If opened non-blocking, report "success". - */ - if (flags & MSG_DONTWAIT) { - pr_debug("CAIF: %s(): MSG_DONTWAIT success\n", - __func__); - ret = 0; - goto open_success; + break; + case SS_CONNECTED: + caif_assert(sk->sk_state == CAIF_CONNECTED || + sk->sk_state == CAIF_DISCONNECTED); + if (sk->sk_shutdown & SHUTDOWN_MASK) { + /* Allow re-connect after SHUTDOWN_IND */ + caif_disconnect_client(&cf_sk->layer); + break; } + /* No reconnect on a seqpacket socket */ + err = -EISCONN; + goto out; + case SS_DISCONNECTING: + case SS_FREE: + caif_assert(1); /*Should never happen */ + break; + } + sk->sk_state = CAIF_DISCONNECTED; + sock->state = SS_UNCONNECTED; + sk_stream_kill_queues(&cf_sk->sk); - trace_printk("CAIF: %s(): Wait for connect response\n", - __func__); + err = -EINVAL; + if (addr_len != sizeof(struct sockaddr_caif) || + !uaddr) + goto out; - /* release lock before waiting */ - release_sock(&cf_sk->sk); + memcpy(&cf_sk->conn_req.sockaddr, uaddr, + sizeof(struct sockaddr_caif)); - result = - wait_event_interruptible(*cf_sk->sk.sk_sleep, - !STATE_IS_PENDING(cf_sk)); + /* Move to connecting socket, start sending Connect Requests */ + sock->state = SS_CONNECTING; + sk->sk_state = CAIF_CONNECTING; - lock_sock(&(cf_sk->sk)); + dbfs_atomic_inc(&cnt.num_connect_req); + cf_sk->layer.receive = caif_sktrecv_cb; + err = caif_connect_client(&cf_sk->conn_req, + &cf_sk->layer); + if (err < 0) { + cf_sk->sk.sk_socket->state = SS_UNCONNECTED; + cf_sk->sk.sk_state = CAIF_DISCONNECTED; + goto out; + } - if (result == -ERESTARTSYS) { - pr_debug("CAIF: %s(): wait_event_interruptible" - "woken by a signal (2)", __func__); - ret = -ERESTARTSYS; - goto open_error; - } + err = -EINPROGRESS; +wait_connect: - if (!STATE_IS_OPEN(cf_sk)) { - /* Lower layers said "no" */ - pr_debug("CAIF: %s(): Closed received\n", __func__); - ret = -EPIPE; - goto open_error; - } + if (sk->sk_state != CAIF_CONNECTED && (flags & O_NONBLOCK)) + goto out; - trace_printk("CAIF: %s(): Connect received\n", __func__); + timeo = sock_sndtimeo(sk, flags & O_NONBLOCK); + + release_sock(sk); + err = wait_event_interruptible_timeout(*sk_sleep(sk), + sk->sk_state != CAIF_CONNECTING, + timeo); + lock_sock(sk); + if (err < 0) + goto out; /* -ERESTARTSYS */ + if (err == 0 && sk->sk_state != CAIF_CONNECTED) { + err = -ETIMEDOUT; + goto out; } -open_success: - /* Open is ok */ - cf_sk->file_mode |= mode; - trace_printk("CAIF: %s(): Connected - file mode = %x\n", - __func__, cf_sk->file_mode); - - release_sock(&cf_sk->sk); - return 0; -open_error: - release_sock(&cf_sk->sk); + if (sk->sk_state != CAIF_CONNECTED) { + sock->state = SS_UNCONNECTED; + err = sock_error(sk); + if (!err) + err = -ECONNREFUSED; + goto out; + } + sock->state = SS_CONNECTED; + err = 0; out: - return ret; + release_sock(sk); + return err; } -static int caif_shutdown(struct socket *sock, int how) + +/* + * caif_release() - Disconnect a CAIF Socket + * Copied and modified af_irda.c:irda_release(). + */ +static int caif_release(struct socket *sock) { - struct caifsock *cf_sk = NULL; - int result; - int tx_flow_state_was_on; struct sock *sk = sock->sk; + struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); int res = 0; - if (how != SHUT_RDWR) - return -EOPNOTSUPP; /* FIXME: ENOTSUP in userland for POSIX */ + if (!sk) + return 0; - cf_sk = container_of(sk, struct caifsock, sk); - if (cf_sk == NULL) { - pr_debug("CAIF: %s(): COULD NOT FIND SOCKET\n", __func__); - return -EBADF; - } + set_tx_flow_off(cf_sk); - /* I want to be alone on cf_sk (except status queue) */ - lock_sock(&(cf_sk->sk)); - dbfs_atomic_inc(&cnt.num_close); + /* + * Ensure that packets are not queued after this point in time. + * caif_queue_rcv_skb checks SOCK_DEAD holding the queue lock, + * this ensures no packets when sock is dead. + */ + spin_lock(&sk->sk_receive_queue.lock); + sock_set_flag(sk, SOCK_DEAD); + spin_unlock(&sk->sk_receive_queue.lock); + sock->sk = NULL; - /* Is the socket open? */ - if (!STATE_IS_OPEN(cf_sk)) { - pr_debug("CAIF: %s(): socket not open (cf_sk=%p) \n", - __func__, cf_sk); - release_sock(&cf_sk->sk); - return 0; - } + dbfs_atomic_inc(&cnt.num_disconnect); - /* Is the socket waiting for link setup response? */ - if (STATE_IS_PENDING(cf_sk)) { - pr_debug("CAIF: %s(): Socket is open pending (cf_sk=%p) \n", - __func__, cf_sk); - release_sock(&cf_sk->sk); - /* What to return here? Seems that EBADF is the closest :-| */ - return -EBADF; - } - /* IS_CLOSED have double meaning: - * 1) Spontanous Remote Shutdown Request. - * 2) Ack on a channel teardown(disconnect) - * Must clear bit in case we previously received - * remote shudown request. - */ + if (cf_sk->debugfs_socket_dir != NULL) + debugfs_remove_recursive(cf_sk->debugfs_socket_dir); - SET_STATE_CLOSED(cf_sk); - SET_PENDING_ON(cf_sk); - sock->state = SS_DISCONNECTING; /* FIXME: Update the sock->states */ - tx_flow_state_was_on = TX_FLOW_IS_ON(cf_sk); - SET_TX_FLOW_OFF(cf_sk); - sock_hold(&cf_sk->sk); - result = caifdev_adapt_unregister(&cf_sk->layer); - - if (result < 0) { - pr_debug("CAIF: %s(): caifdev_adapt_unregister() failed\n", - __func__); - SET_STATE_CLOSED(cf_sk); - SET_PENDING_OFF(cf_sk); - SET_TX_FLOW_OFF(cf_sk); - release_sock(&cf_sk->sk); - return -EIO; - } - if (STATE_IS_REMOTE_SHUTDOWN(cf_sk)) { - SET_PENDING_OFF(cf_sk); - SET_REMOTE_SHUTDOWN_OFF(cf_sk); - } + lock_sock(&(cf_sk->sk)); + sk->sk_state = CAIF_DISCONNECTED; + sk->sk_shutdown = SHUTDOWN_MASK; - dbfs_atomic_inc(&cnt.num_deinit); + if (cf_sk->sk.sk_socket->state == SS_CONNECTED || + cf_sk->sk.sk_socket->state == SS_CONNECTING) + res = caif_disconnect_client(&cf_sk->layer); - /* - * We don't wait for close response here. Close pending state will be - * cleared by flow control callback when response arrives. - */ - drain_queue(cf_sk); - SET_RX_FLOW_ON(cf_sk); - cf_sk->file_mode = 0; + cf_sk->sk.sk_socket->state = SS_DISCONNECTING; + wake_up_interruptible_poll(sk_sleep(sk), POLLERR|POLLHUP); - release_sock(&cf_sk->sk); + sock_orphan(sk); + cf_sk->layer.dn = NULL; + sk_stream_kill_queues(&cf_sk->sk); + release_sock(sk); + sock_put(sk); return res; } -static ssize_t caif_sock_no_sendpage(struct socket *sock, - struct page *page, - int offset, size_t size, int flags) -{ - return -EOPNOTSUPP; -} -/* This function is called as part of close. */ -static int caif_release(struct socket *sock) +/* Copied from af_unix.c:unix_poll(), added CAIF tx_flow handling */ +static unsigned int caif_poll(struct file *file, + struct socket *sock, poll_table *wait) { struct sock *sk = sock->sk; - struct caifsock *cf_sk = NULL; - - caif_assert(sk != NULL); - cf_sk = container_of(sk, struct caifsock, sk); + unsigned int mask; + struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - caif_shutdown(sock, SHUT_RDWR); - lock_sock(&(cf_sk->sk)); + sock_poll_wait(file, sk_sleep(sk), wait); + mask = 0; - sock->sk = NULL; + /* exceptional events? */ + if (sk->sk_err) + mask |= POLLERR; + if (sk->sk_shutdown == SHUTDOWN_MASK) + mask |= POLLHUP; + if (sk->sk_shutdown & RCV_SHUTDOWN) + mask |= POLLRDHUP; - /* Detach the socket from its process context by making it orphan. */ - sock_orphan(sk); + /* readable? */ + if (!skb_queue_empty(&sk->sk_receive_queue) || + (sk->sk_shutdown & RCV_SHUTDOWN)) + mask |= POLLIN | POLLRDNORM; - /* - * Setting SHUTDOWN_MASK means that both send and receive are shutdown - * for the socket. - */ - sk->sk_shutdown = SHUTDOWN_MASK; + /* Connection-based need to check for termination and startup */ + if (sk->sk_state == CAIF_DISCONNECTED) + mask |= POLLHUP; /* - * Set the socket state to closed, the TCP_CLOSE macro is used when - * closing any socket. + * we set writable also when the other side has shut down the + * connection. This prevents stuck sockets. */ - sk->sk_state = TCP_CLOSE; - - /* Flush out this sockets receive queue. */ - drain_queue(cf_sk); + if (sock_writeable(sk) && tx_flow_is_on(cf_sk)) + mask |= POLLOUT | POLLWRNORM | POLLWRBAND; - /* Finally release the socket. */ - STATE_IS_PENDING_DESTROY(cf_sk); - release_sock(&cf_sk->sk); - - /* - * The rest of the cleanup will be handled from the - * caif_sock_destructor - */ - sock_put(sk); - return 0; + return mask; } -static int caif_sock_ioctl(struct socket *sock, - unsigned int cmd, - unsigned long arg) - -{ - return caif_ioctl(cmd, arg, true); -} +static const struct proto_ops caif_seqpacket_ops = { + .family = PF_CAIF, + .owner = THIS_MODULE, + .release = caif_release, + .bind = sock_no_bind, + .connect = caif_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = sock_no_getname, + .poll = caif_poll, + .ioctl = sock_no_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = setsockopt, + .getsockopt = sock_no_getsockopt, + .sendmsg = caif_seqpkt_sendmsg, + .recvmsg = caif_seqpkt_recvmsg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, +}; -static struct proto_ops caif_ops = { +static const struct proto_ops caif_stream_ops = { .family = PF_CAIF, .owner = THIS_MODULE, .release = caif_release, @@ -1189,80 +1093,64 @@ static struct proto_ops caif_ops = { .accept = sock_no_accept, .getname = sock_no_getname, .poll = caif_poll, - .ioctl = caif_sock_ioctl, + .ioctl = sock_no_ioctl, .listen = sock_no_listen, - .shutdown = caif_shutdown, + .shutdown = sock_no_shutdown, .setsockopt = setsockopt, - .getsockopt = getsockopt, - .sendmsg = caif_sendmsg, - .recvmsg = caif_recvmsg, + .getsockopt = sock_no_getsockopt, + .sendmsg = caif_stream_sendmsg, + .recvmsg = caif_stream_recvmsg, .mmap = sock_no_mmap, - .sendpage = caif_sock_no_sendpage, + .sendpage = sock_no_sendpage, }; /* This function is called when a socket is finally destroyed. */ static void caif_sock_destructor(struct sock *sk) { - struct caifsock *cf_sk = NULL; - cf_sk = container_of(sk, struct caifsock, sk); - - /* Error checks. */ + struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); caif_assert(!atomic_read(&sk->sk_wmem_alloc)); caif_assert(sk_unhashed(sk)); caif_assert(!sk->sk_socket); - if (!sock_flag(sk, SOCK_DEAD)) { - pr_debug("CAIF: %s(): 0x%p", __func__, sk); - return; - } - - lock_sock(&(cf_sk->sk)); - - if (STATE_IS_OPEN(cf_sk)) { - pr_debug("CAIF: %s(): socket is opened (cf_sk=%p)" - " file_mode = 0x%x\n", __func__, - cf_sk, cf_sk->file_mode); - release_sock(&cf_sk->sk); + pr_info("Attempt to release alive CAIF socket: %p\n", sk); return; } - drain_queue(cf_sk); - cfglu_free(cf_sk->pktq); - - if (cf_sk->debugfs_socket_dir != NULL) - debugfs_remove_recursive(cf_sk->debugfs_socket_dir); - - release_sock(&cf_sk->sk); - trace_printk("CAIF: %s(): caif_sock_destructor: Removing socket %s\n", - __func__, cf_sk->name); - atomic_dec(&caif_nr_socks); + sk_stream_kill_queues(&cf_sk->sk); + dbfs_atomic_dec(&cnt.caif_nr_socks); } static int caif_create(struct net *net, struct socket *sock, int protocol, - int kern) + int kern) { struct sock *sk = NULL; struct caifsock *cf_sk = NULL; - int result = 0; - static struct proto prot = {.name = "PF_CAIF", .owner = THIS_MODULE, .obj_size = sizeof(struct caifsock), }; - prot.slab = caif_sk_cachep; - if (net != &init_net) - return -EAFNOSUPPORT; + if (!capable(CAP_SYS_ADMIN) && !capable(CAP_NET_ADMIN)) + return -EPERM; + /* + * The sock->type specifies the socket type to use. + * The CAIF socket is a packet stream in the sense + * that it is packet based. CAIF trusts the reliability + * of the link, no resending is implemented. + */ + if (sock->type == SOCK_SEQPACKET) + sock->ops = &caif_seqpacket_ops; + else if (sock->type == SOCK_STREAM) + sock->ops = &caif_stream_ops; + else + return -ESOCKTNOSUPPORT; if (protocol < 0 || protocol >= CAIFPROTO_MAX) return -EPROTONOSUPPORT; - /* - * Set the socket state to unconnected. The socket state is really - * not used at all in the net/core or socket.c but the + * Set the socket state to unconnected. The socket state + * is really not used at all in the net/core or socket.c but the * initialization makes sure that sock->state is not uninitialized. */ - sock->state = SS_UNCONNECTED; - sk = sk_alloc(net, PF_CAIF, GFP_KERNEL, &prot); if (!sk) return -ENOMEM; @@ -1272,27 +1160,9 @@ static int caif_create(struct net *net, struct socket *sock, int protocol, /* Store the protocol */ sk->sk_protocol = (unsigned char) protocol; - spin_lock_init(&cf_sk->read_queue_len_lock); - - /* Fill in some information concerning the misc socket. */ - snprintf(cf_sk->name, sizeof(cf_sk->name), "cf_sk%d", - atomic_read(&caif_nr_socks)); - snprintf(cf_sk->config.name, sizeof(cf_sk->config.name), "caifconf%d", - atomic_read(&caif_nr_socks)); - - /* - * The sock->type specifies the socket type to use. The CAIF socket is - * a packet stream in the sence that it is packet based. - * CAIF trusts the reliability of the link, no resending is implemented. - */ - switch (sock->type) { - case SOCK_SEQPACKET: - sock->ops = &caif_ops; - break; - default: - sk_free(sk); - return -ESOCKTNOSUPPORT; - } + /* Sendbuf dictates the amount of outbound packets not yet sent */ + sk->sk_sndbuf = CAIF_DEF_SNDBUF; + sk->sk_rcvbuf = CAIF_DEF_RCVBUF; /* * Lock in order to try to stop someone from opening the socket @@ -1302,47 +1172,48 @@ static int caif_create(struct net *net, struct socket *sock, int protocol, /* Initialize the nozero default sock structure data. */ sock_init_data(sock, sk); - sk->sk_destruct = caif_sock_destructor; - sk->sk_sndbuf = caif_sockbuf_size; - sk->sk_rcvbuf = caif_sockbuf_size; - - cf_sk->pktq = cfpktq_create(); - if (!cf_sk->pktq) { - pr_err("CAIF: %s(): queue create failed.\n", __func__); - result = -ENOMEM; - release_sock(&cf_sk->sk); - goto err_failed; - } + mutex_init(&cf_sk->readlock); /* single task reading lock */ + cf_sk->layer.ctrlcmd = caif_ctrl_cb; + cf_sk->sk.sk_socket->state = SS_UNCONNECTED; + cf_sk->sk.sk_state = CAIF_DISCONNECTED; - cf_sk->layer.ctrlcmd = caif_sktflowctrl_cb; - SET_STATE_CLOSED(cf_sk); - SET_PENDING_OFF(cf_sk); - SET_TX_FLOW_OFF(cf_sk); - SET_RX_FLOW_ON(cf_sk); + set_tx_flow_off(cf_sk); + set_rx_flow_on(cf_sk); + /* Set default options on configuration */ + cf_sk->conn_req.priority = CAIF_PRIO_NORMAL; + cf_sk->conn_req.link_selector = CAIF_LINK_LOW_LATENCY; + cf_sk->conn_req.protocol = protocol; /* Increase the number of sockets created. */ - atomic_inc(&caif_nr_socks); + dbfs_atomic_inc(&cnt.caif_nr_socks); +#ifdef CONFIG_DEBUG_FS if (!IS_ERR(debugfsdir)) { + /* Fill in some information concerning the misc socket. */ + snprintf(cf_sk->name, sizeof(cf_sk->name), "cfsk%d", + atomic_read(&cnt.caif_nr_socks)); + cf_sk->debugfs_socket_dir = debugfs_create_dir(cf_sk->name, debugfsdir); - debugfs_create_u32("conn_state", S_IRUSR | S_IWUSR, - cf_sk->debugfs_socket_dir, &cf_sk->conn_state); + debugfs_create_u32("sk_state", S_IRUSR | S_IWUSR, + cf_sk->debugfs_socket_dir, + (u32 *) &cf_sk->sk.sk_state); debugfs_create_u32("flow_state", S_IRUSR | S_IWUSR, cf_sk->debugfs_socket_dir, &cf_sk->flow_state); - debugfs_create_u32("read_queue_len", S_IRUSR | S_IWUSR, + debugfs_create_u32("sk_rmem_alloc", S_IRUSR | S_IWUSR, + cf_sk->debugfs_socket_dir, + (u32 *) &cf_sk->sk.sk_rmem_alloc); + debugfs_create_u32("sk_wmem_alloc", S_IRUSR | S_IWUSR, cf_sk->debugfs_socket_dir, - (u32 *) &cf_sk->read_queue_len); + (u32 *) &cf_sk->sk.sk_wmem_alloc); debugfs_create_u32("identity", S_IRUSR | S_IWUSR, cf_sk->debugfs_socket_dir, (u32 *) &cf_sk->layer.id); } +#endif release_sock(&cf_sk->sk); return 0; -err_failed: - sk_free(sk); - return result; } @@ -1354,46 +1225,32 @@ static struct net_proto_family caif_family_ops = { int af_caif_init(void) { - int err; - err = sock_register(&caif_family_ops); - + int err = sock_register(&caif_family_ops); if (!err) return err; - return 0; } static int __init caif_sktinit_module(void) { - int stat; #ifdef CONFIG_DEBUG_FS - debugfsdir = debugfs_create_dir("chnl_skt", NULL); + debugfsdir = debugfs_create_dir("caif_sk", NULL); if (!IS_ERR(debugfsdir)) { - atomic_inc(&caif_nr_socks); debugfs_create_u32("num_sockets", S_IRUSR | S_IWUSR, debugfsdir, - (u32 *) &caif_nr_socks); - debugfs_create_u32("num_open", S_IRUSR | S_IWUSR, + (u32 *) &cnt.caif_nr_socks); + debugfs_create_u32("num_connect_req", S_IRUSR | S_IWUSR, debugfsdir, - (u32 *) &cnt.num_open); - debugfs_create_u32("num_close", S_IRUSR | S_IWUSR, + (u32 *) &cnt.num_connect_req); + debugfs_create_u32("num_connect_resp", S_IRUSR | S_IWUSR, debugfsdir, - (u32 *) &cnt.num_close); - debugfs_create_u32("num_init", S_IRUSR | S_IWUSR, + (u32 *) &cnt.num_connect_resp); + debugfs_create_u32("num_connect_fail_resp", S_IRUSR | S_IWUSR, debugfsdir, - (u32 *) &cnt.num_init); - debugfs_create_u32("num_init_resp", S_IRUSR | S_IWUSR, + (u32 *) &cnt.num_connect_fail_resp); + debugfs_create_u32("num_disconnect", S_IRUSR | S_IWUSR, debugfsdir, - (u32 *) &cnt.num_init_resp); - debugfs_create_u32("num_init_fail_resp", S_IRUSR | S_IWUSR, - debugfsdir, - (u32 *) &cnt.num_init_fail_resp); - debugfs_create_u32("num_deinit", S_IRUSR | S_IWUSR, - debugfsdir, - (u32 *) &cnt.num_deinit); - debugfs_create_u32("num_deinit_resp", S_IRUSR | S_IWUSR, - debugfsdir, - (u32 *) &cnt.num_deinit_resp); + (u32 *) &cnt.num_disconnect); debugfs_create_u32("num_remote_shutdown_ind", S_IRUSR | S_IWUSR, debugfsdir, (u32 *) &cnt.num_remote_shutdown_ind); @@ -1411,13 +1268,7 @@ static int __init caif_sktinit_module(void) (u32 *) &cnt.num_rx_flow_on); } #endif - stat = af_caif_init(); - if (stat) { - pr_err("CAIF: %s(): Failed to initialize CAIF socket layer.", - __func__); - return stat; - } - return 0; + return af_caif_init(); } static void __exit caif_sktexit_module(void) @@ -1426,7 +1277,5 @@ static void __exit caif_sktexit_module(void) if (debugfsdir != NULL) debugfs_remove_recursive(debugfsdir); } - module_init(caif_sktinit_module); module_exit(caif_sktexit_module); - diff --git a/net/caif/cfdbgl.c b/net/caif/cfdbgl.c new file mode 100644 index 00000000000..ab6b6dc34cf --- /dev/null +++ b/net/caif/cfdbgl.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) ST-Ericsson AB 2010 + * Author: Sjur Brendeland/sjur.brandeland@stericsson.com + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/stddef.h> +#include <linux/slab.h> +#include <net/caif/caif_layer.h> +#include <net/caif/cfsrvl.h> +#include <net/caif/cfpkt.h> + +static int cfdbgl_receive(struct 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 = kmalloc(sizeof(struct cfsrvl), GFP_ATOMIC); + if (!dbg) { + pr_warning("CAIF: %s(): Out of memory\n", __func__); + return NULL; + } + caif_assert(offsetof(struct cfsrvl, layer) == 0); + memset(dbg, 0, sizeof(struct cfsrvl)); + cfsrvl_init(dbg, channel_id, dev_info); + dbg->layer.receive = cfdbgl_receive; + dbg->layer.transmit = cfdbgl_transmit; + snprintf(dbg->layer.name, CAIF_LAYER_NAME_SZ - 1, "dbg%d", channel_id); + return &dbg->layer; +} + +static int cfdbgl_receive(struct cflayer *layr, struct cfpkt *pkt) +{ + return layr->up->receive(layr->up, pkt); +} + +static int cfdbgl_transmit(struct cflayer *layr, struct cfpkt *pkt) +{ + return layr->dn->transmit(layr->dn, pkt); +} diff --git a/net/caif/cfpkt_skbuff.c b/net/caif/cfpkt_skbuff.c index 32f23ca02fb..83fff2ff665 100644 --- a/net/caif/cfpkt_skbuff.c +++ b/net/caif/cfpkt_skbuff.c @@ -1,5 +1,5 @@ /* - * Copyright (C) ST-Ericsson AB 2009 + * Copyright (C) ST-Ericsson AB 2010 * Author: Sjur Brendeland/sjur.brandeland@stericsson.com * License terms: GNU General Public License (GPL) version 2 */ @@ -7,8 +7,7 @@ #include <linux/string.h> #include <linux/skbuff.h> #include <linux/hardirq.h> -#include <net/caif/generic/cfglue.h> -#include <net/caif/generic/cfpkt.h> +#include <net/caif/cfpkt.h> #define PKT_PREFIX CAIF_NEEDED_HEADROOM #define PKT_POSTFIX CAIF_NEEDED_TAILROOM @@ -21,12 +20,13 @@ struct cfpktq { struct sk_buff_head head; - cfglu_atomic_t count; + atomic_t count; + /* Lock protects count updates */ spinlock_t lock; }; /* - * net/caif/generic/ is generic and does not + * net/caif/ is generic and does not * understand SKB, so we do this typecast */ struct cfpkt { @@ -59,14 +59,11 @@ inline struct cfpkt *skb_to_pkt(struct sk_buff *skb) return (struct cfpkt *) skb; } -cfglu_atomic_t cfpkt_packet_count; -EXPORT_SYMBOL(cfpkt_packet_count); struct cfpkt *cfpkt_fromnative(enum caif_direction dir, void *nativepkt) { struct cfpkt *pkt = skb_to_pkt(nativepkt); cfpkt_priv(pkt)->erronous = false; - cfglu_atomic_inc(cfpkt_packet_count); return pkt; } EXPORT_SYMBOL(cfpkt_fromnative); @@ -77,7 +74,7 @@ void *cfpkt_tonative(struct cfpkt *pkt) } EXPORT_SYMBOL(cfpkt_tonative); -struct cfpkt *cfpkt_create_pfx(uint16 len, uint16 pfx) +static struct cfpkt *cfpkt_create_pfx(u16 len, u16 pfx) { struct sk_buff *skb; @@ -90,11 +87,10 @@ struct cfpkt *cfpkt_create_pfx(uint16 len, uint16 pfx) return NULL; skb_reserve(skb, pfx); - cfglu_atomic_inc(cfpkt_packet_count); return skb_to_pkt(skb); } -inline struct cfpkt *cfpkt_create(uint16 len) +inline struct cfpkt *cfpkt_create(u16 len) { return cfpkt_create_pfx(len + PKT_POSTFIX, PKT_PREFIX); } @@ -103,8 +99,6 @@ EXPORT_SYMBOL(cfpkt_create); void cfpkt_destroy(struct cfpkt *pkt) { struct sk_buff *skb = pkt_to_skb(pkt); - cfglu_atomic_dec(cfpkt_packet_count); - caif_assert(cfglu_atomic_read(cfpkt_packet_count) >= 0); kfree_skb(skb); } EXPORT_SYMBOL(cfpkt_destroy); @@ -116,82 +110,82 @@ inline bool cfpkt_more(struct cfpkt *pkt) } EXPORT_SYMBOL(cfpkt_more); -int cfpkt_peek_head(struct cfpkt *pkt, void *data, uint16 len) +int cfpkt_peek_head(struct cfpkt *pkt, void *data, u16 len) { struct sk_buff *skb = pkt_to_skb(pkt); - if (skb->tail - skb->data >= len) { + if (skb_headlen(skb) >= len) { memcpy(data, skb->data, len); - return CFGLU_EOK; + return 0; } return !cfpkt_extr_head(pkt, data, len) && !cfpkt_add_head(pkt, data, len); } EXPORT_SYMBOL(cfpkt_peek_head); -int cfpkt_extr_head(struct cfpkt *pkt, void *data, uint16 len) +int cfpkt_extr_head(struct cfpkt *pkt, void *data, u16 len) { struct sk_buff *skb = pkt_to_skb(pkt); - uint8 *from; + u8 *from; if (unlikely(is_erronous(pkt))) - return CFGLU_EPKT; + return -EPROTO; if (unlikely(len > skb->len)) { PKT_ERROR(pkt, "cfpkt_extr_head read beyond end of packet\n"); - return CFGLU_EPKT; + return -EPROTO; } if (unlikely(len > skb_headlen(skb))) { if (unlikely(skb_linearize(skb) != 0)) { PKT_ERROR(pkt, "cfpkt_extr_head linearize failed\n"); - return CFGLU_EPKT; + return -EPROTO; } } from = skb_pull(skb, len); from -= len; memcpy(data, from, len); - return CFGLU_EOK; + return 0; } EXPORT_SYMBOL(cfpkt_extr_head); -int cfpkt_extr_trail(struct cfpkt *pkt, void *dta, uint16 len) +int cfpkt_extr_trail(struct cfpkt *pkt, void *dta, u16 len) { struct sk_buff *skb = pkt_to_skb(pkt); - uint8 *data = dta; - uint8 *from; + u8 *data = dta; + u8 *from; if (unlikely(is_erronous(pkt))) - return CFGLU_EPKT; + return -EPROTO; if (unlikely(skb_linearize(skb) != 0)) { PKT_ERROR(pkt, "cfpkt_extr_trail linearize failed\n"); - return CFGLU_EPKT; + return -EPROTO; } if (unlikely(skb->data + len > skb_tail_pointer(skb))) { PKT_ERROR(pkt, "cfpkt_extr_trail read beyond end of packet\n"); - return CFGLU_EPKT; + return -EPROTO; } from = skb_tail_pointer(skb) - len; skb_trim(skb, skb->len - len); memcpy(data, from, len); - return CFGLU_EOK; + return 0; } EXPORT_SYMBOL(cfpkt_extr_trail); -int cfpkt_pad_trail(struct cfpkt *pkt, uint16 len) +int cfpkt_pad_trail(struct cfpkt *pkt, u16 len) { return cfpkt_add_body(pkt, NULL, len); } EXPORT_SYMBOL(cfpkt_pad_trail); -int cfpkt_add_body(struct cfpkt *pkt, const void *data, uint16 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; - uint8 *to; - uint16 addlen = 0; + u8 *to; + u16 addlen = 0; if (unlikely(is_erronous(pkt))) - return CFGLU_EPKT; + return -EPROTO; lastskb = skb; @@ -209,7 +203,7 @@ int cfpkt_add_body(struct cfpkt *pkt, const void *data, uint16 len) /* Make sure data is writable */ if (unlikely(skb_cow_data(skb, addlen, &lastskb) < 0)) { PKT_ERROR(pkt, "cfpkt_add_body: cow failed\n"); - return CFGLU_EPKT; + return -EPROTO; } /* * Is the SKB non-linear after skb_cow_data()? If so, we are @@ -228,79 +222,79 @@ int cfpkt_add_body(struct cfpkt *pkt, const void *data, uint16 len) to = skb_put(lastskb, len); if (likely(data)) memcpy(to, data, len); - return CFGLU_EOK; + return 0; } EXPORT_SYMBOL(cfpkt_add_body); -inline int cfpkt_addbdy(struct cfpkt *pkt, uint8 data) +inline int cfpkt_addbdy(struct cfpkt *pkt, u8 data) { return cfpkt_add_body(pkt, &data, 1); } EXPORT_SYMBOL(cfpkt_addbdy); -int cfpkt_add_head(struct cfpkt *pkt, const void *data2, uint16 len) +int cfpkt_add_head(struct cfpkt *pkt, const void *data2, u16 len) { struct sk_buff *skb = pkt_to_skb(pkt); struct sk_buff *lastskb; - uint8 *to; - const uint8 *data = data2; + u8 *to; + const u8 *data = data2; if (unlikely(is_erronous(pkt))) - return CFGLU_EPKT; + return -EPROTO; if (unlikely(skb_headroom(skb) < len)) { PKT_ERROR(pkt, "cfpkt_add_head: no headroom\n"); - return CFGLU_EPKT; + return -EPROTO; } /* Make sure data is writable */ if (unlikely(skb_cow_data(skb, 0, &lastskb) < 0)) { PKT_ERROR(pkt, "cfpkt_add_head: cow failed\n"); - return CFGLU_EPKT; + return -EPROTO; } to = skb_push(skb, len); memcpy(to, data, len); - return CFGLU_EOK; + return 0; } EXPORT_SYMBOL(cfpkt_add_head); -inline int cfpkt_add_trail(struct cfpkt *pkt, const void *data, uint16 len) +inline int cfpkt_add_trail(struct cfpkt *pkt, const void *data, u16 len) { return cfpkt_add_body(pkt, data, len); } EXPORT_SYMBOL(cfpkt_add_trail); -inline uint16 cfpkt_getlen(struct cfpkt *pkt) +inline u16 cfpkt_getlen(struct cfpkt *pkt) { struct sk_buff *skb = pkt_to_skb(pkt); return skb->len; } EXPORT_SYMBOL(cfpkt_getlen); -inline uint16 cfpkt_iterate(struct cfpkt *pkt, - uint16 (*iter_func)(uint16, void *, uint16), - uint16 data) +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 CFGLU_EPKT; + return -EPROTO; if (unlikely(skb_linearize(&pkt->skb) != 0)) { PKT_ERROR(pkt, "cfpkt_iterate: linearize failed\n"); - return CFGLU_EPKT; + return -EPROTO; } return iter_func(data, pkt->skb.data, cfpkt_getlen(pkt)); } EXPORT_SYMBOL(cfpkt_iterate); -int cfpkt_setlen(struct cfpkt *pkt, uint16 len) +int cfpkt_setlen(struct cfpkt *pkt, u16 len) { struct sk_buff *skb = pkt_to_skb(pkt); if (unlikely(is_erronous(pkt))) - return CFGLU_EPKT; + return -EPROTO; if (likely(len <= skb->len)) { if (unlikely(skb->data_len)) @@ -328,18 +322,17 @@ struct cfpkt *cfpkt_create_uplink(const unsigned char *data, unsigned int len) } EXPORT_SYMBOL(cfpkt_create_uplink); - struct cfpkt *cfpkt_append(struct cfpkt *dstpkt, struct cfpkt *addpkt, - uint16 expectlen) + u16 expectlen) { struct sk_buff *dst = pkt_to_skb(dstpkt); struct sk_buff *add = pkt_to_skb(addpkt); - uint16 addlen = add->tail - add->data; - uint16 neededtailspace; + u16 addlen = skb_headlen(add); + u16 neededtailspace; struct sk_buff *tmp; - uint16 dstlen; - uint16 createlen; + u16 dstlen; + u16 createlen; if (unlikely(is_erronous(dstpkt) || is_erronous(addpkt))) { cfpkt_destroy(addpkt); return dstpkt; @@ -351,19 +344,19 @@ struct cfpkt *cfpkt_append(struct cfpkt *dstpkt, if (dst->tail + neededtailspace > dst->end) { /* Create a dumplicate of 'dst' with more tail space */ - dstlen = dst->tail - dst->data; + dstlen = skb_headlen(dst); createlen = dstlen + neededtailspace; tmp = pkt_to_skb( cfpkt_create(createlen + PKT_PREFIX + PKT_POSTFIX)); if (!tmp) return NULL; - tmp->tail = tmp->data + dstlen; + skb_set_tail_pointer(tmp, dstlen); tmp->len = dstlen; memcpy(tmp->data, dst->data, dstlen); cfpkt_destroy(dstpkt); dst = tmp; } - memcpy(dst->tail, add->data, add->tail - add->data); + memcpy(skb_tail_pointer(dst), add->data, skb_headlen(add)); cfpkt_destroy(addpkt); dst->tail += addlen; dst->len += addlen; @@ -371,17 +364,17 @@ struct cfpkt *cfpkt_append(struct cfpkt *dstpkt, } EXPORT_SYMBOL(cfpkt_append); -struct cfpkt *cfpkt_split(struct cfpkt *pkt, uint16 pos) +struct cfpkt *cfpkt_split(struct cfpkt *pkt, u16 pos) { struct sk_buff *skb2; struct sk_buff *skb = pkt_to_skb(pkt); - uint8 *split = skb->data + pos; - uint16 len2nd = skb->tail - split; + 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) { + if (skb->data + pos > skb_tail_pointer(skb)) { PKT_ERROR(pkt, "cfpkt_split: trying to split beyond end of packet"); return NULL; @@ -396,7 +389,7 @@ struct cfpkt *cfpkt_split(struct cfpkt *pkt, uint16 pos) return NULL; /* Reduce the length of the original packet */ - skb->tail = split; + skb_set_tail_pointer(skb, pos); skb->len = pos; memcpy(skb2->data, split, len2nd); @@ -406,7 +399,6 @@ struct cfpkt *cfpkt_split(struct cfpkt *pkt, uint16 pos) } EXPORT_SYMBOL(cfpkt_split); - char *cfpkt_log_pkt(struct cfpkt *pkt, char *buf, int buflen) { struct sk_buff *skb = pkt_to_skb(pkt); @@ -420,17 +412,18 @@ char *cfpkt_log_pkt(struct cfpkt *pkt, char *buf, int buflen) if (buflen < 50) return NULL; - snprintf(buf, buflen, "%s: pkt:%p len:%d(%d+%d) {%d,%d} data: [", + snprintf(buf, buflen, "%s: pkt:%p len:%ld(%ld+%ld) {%ld,%ld} data: [", is_erronous(pkt) ? "ERRONOUS-SKB" : (skb->data_len != 0 ? "COMPLEX-SKB" : "SKB"), - skb, - skb->len, - skb->tail - skb->data, - skb->data_len, - skb->data - skb->head, skb->tail - skb->head); + skb, + (long) skb->len, + (long) (skb_tail_pointer(skb) - skb->data), + (long) skb->data_len, + (long) (skb->data - skb->head), + (long) (skb_tail_pointer(skb) - skb->head)); p = buf + strlen(buf); - for (i = 0; i < skb->tail - skb->data && i < 300; i++) { + for (i = 0; i < skb_tail_pointer(skb) - skb->data && i < 300; i++) { if (p > buf + buflen - 10) { sprintf(p, "..."); p = buf + strlen(buf); @@ -451,21 +444,21 @@ int cfpkt_raw_append(struct cfpkt *pkt, void **buf, unsigned int buflen) caif_assert(buf != NULL); if (unlikely(is_erronous(pkt))) - return CFGLU_EPKT; + return -EPROTO; /* Make sure SKB is writable */ if (unlikely(skb_cow_data(skb, 0, &lastskb) < 0)) { PKT_ERROR(pkt, "cfpkt_raw_append: skb_cow_data failed\n"); - return CFGLU_EPKT; + return -EPROTO; } if (unlikely(skb_linearize(skb) != 0)) { PKT_ERROR(pkt, "cfpkt_raw_append: linearize failed\n"); - return CFGLU_EPKT; + return -EPROTO; } if (unlikely(skb_tailroom(skb) < buflen)) { PKT_ERROR(pkt, "cfpkt_raw_append: buffer too short - failed\n"); - return CFGLU_EPKT; + return -EPROTO; } *buf = skb_put(skb, buflen); @@ -479,18 +472,18 @@ int cfpkt_raw_extract(struct cfpkt *pkt, void **buf, unsigned int buflen) caif_assert(buf != NULL); if (unlikely(is_erronous(pkt))) - return CFGLU_EPKT; + return -EPROTO; if (unlikely(buflen > skb->len)) { PKT_ERROR(pkt, "cfpkt_raw_extract: buflen too large " "- failed\n"); - return CFGLU_EPKT; + return -EPROTO; } if (unlikely(buflen > skb_headlen(skb))) { if (unlikely(skb_linearize(skb) != 0)) { PKT_ERROR(pkt, "cfpkt_raw_extract: linearize failed\n"); - return CFGLU_EPKT; + return -EPROTO; } } @@ -507,30 +500,13 @@ inline bool cfpkt_erroneous(struct cfpkt *pkt) } EXPORT_SYMBOL(cfpkt_erroneous); -struct cfpkt *cfpkt_create_pkt(enum caif_direction dir, - const unsigned char *data, unsigned int len) -{ - struct cfpkt *pkt; - if (dir == CAIF_DIR_OUT) - pkt = cfpkt_create_pfx(len + PKT_POSTFIX, PKT_PREFIX); - else - pkt = cfpkt_create_pfx(len, 0); - if (unlikely(!pkt)) - return NULL; - if (unlikely(data)) - cfpkt_add_body(pkt, data, len); - cfpkt_priv(pkt)->erronous = false; - return pkt; -} -EXPORT_SYMBOL(cfpkt_create_pkt); - -struct cfpktq *cfpktq_create() +struct cfpktq *cfpktq_create(void) { - struct cfpktq *q = cfglu_alloc(sizeof(struct cfpktq)); + struct cfpktq *q = kmalloc(sizeof(struct cfpktq), GFP_ATOMIC); if (!q) return NULL; skb_queue_head_init(&q->head); - cfglu_atomic_set(q->count, 0); + atomic_set(&q->count, 0); spin_lock_init(&q->lock); return q; } @@ -538,7 +514,7 @@ EXPORT_SYMBOL(cfpktq_create); void cfpkt_queue(struct cfpktq *pktq, struct cfpkt *pkt, unsigned short prio) { - cfglu_atomic_inc(pktq->count); + atomic_inc(&pktq->count); spin_lock(&pktq->lock); skb_queue_tail(&pktq->head, pkt_to_skb(pkt)); spin_unlock(&pktq->lock); @@ -562,8 +538,8 @@ struct cfpkt *cfpkt_dequeue(struct cfpktq *pktq) spin_lock(&pktq->lock); pkt = skb_to_pkt(skb_dequeue(&pktq->head)); if (pkt) { - cfglu_atomic_dec(pktq->count); - caif_assert(cfglu_atomic_read(pktq->count) >= 0); + atomic_dec(&pktq->count); + caif_assert(atomic_read(&pktq->count) >= 0); } spin_unlock(&pktq->lock); return pkt; @@ -572,7 +548,7 @@ EXPORT_SYMBOL(cfpkt_dequeue); int cfpkt_qcount(struct cfpktq *pktq) { - return cfglu_atomic_read(pktq->count); + return atomic_read(&pktq->count); } EXPORT_SYMBOL(cfpkt_qcount); @@ -584,13 +560,12 @@ struct cfpkt *cfpkt_clone_release(struct cfpkt *pkt) cfpkt_destroy(pkt); if (!clone) return NULL; - cfglu_atomic_inc(cfpkt_packet_count); return clone; } EXPORT_SYMBOL(cfpkt_clone_release); -struct payload_info *cfpkt_info(struct cfpkt *pkt) +struct caif_payload_info *cfpkt_info(struct cfpkt *pkt) { - return (struct payload_info *)&pkt_to_skb(pkt)->cb; + return (struct caif_payload_info *)&pkt_to_skb(pkt)->cb; } EXPORT_SYMBOL(cfpkt_info); diff --git a/net/caif/chnl_chr.c b/net/caif/chnl_chr.c index 77e6d9a4529..b9a52460646 100644 --- a/net/caif/chnl_chr.c +++ b/net/caif/chnl_chr.c @@ -1,5 +1,5 @@ /* - * Copyright (C) ST-Ericsson AB 2009 + * Copyright (C) ST-Ericsson AB 2010 * Author: Per Sigmond / Per.Sigmond@stericsson.com * License terms: GNU General Public License (GPL) version 2 */ @@ -18,10 +18,10 @@ #include <asm/atomic.h> /* CAIF header files. */ -#include <net/caif/generic/caif_layer.h> -#include <net/caif/generic/cfcnfg.h> -#include <net/caif/generic/cfpkt.h> -#include <net/caif/generic/cffrml.h> +#include <net/caif/caif_layer.h> +#include <net/caif/cfcnfg.h> +#include <net/caif/cfpkt.h> +#include <net/caif/cffrml.h> #include <net/caif/caif_chr.h> #include <linux/caif/caif_config.h> #include <net/caif/caif_actions.h> @@ -35,11 +35,11 @@ MODULE_LICENSE("GPL"); static LIST_HEAD(caif_chrdev_list); static spinlock_t list_lock; -#define CONN_STATE_OPEN_BIT 1 -#define CONN_STATE_PENDING_BIT 2 +#define CONN_STATE_OPEN_BIT 1 +#define CONN_STATE_PENDING_BIT 2 #define CONN_REMOTE_SHUTDOWN_BIT 4 -#define TX_FLOW_ON_BIT 1 -#define RX_FLOW_ON_BIT 2 +#define TX_FLOW_ON_BIT 1 +#define RX_FLOW_ON_BIT 2 #define STATE_IS_OPEN(dev) test_bit(CONN_STATE_OPEN_BIT,\ (void *) &(dev)->conn_state) @@ -86,7 +86,7 @@ static struct dentry *debugfsdir; #endif struct caif_char_dev { - struct layer layer; + struct cflayer layer; u32 conn_state; u32 flow_state; struct cfpktq *pktq; @@ -127,7 +127,7 @@ struct caif_char_dev { static void drain_queue(struct caif_char_dev *dev); /* Packet Receive Callback function called from CAIF Stack */ -static int caif_chrrecv_cb(struct layer *layr, struct cfpkt *pkt) +static int caif_chrrecv_cb(struct cflayer *layr, struct cfpkt *pkt) { struct caif_char_dev *dev; int read_queue_high; @@ -193,7 +193,8 @@ static int caif_chrrecv_cb(struct layer *layr, struct cfpkt *pkt) } /* Packet Flow Control Callback function called from CAIF */ -static void caif_chrflowctrl_cb(struct layer *layr, enum caif_ctrlcmd flow, int phyid) +static void caif_chrflowctrl_cb(struct cflayer *layr, enum caif_ctrlcmd flow, + int phyid) { struct caif_char_dev *dev; @@ -389,11 +390,11 @@ ssize_t caif_chrread(struct file *filp, char __user *buf, size_t count, dev->pktf.cfpkt_qpeek(dev->pktq) || STATE_IS_REMOTE_SHUTDOWN(dev) || !STATE_IS_OPEN(dev)) == - -ERESTARTSYS) { + -ERESTARTSYS) { pr_debug("CAIF: %s():_event_interruptible woken by " - "a signal, signal_pending(current) = %d\n", + "a signal, signal_pending(current) = %d\n", __func__, - signal_pending(current)); + signal_pending(current)); return -ERESTARTSYS; } @@ -911,8 +912,8 @@ int caif_chropen(struct inode *inode, struct file *filp) /* Open */ pr_debug("CAIF: %s():" " Device is already opened (dev=%p) check access " - "f_flags = 0x%x file_mode = 0x%x\n", - __func__, dev, mode, dev->file_mode); + "f_flags = 0x%x file_mode = 0x%x\n", + __func__, dev, mode, dev->file_mode); if (mode & dev->file_mode) { pr_debug("CAIF: %s():Access mode already in use 0x%x\n", @@ -922,9 +923,9 @@ int caif_chropen(struct inode *inode, struct file *filp) } } else { /* We are closed or pending open. - * If closed: send link setup + * If closed: send link setup * If pending open: link setup already sent (we could have been - * interrupted by a signal last time) + * interrupted by a signal last time) */ if (!STATE_IS_OPEN(dev)) { /* First opening of file; connect lower layers: */ @@ -936,7 +937,7 @@ int caif_chropen(struct inode *inode, struct file *filp) /* Register this channel. */ result = - caifdev_adapt_register(&dev->config, &dev->layer); + add_adaptation_layer(&dev->config, &dev->layer); if (result < 0) { pr_debug("CAIF: %s():can't register channel\n", __func__); @@ -988,9 +989,6 @@ int caif_chropen(struct inode *inode, struct file *filp) caif_assert(dev->layer.dn); caif_assert(dev->layer.dn->ctrlcmd); - (void) dev->layer.dn->modemcmd(dev->layer.dn, - CAIF_MODEMCMD_FLOW_ON_REQ); - } open_success: /* Open is OK. */ @@ -1067,7 +1065,7 @@ int caif_chrrelease(struct inode *inode, struct file *filp) dev->file_mode &= ~mode; if (dev->file_mode) { pr_debug("CAIF: %s(): Device is kept open by someone else," - " don't close. CAIF connection - file_mode = %x\n", + " don't close. CAIF connection - file_mode = %x\n", __func__, dev->file_mode); mutex_unlock(&dev->mutex); return 0; @@ -1084,10 +1082,10 @@ int caif_chrrelease(struct inode *inode, struct file *filp) SET_PENDING_ON(dev); tx_flow_state_was_on = TX_FLOW_IS_ON(dev); SET_TX_FLOW_OFF(dev); - result = caifdev_adapt_unregister(&dev->layer); + result = caif_disconnect_client(&dev->layer); if (result < 0) { - pr_debug("CAIF: %s(): caifdev_adapt_unregister() failed\n", + pr_debug("CAIF: %s(): caif_disconnect_client() failed\n", __func__); SET_STATE_CLOSED(dev); SET_PENDING_OFF(dev); @@ -1292,7 +1290,7 @@ int chrdev_remove(char *name) if (STATE_IS_OPEN(dev)) { pr_debug("CAIF: %s(): Device is opened " "(dev=%p) file_mode = 0x%x\n", - __func__, dev, dev->file_mode); + __func__, dev, dev->file_mode); mutex_unlock(&dev->mutex); return -EBUSY; } @@ -1303,7 +1301,7 @@ int chrdev_remove(char *name) drain_queue(dev); ret = misc_deregister(&dev->misc); - cfglu_free(dev->pktq); + kfree(dev->pktq); #ifdef CONFIG_DEBUG_FS if (dev->debugfs_device_dir != NULL) diff --git a/net/caif/chnl_net.c b/net/caif/chnl_net.c index 2e53a048f0a..354a9ff6adb 100644 --- a/net/caif/chnl_net.c +++ b/net/caif/chnl_net.c @@ -1,6 +1,7 @@ /* - * Copyright (C) ST-Ericsson AB 2009 - * Author: Daniel Martensson / Daniel.Martensson@stericsson.com + * Copyright (C) ST-Ericsson AB 2010 + * Authors: Sjur Brendeland/sjur.brandeland@stericsson.com + * Daniel Martensson / Daniel.Martensson@stericsson.com * License terms: GNU General Public License (GPL) version 2 */ @@ -16,36 +17,48 @@ #include <linux/sockios.h> #include <linux/caif/if_caif.h> #include <net/rtnetlink.h> -#include <net/caif/generic/caif_layer.h> -#include <net/caif/generic/cfcnfg.h> -#include <net/caif/generic/cfpkt.h> +#include <net/caif/caif_layer.h> +#include <net/caif/cfcnfg.h> +#include <net/caif/cfpkt.h> #include <net/caif/caif_dev.h> -#define CAIF_CONNECT_TIMEOUT 30 +/* GPRS PDP connection has MTU to 1500 */ #define SIZE_MTU 1500 -#define SIZE_MTU_MAX 4080 -#define SIZE_MTU_MIN 68 +/* 5 sec. connect timeout */ +#define CONNECT_TIMEOUT (5 * HZ) #define CAIF_NET_DEFAULT_QUEUE_LEN 500 -/*This list isi protected by the rtnl lock. */ +#undef pr_debug +#define pr_debug pr_warning + +/*This list is protected by the rtnl lock. */ static LIST_HEAD(chnl_net_list); MODULE_LICENSE("GPL"); MODULE_ALIAS_RTNL_LINK("caif"); +enum caif_states { + CAIF_CONNECTED = 1, + CAIF_CONNECTING, + CAIF_DISCONNECTED, + CAIF_SHUTDOWN +}; + struct chnl_net { - struct layer chnl; + struct cflayer chnl; struct net_device_stats stats; - struct caif_channel_config config; + 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; }; - +//deprecated-functionality-below +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)) static struct chnl_net *find_device(char *name) { struct list_head *list_node; @@ -71,7 +84,8 @@ static struct chnl_net *find_device(char *name) } return dev; } - +#endif +//deprecated-functionality-above static void robust_list_del(struct list_head *delete_node) { struct list_head *list_node; @@ -80,15 +94,16 @@ static void robust_list_del(struct list_head *delete_node) list_for_each_safe(list_node, n, &chnl_net_list) { if (list_node == delete_node) { list_del(list_node); - break; + return; } } + WARN_ON(1); } -static int chnl_recv_cb(struct layer *layr, struct cfpkt *pkt) +static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt) { struct sk_buff *skb; - struct chnl_net *priv = NULL; + struct chnl_net *priv = container_of(layr, struct chnl_net, chnl); int pktlen; int err = 0; @@ -107,14 +122,12 @@ static int chnl_recv_cb(struct layer *layr, struct cfpkt *pkt) skb->dev = priv->netdev; skb->protocol = htons(ETH_P_IP); - if (priv->config.type == CAIF_CHTY_DATAGRAM_LOOP) { - /* We change the header, so the checksum is corrupted. */ + /* If we change the header in loop mode, the checksum is corrupted. */ + if (priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP) skb->ip_summed = CHECKSUM_UNNECESSARY; - } else { - skb->ip_summed = CHECKSUM_COMPLETE; - } + else + skb->ip_summed = CHECKSUM_NONE; - /* FIXME: Drivers should call this in tasklet context. */ if (in_interrupt()) netif_rx(skb); else @@ -127,11 +140,38 @@ static int chnl_recv_cb(struct layer *layr, struct cfpkt *pkt) return err; } -static void chnl_flowctrl_cb(struct layer *layr, enum caif_ctrlcmd flow, +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; + /* May be called with or without RTNL lock held */ + int islocked = rtnl_is_locked(); + if (!islocked) + rtnl_lock(); + list_for_each_safe(list_node, _tmp, &chnl_net_list) { + dev = list_entry(list_node, struct chnl_net, list_field); + if (dev->state == CAIF_SHUTDOWN) + dev_close(dev->netdev); + } + if (!islocked) + rtnl_unlock(); +} +static DECLARE_WORK(close_worker, close_work); + +static void chnl_flowctrl_cb(struct cflayer *layr, enum caif_ctrlcmd flow, int phyid) { - struct chnl_net *priv = NULL; - pr_debug("CAIF: %s(): NET flowctrl func called flow: %s.\n", + struct chnl_net *priv = container_of(layr, struct chnl_net, chnl); + pr_debug("CAIF: %s(): NET flowctrl func called flow: %s\n", __func__, flow == CAIF_CTRLCMD_FLOW_ON_IND ? "ON" : flow == CAIF_CTRLCMD_INIT_RSP ? "INIT" : @@ -141,41 +181,48 @@ static void chnl_flowctrl_cb(struct layer *layr, enum caif_ctrlcmd flow, flow == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND ? "REMOTE_SHUTDOWN" : "UKNOWN CTRL COMMAND"); - priv = container_of(layr, struct chnl_net, chnl); + switch (flow) { case CAIF_CTRLCMD_FLOW_OFF_IND: + 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->flowenabled = false; + priv->state = CAIF_SHUTDOWN; netif_tx_disable(priv->netdev); - wake_up_interruptible(&priv->netmgmt_wq); - break; + 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: + priv->state = CAIF_CONNECTED; priv->flowenabled = true; netif_wake_queue(priv->netdev); wake_up_interruptible(&priv->netmgmt_wq); - break; + break; default: break; } } -static int chnl_net_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +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; - ASSERT_RTNL(); - /* Get our private data. */ - priv = (struct chnl_net *)netdev_priv(dev); - if (!priv) - return -ENOSPC; - + priv = netdev_priv(dev); if (skb->len > priv->netdev->mtu) { pr_warning("CAIF: %s(): Size of skb exceeded MTU\n", __func__); @@ -187,16 +234,9 @@ static int chnl_net_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_BUSY; } - if (priv->config.type == CAIF_CHTY_DATAGRAM_LOOP) { - struct iphdr *hdr; - __be32 swap; - /* Retrieve IP header. */ - hdr = ip_hdr(skb); - /* Change source and destination address. */ - swap = hdr->saddr; - hdr->saddr = hdr->daddr; - hdr->daddr = swap; - } + if (priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP) + swap(ip_hdr(skb)->saddr, ip_hdr(skb)->daddr); + /* Store original SKB length. */ len = skb->len; @@ -205,7 +245,7 @@ static int chnl_net_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) /* Send the packet down the stack. */ result = priv->chnl.dn->transmit(priv->chnl.dn, pkt); if (result) { - if (result == CFGLU_ERETRY) + if (result == -EAGAIN) result = NETDEV_TX_BUSY; return result; } @@ -217,120 +257,96 @@ static int chnl_net_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } - static int chnl_net_open(struct net_device *dev) { struct chnl_net *priv = NULL; int result = -1; ASSERT_RTNL(); - - priv = (struct chnl_net *)netdev_priv(dev); - pr_debug("CAIF: %s(): dev name: %s\n", __func__, priv->name); - + priv = netdev_priv(dev); if (!priv) { pr_debug("CAIF: %s(): chnl_net_open: no priv\n", __func__); return -ENODEV; } - result = caifdev_adapt_register(&priv->config, &priv->chnl); - if (result != 0) { - pr_debug("CAIF: %s(): err: " - "Unable to register and open device, Err:%d\n", - __func__, - result); - return -ENODEV; + + if (priv->state != CAIF_CONNECTING) { + priv->state = CAIF_CONNECTING; + result = caif_connect_client(&priv->conn_req, &priv->chnl); + if (result != 0) { + priv->state = CAIF_DISCONNECTED; + pr_debug("CAIF: %s(): err: " + "Unable to register and open device," + " Err:%d\n", + __func__, + result); + return result; + } } - result = wait_event_interruptible(priv->netmgmt_wq, priv->flowenabled); + + result = wait_event_interruptible_timeout(priv->netmgmt_wq, + priv->state != CAIF_CONNECTING, + CONNECT_TIMEOUT); if (result == -ERESTARTSYS) { pr_debug("CAIF: %s(): wait_event_interruptible" " woken by a signal\n", __func__); return -ERESTARTSYS; - } else - pr_debug("CAIF: %s(): Flow on recieved\n", __func__); + } + if (result == 0) { + pr_debug("CAIF: %s(): connect timeout\n", __func__); + caif_disconnect_client(&priv->chnl); + priv->state = CAIF_DISCONNECTED; + pr_debug("CAIF: %s(): state disconnected\n", __func__); + return -ETIMEDOUT; + } + if (priv->state != CAIF_CONNECTED) { + pr_debug("CAIF: %s(): connect failed\n", __func__); + return -ECONNREFUSED; + } + pr_debug("CAIF: %s(): CAIF Netdevice connected\n", __func__); return 0; } static int chnl_net_stop(struct net_device *dev) { struct chnl_net *priv; - int result = -1; - ASSERT_RTNL(); - priv = (struct chnl_net *)netdev_priv(dev); - - result = caifdev_adapt_unregister(&priv->chnl); - if (result != 0) { - pr_debug("CAIF: %s(): chnl_net_stop: err: " - "Unable to STOP device, Err:%d\n", - __func__, result); - return -EBUSY; - } - result = wait_event_interruptible(priv->netmgmt_wq, - !priv->flowenabled); - - if (result == -ERESTARTSYS) { - pr_debug("CAIF: %s(): wait_event_interruptible woken by" - " signal, signal_pending(current) = %d\n", - __func__, - signal_pending(current)); - } else { - pr_debug("CAIF: %s(): disconnect received\n", __func__); - - } + ASSERT_RTNL(); + priv = netdev_priv(dev); + priv->state = CAIF_DISCONNECTED; + caif_disconnect_client(&priv->chnl); return 0; } -int chnl_net_init(struct net_device *dev) +static int chnl_net_init(struct net_device *dev) { struct chnl_net *priv; ASSERT_RTNL(); - priv = (struct chnl_net *)netdev_priv(dev); - strncpy(priv->config.name, dev->name, sizeof(priv->config.name)); + priv = netdev_priv(dev); strncpy(priv->name, dev->name, sizeof(priv->name)); return 0; } -void chnl_net_uninit(struct net_device *dev) +static void chnl_net_uninit(struct net_device *dev) { struct chnl_net *priv; ASSERT_RTNL(); - priv = (struct chnl_net *)netdev_priv(dev); - /* If someone already have the lock it's already protected */ + priv = netdev_priv(dev); robust_list_del(&priv->list_field); - dev_put(dev); } -//official-kernel-patch-cut-here -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 28)) -//official-kernel-patch-resume-here 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_hard_start_xmit, + .ndo_start_xmit = chnl_net_start_xmit, }; -//official-kernel-patch-cut-here -#endif -//official-kernel-patch-resume-here -static void ipcaif_net_init(struct net_device *dev) +static void ipcaif_net_setup(struct net_device *dev) { struct chnl_net *priv; -//official-kernel-patch-cut-here -#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 28)) - dev->open = chnl_net_open; - dev->stop = chnl_net_stop; - dev->init = chnl_net_init; - dev->uninit = chnl_net_uninit; - dev->hard_start_xmit = chnl_net_hard_start_xmit; -#else -//official-kernel-patch-resume-here dev->netdev_ops = &netdev_ops; -//official-kernel-patch-cut-here -#endif -//official-kernel-patch-resume-here dev->destructor = free_netdev; dev->flags |= IFF_NOARP; dev->flags |= IFF_POINTOPOINT; @@ -339,14 +355,15 @@ static void ipcaif_net_init(struct net_device *dev) dev->mtu = SIZE_MTU; dev->tx_queue_len = CAIF_NET_DEFAULT_QUEUE_LEN; - priv = (struct chnl_net *)netdev_priv(dev); + priv = netdev_priv(dev); priv->chnl.receive = chnl_recv_cb; priv->chnl.ctrlcmd = chnl_flowctrl_cb; priv->netdev = dev; - priv->config.type = CAIF_CHTY_DATAGRAM; - priv->config.phy_pref = CFPHYPREF_HIGH_BW; - priv->config.priority = CAIF_PRIO_LOW; - priv->config.u.dgm.connection_id = -1; /* Insert illegal value */ + priv->conn_req.protocol = CAIFPROTO_DATAGRAM; + priv->conn_req.link_selector = CAIF_LINK_HIGH_BANDW; + priv->conn_req.priority = CAIF_PRIO_LOW; + /* Insert illegal value */ + priv->conn_req.sockaddr.u.dgm.connection_id = -1; priv->flowenabled = false; ASSERT_RTNL(); @@ -354,24 +371,17 @@ static void ipcaif_net_init(struct net_device *dev) list_add(&priv->list_field, &chnl_net_list); } -static int delete_device(struct chnl_net *dev) -{ - ASSERT_RTNL(); - if (dev->netdev) - unregister_netdevice(dev->netdev); - return 0; -} static int ipcaif_fill_info(struct sk_buff *skb, const struct net_device *dev) { struct chnl_net *priv; u8 loop; - priv = (struct chnl_net *)netdev_priv(dev); + priv = netdev_priv(dev); NLA_PUT_U32(skb, IFLA_CAIF_IPV4_CONNID, - priv->config.u.dgm.connection_id); + priv->conn_req.sockaddr.u.dgm.connection_id); NLA_PUT_U32(skb, IFLA_CAIF_IPV6_CONNID, - priv->config.u.dgm.connection_id); - loop = priv->config.type == CAIF_CHTY_DATAGRAM_LOOP; + priv->conn_req.sockaddr.u.dgm.connection_id); + loop = priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP; NLA_PUT_U8(skb, IFLA_CAIF_LOOPBACK, loop); @@ -382,52 +392,56 @@ nla_put_failure: } static void caif_netlink_parms(struct nlattr *data[], - struct caif_channel_config *parms) + struct caif_connect_request *conn_req) { if (!data) { pr_warning("CAIF: %s: no params data found\n", __func__); return; } if (data[IFLA_CAIF_IPV4_CONNID]) - parms->u.dgm.connection_id = + conn_req->sockaddr.u.dgm.connection_id = nla_get_u32(data[IFLA_CAIF_IPV4_CONNID]); if (data[IFLA_CAIF_IPV6_CONNID]) - parms->u.dgm.connection_id = + 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])) - parms->type = CAIF_CHTY_DATAGRAM_LOOP; + conn_req->protocol = CAIFPROTO_DATAGRAM_LOOP; else - parms->type = CAIF_CHTY_DATAGRAM; + conn_req->protocol = CAIFPROTO_DATAGRAM; } } -//official-kernel-patch-cut-here +//deprecated-functionality-below #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)) -//official-kernel-patch-resume-here +//deprecated-functionality-above static int ipcaif_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) -//official-kernel-patch-cut-here +//deprecated-functionality-below #else static int ipcaif_newlink(struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) #endif -//official-kernel-patch-resume-here +//deprecated-functionality-above { - int err; + int ret; struct chnl_net *caifdev; ASSERT_RTNL(); caifdev = netdev_priv(dev); - caif_netlink_parms(data, &caifdev->config); - err = register_netdevice(dev); - if (err) { + caif_netlink_parms(data, &caifdev->conn_req); +//deprecated-functionality-below +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)) +//deprecated-functionality-above + dev_net_set(caifdev->netdev, src_net); +//deprecated-functionality-below +#endif +//deprecated-functionality-above + + ret = register_netdevice(dev); + if (ret) pr_warning("CAIF: %s(): device rtml registration failed\n", __func__); - goto out; - } - dev_hold(dev); -out: - return err; + return ret; } static int ipcaif_changelink(struct net_device *dev, struct nlattr *tb[], @@ -436,7 +450,7 @@ static int ipcaif_changelink(struct net_device *dev, struct nlattr *tb[], struct chnl_net *caifdev; ASSERT_RTNL(); caifdev = netdev_priv(dev); - caif_netlink_parms(data, &caifdev->config); + caif_netlink_parms(data, &caifdev->conn_req); netdev_state_change(dev); return 0; } @@ -462,8 +476,8 @@ static const struct nla_policy ipcaif_policy[IFLA_CAIF_MAX + 1] = { static struct rtnl_link_ops ipcaif_link_ops __read_mostly = { .kind = "caif", - .priv_size = (size_t)sizeof(struct chnl_net), - .setup = ipcaif_net_init, + .priv_size = sizeof(struct chnl_net), + .setup = ipcaif_net_setup, .maxtype = IFLA_CAIF_MAX, .policy = ipcaif_policy, .newlink = ipcaif_newlink, @@ -473,6 +487,8 @@ static struct rtnl_link_ops ipcaif_link_ops __read_mostly = { }; +//deprecated-functionality-below +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)) int chnl_net_ioctl(unsigned int cmd, unsigned long arg, bool from_user_land) { struct chnl_net *priv; @@ -521,58 +537,89 @@ int chnl_net_ioctl(unsigned int cmd, unsigned long arg, bool from_user_land) } netdevptr = alloc_netdev(sizeof(struct chnl_net), - ifreq.ifr_name, ipcaif_net_init); + ifreq.ifr_name, ipcaif_net_setup); if (!netdevptr) { rtnl_unlock(); return -ENODEV; } - dev_hold(netdevptr); - priv = (struct chnl_net *)netdev_priv(netdevptr); - priv->config.u.dgm.connection_id = param.ipv4_connid; + priv = netdev_priv(netdevptr); + priv->conn_req.sockaddr.u.dgm.connection_id = param.ipv4_connid; if (param.loop) - priv->config.type = CAIF_CHTY_DATAGRAM_LOOP; + priv->conn_req.protocol = CAIFPROTO_DATAGRAM_LOOP; else - priv->config.type = CAIF_CHTY_DATAGRAM; + priv->conn_req.protocol = CAIFPROTO_DATAGRAM; result = register_netdevice(priv->netdev); if (result < 0) { pr_warning("CAIF: %s(): can't register netdev %s %d\n", __func__, ifreq.ifr_name, result); - dev_put(netdevptr); rtnl_unlock(); return -ENODEV; } pr_debug("CAIF: %s(): netdev channel open:%s\n", __func__, priv->name); rtnl_unlock(); return 0; -}; +} +#endif +#include <linux/caif/caif_config.h> struct net_device *chnl_net_create(char *name, - struct caif_channel_config *config) + struct caif_channel_config *config) { struct net_device *dev; + struct caif_connect_request s; + memset(&s,0,sizeof(s)); ASSERT_RTNL(); - dev = alloc_netdev(sizeof(struct chnl_net), name, ipcaif_net_init); + switch(config->type) { + case CAIF_CHTY_DATAGRAM: + s.protocol = CAIFPROTO_DATAGRAM; + break; + + case CAIF_CHTY_DATAGRAM_LOOP: + s.protocol = CAIFPROTO_DATAGRAM_LOOP; + break; + default: + return NULL; + } + strcpy(s.link_name, config->phy_name); + switch (config->phy_pref) { + case CAIF_PHYPREF_UNSPECIFIED: + case CAIF_PHYPREF_HIGH_BW: + s.link_selector = CAIF_LINK_HIGH_BANDW; + break; + case CAIF_PHYPREF_LOW_LAT: + s.link_selector = CAIF_LINK_LOW_LATENCY; + break; + default: + return NULL; + } + s.sockaddr.u.dgm.connection_id = + config->u.dgm.connection_id; + dev = alloc_netdev(sizeof(struct chnl_net), name, ipcaif_net_setup); if (!dev) return NULL; - ((struct chnl_net *)netdev_priv(dev))->config = *config; - dev_hold(dev); + ((struct chnl_net *)netdev_priv(dev))->conn_req = s; return dev; } EXPORT_SYMBOL(chnl_net_create); - +//deprecated-functionality-above static int __init chnl_init_module(void) { - int err = -1; - caif_register_ioctl(chnl_net_ioctl); - err = rtnl_link_register(&ipcaif_link_ops); - if (err < 0) { - rtnl_link_unregister(&ipcaif_link_ops); - return err; - } - return 0; +//deprecated-functionality-below +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)) + int ret; + ret = rtnl_link_register(&ipcaif_link_ops); + if (!ret) + caif_register_ioctl(chnl_net_ioctl); + return ret; +#else +//deprecated-functionality-above + return rtnl_link_register(&ipcaif_link_ops); +//deprecated-functionality-below +#endif +//deprecated-functionality-above } static void __exit chnl_exit_module(void) @@ -580,14 +627,19 @@ 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); +//deprecated-functionality-below +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)) + caif_register_ioctl(NULL); +#endif +//deprecated-functionality-above 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(); - rtnl_link_unregister(&ipcaif_link_ops); - caif_register_ioctl(NULL); } module_init(chnl_init_module); |