[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-id: <1253727086-10353-1-git-send-email-sjur.brandeland@stericsson.com>
Date: Wed, 23 Sep 2009 19:31:26 +0200
From: sjur.brandeland@...ricsson.com
To: netdev@...r.kernel.org
Cc: Kim.xx.Lilliestierna@...csson.com, sjur.brandeland@...ricsson.com
Subject: [PATCH 5/8] [RFC] CAIF Protocol Stack
From: Kim Lilliestierna <Kim.xx.Lilliestierna@...csson.com>
Signed-off-by: sjur.brandeland@...ricsson.com
---
net/caif/Kconfig | 61 +++
net/caif/Makefile | 62 +++
net/caif/caif_chnlif.c | 219 ++++++++
net/caif/caif_chr.c | 378 ++++++++++++++
net/caif/caif_config_util.c | 167 +++++++
net/caif/chnl_chr.c | 1161 +++++++++++++++++++++++++++++++++++++++++++
net/caif/chnl_net.c | 464 +++++++++++++++++
7 files changed, 2512 insertions(+), 0 deletions(-)
create mode 100644 net/caif/Kconfig
create mode 100644 net/caif/Makefile
create mode 100644 net/caif/caif_chnlif.c
create mode 100644 net/caif/caif_chr.c
create mode 100644 net/caif/caif_config_util.c
create mode 100644 net/caif/chnl_chr.c
create mode 100644 net/caif/chnl_net.c
diff --git a/net/caif/Kconfig b/net/caif/Kconfig
new file mode 100644
index 0000000..24151c1
--- /dev/null
+++ b/net/caif/Kconfig
@@ -0,0 +1,61 @@
+#
+# CAIF net configurations
+#
+
+#menu "Caif Support"
+comment "CAIF Support"
+
+menuconfig CAIF
+ tristate "Enable Caif support"
+ default m
+ ---help---
+ Say Y here if you need to a phone modem that uses CAIF as transport
+ You will also need to say yes to anny caif physical devices that your platform
+ supports.
+ This can be built as either built in or as a module, if you select to build it as module
+ the other CAIF parts also needs to built as modules
+ See Documentation/CAIF for a further explanation on how to use and configure.
+
+if CAIF
+
+config CAIF_CHARDEV
+ tristate "CAIF character device"
+ default CAIF
+ ---help---
+ Say Y if you will be using the CAIF character devices,
+ This is needed for AT type channels
+ If you select to build it as a built in then the main caif device must also be a builtin
+ If unsure say Y
+
+config CAIF_NETDEV
+ tristate "CAIF Network device"
+ default CAIF
+ ---help---
+ If you select to build it as a built in then the main caif device must also be a builtin
+ Say Y if you will be using the CAIF based network device
+ If unsure say Y
+
+
+config CAIF_USE_PLAIN
+ bool "Use plain buffers instead of SKB in caif"
+ default n
+ ---help---
+ Use plain buffer to transport data
+ Select what type of internal buffering CAIF should use,
+ skb or plain,
+ If unsure say y
+
+config CAIF_DEBUG
+ bool "Enable Debug"
+ default n
+ --- help ---
+ Enable the inclusion of debug code in the caif stack
+ be aware that doing this will impact performance
+ If unsure say n here.
+
+# Include physical drivers
+# should be broken out into its own config file
+# source "drivers/net/caif/Kconfig"
+source "drivers/net/caif/Kconfig"
+endif
+#endmenu
diff --git a/net/caif/Makefile b/net/caif/Makefile
new file mode 100644
index 0000000..49696ab
--- /dev/null
+++ b/net/caif/Makefile
@@ -0,0 +1,62 @@
+ifeq ($(CONFIG_CAIF_USE_PLAIN),1)
+CFPKT:=plain
+else
+CFPKT:=skbuff
+CAIF_FLAGS+=-DCAIF_USE_SKB
+endif
+
+ifeq ($(CONFIG_CAIF_DEBUG),1)
+CAIF_FLAGS+=-DCAIF_DEBUG_ON
+endif
+
+
+ccflags-y := -DCAIF_KERNEL $(CAIF_FLAGS) -Iinclude/net/caif/ -Iinclude/net/caif/generic/ -Iinclude/linux/caif/
+
+
+caif-objs := caif_chr.o caif_chnlif.o caif_config_util.o \
+ generic/cfcnfg.o generic/cfmuxl.o generic/cfctrl.o \
+ generic/cffrml.o generic/cfveil.o generic/cflist.o \
+ generic/fcs.o generic/cfserl.o generic/cfdgml.o \
+ generic/cfspil.o generic/cfrfml.o generic/cfvidl.o \
+ generic/cfmsll.o generic/cfutill.o generic/cfshml.o \
+ generic/cfloopcfg.o generic/cflooplayer.o generic/cfsrvl.o \
+ generic/cfpkt_$(CFPKT).o
+
+
+clean-dirs:= .tmp_versions
+
+clean-files:= Module.symvers modules.order *.cmd *~ \
+ generic/loopback/Module.symvers \
+ generic/loopback/modules.order \
+ generic/loopback/*.cmd \
+ generic/loopback/*.o \
+ generic/loopback/*~ \
+ generic/Module.symvers \
+ generic/modules.order \
+ generic/*.cmd \
+ generic/*.o \
+ generic/*~
+
+
+# Main caif module
+obj-$(CONFIG_CAIF) += caif.o
+
+# Character device
+obj-$(CAIF_CHRDEV) += chnl_chr.o
+
+# Net device
+obj-$(CAIF_NETDEV) += chnl_net.o
+
+export-objs := caif_chr.o
+
+## Indent.. to remove all DOS cruft like CRLF and trailing spaces, and to standardize the indenting style
+indent:
+ ${MAKE} -C generic indent
+ sh -c 'for F in *.[ch]; do cat $$F | tr -d "\r" |tr -c '\015'| sed "s/[ \t]*$$//;" > $$F.tmp; mv $$F.tmp $$F; done'
+ ${INDENT} -kr -i8 *.[ch]
+
+clean:
+ ${MAKE} -C generic clean
+ rm generic/modules.order generic/Module.symvers generic/*.cmd generic/*~ \
+ generic/modules.order generic/Module.symvers \
+ generic/*.o generic/loopback/*.o
diff --git a/net/caif/caif_chnlif.c b/net/caif/caif_chnlif.c
new file mode 100644
index 0000000..e53ca7a
--- /dev/null
+++ b/net/caif/caif_chnlif.c
@@ -0,0 +1,219 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Daniel Martensson / Daniel.Martensson@...ricsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+#include <linux/skbuff.h>
+#include "caif_kernel.h"
+#include "caif_layer.h"
+#include "caif_config_util.h"
+#include "caif_log.h"
+#include "cfpkt.h"
+#include "cfcnfg.h"
+#include "cfglue.h"
+
+
+
+struct caif_kernelif {
+ layer_t layer;
+ struct caif_device *dev;
+ cfctrl_link_param_t param;
+};
+static cfcnfg_t *cnfg;
+/**
+ * func chnlif_set_cnfg - Set the global config
+ * @cfg: Config structure to set
+*/
+
+void chnlif_set_cnfg(cfcnfg_t *cfg)
+{
+ cnfg = cfg;
+}
+EXPORT_SYMBOL(chnlif_set_cnfg);
+
+/**
+ * func caif_create_skb - Creates a caif skb buffer
+ * @data: data to add to buffer
+ * @data_length: lenht of data
+*/
+struct sk_buff *caif_create_skb(unsigned char *data, unsigned int data_length)
+{
+ /* NOTE: Make room for CAIF headers when using SKB inside CAIF. */
+ struct sk_buff *skb =
+ alloc_skb(data_length + CAIF_SKB_HEAD_RESERVE +
+ CAIF_SKB_TAIL_RESERVE, GFP_ATOMIC);
+ skb_reserve(skb, CAIF_SKB_HEAD_RESERVE);
+ if (skb == NULL)
+ return NULL;
+
+ memcpy(skb_put(skb, data_length), data, data_length);
+ return skb;
+}
+EXPORT_SYMBOL(caif_create_skb);
+
+int
+caif_extract_and_destroy_skb(struct sk_buff *skb, unsigned char *data,
+ unsigned int max_length)
+{
+ unsigned int len;
+ len = skb->len;
+ skb_linearize(skb);
+ if (skb->len > max_length)
+ return CFGLU_EOVERFLOW;
+ memcpy(data, skb->data, skb->len);
+ kfree_skb(skb);
+ return len;
+}
+EXPORT_SYMBOL(caif_extract_and_destroy_skb);
+
+/* NOTE: transmit takes ownership of the SKB.
+ * i.e. transmit only fails on severe errors.
+ * flow_off is not checked on transmit this is clients responcibility.
+ */
+
+int caif_transmit(struct caif_device *dev, struct sk_buff *skb)
+{
+ struct caif_kernelif *chnlif =
+ (struct caif_kernelif *) dev->_caif_handle;
+ cfpkt_t *pkt;
+#ifdef CAIF_USE_SKB
+ pkt = cfpkt_fromnative(CAIF_DIR_OUT, (void *) skb);
+#else
+ pkt = cfpkt_create(skb->len);
+ cfpkt_add_body(pkt, skb->data, skb->len);
+ kfree_skb(skb);
+#endif
+ CAIFLOG_TRACE2("Transmit (%p)", chnlif->layer.dn);
+ return chnlif->layer.dn->transmit(chnlif->layer.dn, NULL, pkt);
+}
+EXPORT_SYMBOL(caif_transmit);
+
+
+int caif_flow_control(struct caif_device *dev, enum caif_flowctrl flow)
+{
+ caif_modemcmd_t modemcmd;
+ struct caif_kernelif *chnlif =
+ (struct caif_kernelif *) dev->_caif_handle;
+ switch (flow) {
+ case CAIF_FLOWCTRL_ON:
+ modemcmd = CAIF_MODEMCMD_FLOW_ON_REQ;
+ break;
+ case CAIF_FLOWCTRL_OFF:
+ modemcmd = CAIF_MODEMCMD_FLOW_OFF_REQ;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return chnlif->layer.dn->modemcmd(chnlif->layer.dn, modemcmd);
+}
+EXPORT_SYMBOL(caif_flow_control);
+
+
+static int chnlif_receive(layer_t *layr, struct _cfpkt_t *cfpkt)
+{
+ /* FIXME: Use container of */
+ struct caif_kernelif *chnl = (struct caif_kernelif *) layr;
+
+ struct sk_buff *skb;
+#ifndef CAIF_USE_SKB
+ unsigned int pktlen = cfpkt_getlen(cfpkt);
+ unsigned int actual_len;
+ skb = alloc_skb(pktlen, GFP_ATOMIC);
+ if (skb == NULL)
+ return CFGLU_ENOMEM;
+
+ /* Extract data from the caif packet and copy it to the skb. */
+ cfpkt_extract(cfpkt, skb_put(skb, pktlen), pktlen, &actual_len);
+ cfglu_assert(pktlen == actual_len);
+ cfpkt_destroy(cfpkt);
+#else
+ skb = (struct sk_buff *) cfpkt_tonative(cfpkt);
+#endif
+ chnl->dev->receive_cb(chnl->dev, skb);
+ return CFGLU_EOK;
+}
+
+static void chnlif_flowctrl(layer_t *layr, caif_ctrlcmd_t ctrl, int phyid)
+{
+ struct caif_kernelif *chnl = (struct caif_kernelif *) layr;
+ enum caif_control ctl;
+
+ CAIFLOG_TRACE("Flow Control received %d\n", ctrl);
+
+ switch (ctrl) {
+ case CAIF_CTRLCMD_FLOW_OFF_IND:
+ ctl = CAIF_CONTROL_FLOW_OFF;
+ break;
+ case CAIF_CTRLCMD_FLOW_ON_IND:
+ ctl = CAIF_CONTROL_FLOW_ON;
+ break;
+ case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND:
+ ctl = CAIF_CONTROL_REMOTE_SHUTDOWN;
+ break;
+ case CAIF_CTRLCMD_DEINIT_RSP:
+ ctl = CAIF_CONTROL_DEV_DEINIT;
+ chnl->dev->_caif_handle = NULL;
+ chnl->dev->control_cb(chnl->dev, ctl);
+ memset(chnl, 0, sizeof(chnl));
+ cfglu_free(chnl);
+ return;
+
+ case CAIF_CTRLCMD_INIT_RSP:
+ ctl = CAIF_CONTROL_DEV_INIT;
+ break;
+ case CAIF_CTRLCMD_INIT_FAIL_RSP:
+ ctl = CAIF_CONTROL_DEV_INIT_FAILED;
+ break;
+ default:
+ return;
+ }
+ chnl->dev->control_cb(chnl->dev, ctl);
+}
+
+
+int caif_add_device(struct caif_device *dev)
+{
+ int ret;
+ struct caif_kernelif *chnl = cfglu_alloc(sizeof(struct caif_kernelif));
+ chnl->dev = dev;
+ chnl->layer.ctrlcmd = chnlif_flowctrl;
+ chnl->layer.receive = chnlif_receive;
+ ret =
+ channel_config_2_link_param(cnfg, &dev->caif_config, &chnl->param);
+
+
+ if (ret < 0) {
+ CAIFLOG_WARN("Bad Channel Configuration\n");
+ ret = CFGLU_EBADPARAM;
+ goto error;
+ }
+
+ if (!cfcnfg_add_adaptation_layer(cnfg, &chnl->param, &chnl->layer)) {
+ ret = CFGLU_ENOTCONN;
+ goto error;
+ }
+
+ dev->_caif_handle = chnl;
+
+ return CFGLU_EOK;
+error:
+ chnl->dev->_caif_handle = NULL;
+ memset(chnl, 0, sizeof(chnl));
+ cfglu_free(chnl);
+ return ret;
+}
+EXPORT_SYMBOL(caif_add_device);
+
+int caif_remove_device(struct caif_device *caif_dev)
+{
+
+ struct caif_kernelif *chnl =
+ container_of(caif_dev->_caif_handle, struct caif_kernelif, layer);
+ bool ok = cfcnfg_del_adapt_layer(cnfg, &chnl->layer);
+ return ok ? CFGLU_EOK : CFGLU_EIO;
+}
+EXPORT_SYMBOL(caif_remove_device);
diff --git a/net/caif/caif_chr.c b/net/caif/caif_chr.c
new file mode 100644
index 0000000..92c60cd
--- /dev/null
+++ b/net/caif/caif_chr.c
@@ -0,0 +1,378 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Daniel Martensson / Daniel.Martensson@...ricsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+
+
+
+
+
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/ioctl.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+
+#include "caif_actions.h"
+#include "caif_chr.h"
+#include "cfloopcfg.h"
+#include "cfpkt.h"
+#include "cfcnfg.h"
+#include "caif_config_util.h"
+#include "caif_log.h"
+#include "caif_ioctl.h"
+
+#define caif_assert(assert) BUG_ON(!(assert))
+
+MODULE_LICENSE("GPL");
+int (*chrdev_mgmt_func) (int action, union caif_action *param);
+int (*netdev_mgmt_func) (int action, union caif_action *param);
+
+struct caif_chr {
+ cfcnfg_t *cfg;
+ cfloopcfg_t *loop;
+ struct miscdevice misc;
+};
+
+int caif_dbg_level = CAIFLOG_LEVEL_WARNING;
+EXPORT_SYMBOL(caif_dbg_level);
+
+static struct caif_chr caifdev;
+
+struct class caif_class = {
+ .name = "caif",
+};
+
+static ssize_t dbg_lvl_show(struct class *class, char *buf)
+{
+ return sprintf(buf, "%d\n", caif_dbg_level);
+}
+
+static ssize_t dbg_lvl_store(struct class *class, const char *buf,
+ size_t count)
+{
+ int val;
+
+ sscanf(buf, "%d", &val);
+ if ((val < CAIFLOG_MIN_LEVEL) || (val > CAIFLOG_MAX_LEVEL)) {
+ printk(KERN_WARNING "caif_dbg_level: Invalid value\n");
+ return -EINVAL;
+ }
+
+ caif_dbg_level = val;
+
+ return count;
+}
+
+CLASS_ATTR(dbg_lvl, 0644, dbg_lvl_show, dbg_lvl_store);
+
+int caifdev_open(struct inode *inode, struct file *filp)
+{
+ printk(KERN_WARNING "caifdev_open: Entered\n");
+ return 0;
+}
+
+int caifdev_release(struct inode *inode, struct file *filp)
+{
+ printk(KERN_WARNING "caifdev_release: Entered\n");
+ return 0;
+}
+
+static int caifdev_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long argp)
+{
+ union caif_action param;
+ int ret;
+ int (*mgmt_func) (int action, union caif_action *param) = NULL;
+ int type;
+ int size;
+ int operation;
+ enum caif_dev_type devtype;
+
+ printk(KERN_WARNING "caifdev_ioctl: Entered\n");
+
+ if (argp == 0) {
+ printk(KERN_INFO "caifdev_ioctl: argument is null\n");
+ return -EINVAL;
+ }
+
+ type = _IOC_TYPE(cmd);
+ printk(KERN_INFO "caifdev_ioctl: type = %d\n", type);
+
+ if (type != CAIF_IOC_MAGIC) {
+ printk(KERN_INFO "caifdev_ioctl: unknown ioctl type\n");
+ return -EINVAL;
+ }
+
+ /* Check if command is valid before copying anything */
+ switch (cmd) {
+ case CAIF_IOC_CONFIG_DEVICE:
+ case CAIF_IOC_REMOVE_DEVICE:
+ break;
+ default:
+ printk(KERN_INFO "caifdev_ioctl: unknown ioctl command\n");
+ return -EINVAL;
+ }
+
+ size = _IOC_SIZE(cmd);
+ printk(KERN_INFO "caifdev_ioctl: size = %d\n", size);
+
+ if (copy_from_user(¶m, (void *) argp, size)) {
+ printk(KERN_WARNING
+ "caifdev_ioctl: copy_from_user returned non zero.\n");
+ return -EINVAL;
+ }
+
+ switch (cmd) {
+
+ case CAIF_IOC_CONFIG_DEVICE:
+
+ operation = CAIF_ACT_CREATE_DEVICE;
+ devtype = param.create_channel.name.devtype;
+ break;
+
+ case CAIF_IOC_REMOVE_DEVICE:
+
+ operation = CAIF_ACT_DELETE_DEVICE;
+ devtype = param.delete_channel.devtype;
+ break;
+
+ default:
+ printk(KERN_INFO
+ "caifdev_ioctl: OTHER ACTIONS NOT YET IMPLEMENTED\n");
+ return -EINVAL;
+ }
+
+ if (devtype == CAIF_DEV_CHR) {
+ printk(KERN_INFO "caifdev_ioctl: device type CAIF_DEV_CHR\n");
+ mgmt_func = chrdev_mgmt_func;
+ } else if (devtype == CAIF_DEV_NET) {
+ printk(KERN_INFO "caifdev_ioctl: device type CAIF_DEV_NET\n");
+ mgmt_func = netdev_mgmt_func;
+ }
+
+ if (mgmt_func == NULL) {
+ printk(KERN_WARNING
+ "caifdev_ioctl: DevType %s is not registered\n",
+ devtype == CAIF_DEV_CHR ? "CHAR" :
+ devtype == CAIF_DEV_NET ? "NET" : "UNKNOWN");
+ return -EINVAL;
+ }
+
+ ret = (*mgmt_func) (operation, ¶m);
+
+ if (ret < 0) {
+ printk(KERN_INFO
+ "caifdev_ioctl: error performing device operation\n");
+ return ret;
+ }
+ if (copy_to_user((void *) argp, ¶m, size)) {
+ printk(KERN_WARNING
+ "caifdev_ioctl: copy_to_user returned non zero.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+const struct file_operations caifdev_fops = {
+ .owner = THIS_MODULE,
+ .open = caifdev_open,
+ .ioctl = caifdev_ioctl,
+ .release = caifdev_release,
+};
+
+void caifdev_exit_module(void)
+{
+ class_remove_file(&caif_class, &class_attr_dbg_lvl);
+ class_unregister(&caif_class);
+ misc_deregister(&caifdev.misc);
+}
+
+int caifdev_init_module(void)
+{
+ int result;
+ /* FIXME: Reverse the directon, rather make chnlif do a get! */
+ extern void chnlif_set_cnfg(cfcnfg_t *cfg);
+
+ caifdev.cfg = cfcnfg_create();
+ if (!caifdev.cfg) {
+ printk(KERN_WARNING "caifdev: err: can't create cfcnfg.\n");
+ goto err_cfcnfg_create_failed;
+ }
+
+ chnlif_set_cnfg(caifdev.cfg);
+
+ caifdev.misc.minor = MISC_DYNAMIC_MINOR;
+ /*FIXME: Rename device to "caifconfig" */
+ caifdev.misc.name = "caifconfig";
+ caifdev.misc.fops = &caifdev_fops;
+
+ result = misc_register(&caifdev.misc);
+
+ if (result < 0) {
+ printk(KERN_WARNING
+ "caifdev: err: %d, can't register misc.\n", result);
+ goto err_misc_register_failed;
+ }
+
+ /* Register class for SYSFS. */
+ result = class_register(&caif_class);
+ if (unlikely(result)) {
+ printk(KERN_WARNING
+ "caifdev: err: %d, can't create sysfs node.\n", result);
+ goto err_class_register_failed;
+ }
+
+ /* Create SYSFS nodes. */
+ result = class_create_file(&caif_class, &class_attr_dbg_lvl);
+ if (unlikely(result)) {
+ printk(KERN_WARNING
+ "caifdev: err: %d, can't create sysfs node.\n", result);
+ goto err_sysfs_create_failed;
+ }
+
+ return result;
+
+err_sysfs_create_failed:
+ class_unregister(&caif_class);
+err_class_register_failed:
+ misc_deregister(&caifdev.misc);
+err_misc_register_failed:
+err_cfcnfg_create_failed:
+ return -ENODEV;
+}
+
+int caifdev_phy_register(layer_t *phyif, cfcnfg_phy_type_t phy_type,
+ cfcnfg_phy_preference_t phy_pref)
+{
+ uint16 phyid;
+
+ /* Hook up the physical interface.
+ * Right now we are not using the returned id. */
+ cfcnfg_add_phy_layer(caifdev.cfg, phy_type, phyif, &phyid, phy_pref);
+ printk(KERN_WARNING "caifdev_phy_register: phy:%p id:%d\n",
+ phyif, phyid);
+
+ printk(KERN_WARNING "caifdev_phy_register: %p ID %d == %d\n",
+ (void *)phyif, phyid, phyif->id);
+ return 0;
+}
+EXPORT_SYMBOL(caifdev_phy_register);
+
+int caifdev_phy_unregister(layer_t *phyif)
+{
+ printk(KERN_WARNING "caifdev_phy_unregister: phy:%p id:%d\n",
+ phyif, phyif->id);
+ cfcnfg_del_phy_layer(caifdev.cfg, phyif);
+ return 0;
+}
+EXPORT_SYMBOL(caifdev_phy_unregister);
+
+
+int caifdev_phy_loop_register(layer_t *phyif, cfcnfg_phy_type_t phy_type)
+{
+ /* Create the loop stack. */
+ caifdev.loop = cfloopcfg_create();
+
+ /* Hook up the loop layer. */
+ cfloopcfg_add_phy_layer(caifdev.loop, phy_type, phyif);
+
+ return 0;
+}
+EXPORT_SYMBOL(caifdev_phy_loop_register);
+
+int caifdev_phy_spi_xmitlen(cfspil_t *layr)
+{
+ return cfspil_xmitlen(layr);
+}
+EXPORT_SYMBOL(caifdev_phy_spi_xmitlen);
+
+cfpkt_t *caifdev_phy_spi_getxmitpkt(cfspil_t *layr)
+{
+ return cfspil_getxmitpkt(layr);
+}
+EXPORT_SYMBOL(caifdev_phy_spi_getxmitpkt);
+
+/* FIXME: Generally comment on the new
+ * and old fashion functions for phy registration.
+ */
+
+int caifdev_adapt_register(struct caif_channel_config *config,
+ layer_t *adap_layer)
+{
+ cfctrl_link_param_t param;
+
+ if (channel_config_2_link_param(caifdev.cfg, config, ¶m) == 0)
+ /* Hook up the adaptation layer. */
+ if (cfcnfg_add_adaptation_layer(caifdev.cfg,
+ ¶m, adap_layer))
+ return 0;
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(caifdev_adapt_register);
+
+
+/* FIXME: Comment on this function being new and not yet in use */
+int caifdev_adapt_unregister(layer_t *adap_layer)
+{
+ if (cfcnfg_del_adapt_layer(caifdev.cfg, adap_layer))
+ return 0;
+ else
+ return -1;
+}
+EXPORT_SYMBOL(caifdev_adapt_unregister);
+
+int caif_register_chrdev(int (*chrdev_mgmt)
+ (int action, union caif_action *param))
+{
+
+ chrdev_mgmt_func = chrdev_mgmt;
+ return 0;
+}
+EXPORT_SYMBOL(caif_register_chrdev);
+
+int caif_register_netdev(int (*netdev_mgmt)
+ (int action, union caif_action *param))
+{
+ netdev_mgmt_func = netdev_mgmt;
+ return 0;
+}
+EXPORT_SYMBOL(caif_register_netdev);
+
+
+void caif_unregister_chrdev()
+{
+ chrdev_mgmt_func = NULL;
+}
+EXPORT_SYMBOL(caif_unregister_chrdev);
+
+void caif_unregister_netdev()
+{
+ netdev_mgmt_func = NULL;
+}
+EXPORT_SYMBOL(caif_unregister_netdev);
+
+/* Exported CAIF functions. */
+EXPORT_SYMBOL(cfcnfg_get_packet_funcs);
+
+
+extern int serial_use_stx;
+EXPORT_SYMBOL(serial_use_stx);
+
+extern cfglu_atomic_t cfpkt_packet_count;
+EXPORT_SYMBOL(cfpkt_packet_count);
+module_exit(caifdev_exit_module);
+module_init(caifdev_init_module);
diff --git a/net/caif/caif_config_util.c b/net/caif/caif_config_util.c
new file mode 100644
index 0000000..093d1be
--- /dev/null
+++ b/net/caif/caif_config_util.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2009
+ *
+ * Author: Daniel Martensson / Daniel.Martensson@...ricsson.com
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ *
+ */
+
+#include "cfglue.h"
+#include "cfctrl.h"
+#include "cfcnfg.h"
+#include "caif_config_util.h"
+#include "caif_config.h"
+#include "caif_ioctl.h"
+#include "caif_actions.h"
+
+
+
+int
+channel_config_2_link_param(cfcnfg_t *cnfg, struct caif_channel_config *s,
+ cfctrl_link_param_t *l)
+{
+
+ cfcnfg_phy_preference_t pref;
+
+ memset(l, 0, sizeof(*l));
+
+ l->priority = s->priority;
+
+ if (s->phy_name[0] != '\0') {
+ l->phyid = cfcnfg_get_named(cnfg, s->phy_name);
+ CFLOG_TRACE(("PHYNAME: '%s' -> id:%d\n", s->phy_name,
+ l->phyid));
+
+ } else {
+ switch (s->phy_pref) {
+ case CAIF_PHYPREF_UNSPECIFIED:
+ pref = CFPHYPREF_UNSPECIFIED;
+ break;
+ case CAIF_PHYPREF_LOW_LAT:
+ pref = CFPHYPREF_LOW_LAT;
+ break;
+ case CAIF_PHYPREF_HIGH_BW:
+ pref = CFPHYPREF_HIGH_BW;
+ break;
+ case _CAIF_PHYPREF_LOOP:
+ pref = CFPHYPREF_LOOP;
+ break;
+ case _CAIF_PHYPREF_RAW_LOOP:
+ pref = CFPHYPREF_RAW_LOOP;
+ break;
+ default:
+ CFLOG_TRACE(("PHYPREF: "
+ "No Preferered Device Specified '%d'\n",
+ s->phy_pref));
+ return -CFGLU_ENODEV;
+ }
+ l->phyid = cfcnfg_get_phyid(cnfg, pref);
+ CFLOG_TRACE(("PHYPREF: '%d' -> id:%d\n", pref, l->phyid));
+ }
+
+ switch (s->type) {
+ case CAIF_CHTY_AT:
+ l->linktype = CFCTRL_SRV_VEI;
+ l->chtype = 0x02;
+ l->endpoint = 0x00;
+ CFLOG_TRACE(("CHTY: AT\n"));
+ break;
+
+ case CAIF_CHTY_AT_CTRL:
+ CFLOG_TRACE(("CHTY: AT_CTRL\n"));
+ l->linktype = CFCTRL_SRV_VEI;
+ l->chtype = 0x00;
+ l->endpoint = 0x00;
+ break;
+
+ case CAIF_CHTY_AT_PAIRED:
+ CFLOG_TRACE(("CHTY: AT_PAIRED\n"));
+ l->linktype = CFCTRL_SRV_VEI;
+ l->chtype = 0x03;
+ l->endpoint = 0x00;
+ break;
+
+ case CAIF_CHTY_VIDEO:
+ CFLOG_TRACE(("CHTY: VIDEO\n"));
+ l->linktype = CFCTRL_SRV_VIDEO;
+ l->chtype = 0x00;
+ l->endpoint = 0x00;
+ l->u.video.connid = s->u.dgm.connection_id;
+ break;
+
+ case CAIF_CHTY_DATAGRAM:
+ CFLOG_TRACE(("CHTY: DATAGRAM\n"));
+ l->linktype = CFCTRL_SRV_DATAGRAM;
+ l->chtype = 0x00;
+ l->u.datagram.connid = s->u.dgm.connection_id;
+ break;
+
+ case CAIF_CHTY_DATAGRAM_LOOP:
+ CFLOG_TRACE(("CHTY: DATAGRAM\n"));
+ l->linktype = CFCTRL_SRV_DATAGRAM;
+ l->chtype = 0x03;
+ l->endpoint = 0x00;
+ l->u.datagram.connid = s->u.dgm.connection_id;
+ break;
+
+ case CAIF_CHTY_DEBUG:
+ CFLOG_TRACE(("CHTY: DEBUG\n"));
+ l->linktype = CFCTRL_SRV_DBG;
+ l->endpoint = 0x01; /* ACC SIDE */
+ l->chtype = 0x00; /* Single channel with interactive
+ debug and print-out mixed */
+ break;
+
+ case CAIF_CHTY_DEBUG_INTERACT:
+ CFLOG_TRACE(("CHTY: IDEBUG\n"));
+ l->linktype = CFCTRL_SRV_DBG;
+ l->endpoint = 0x01; /* ACC SIDE */
+ l->chtype = 0x02; /* Interactive debug only */
+ break;
+
+ case CAIF_CHTY_DEBUG_TRACE:
+ CFLOG_TRACE(("CHTY: TRACE\n"));
+ l->linktype = CFCTRL_SRV_DBG;
+ l->endpoint = 0x01; /* ACC SIDE */
+ l->chtype = 0x01; /* Debug print-out only */
+ break;
+
+ case CAIF_CHTY_RFM:
+
+ CFLOG_TRACE(("CHTY: RFN\n"));
+ l->linktype = CFCTRL_SRV_RFM;
+ l->u.datagram.connid = s->u.rfm.connection_id;
+ strncpy(l->u.rfm.volume, s->u.rfm.volume,
+ sizeof(l->u.rfm.volume));
+ break;
+
+ case CAIF_CHTY_UTILITY:
+ CFLOG_TRACE(("CHTY: UTILTY\n"));
+ l->linktype = CFCTRL_SRV_UTIL;
+ l->endpoint = 0x00;
+ l->chtype = 0x00;
+ l->u.utility.fifosize_bufs = s->u.utility.fifosize_bufs;
+ l->u.utility.fifosize_kb = s->u.utility.fifosize_kb;
+ strncpy(l->u.utility.name, s->u.utility.name,
+ sizeof(l->u.utility.name));
+ l->u.utility.paramlen = s->u.utility.paramlen;
+ if (l->u.utility.paramlen > sizeof(l->u.utility.params))
+ l->u.utility.paramlen = sizeof(l->u.utility.params);
+ memcpy(l->u.utility.params, s->u.utility.params,
+ l->u.utility.paramlen);
+ break;
+
+ case CAIF_CHTY_RAW:
+ l->linktype = s->u._raw.channeltype;
+ l->endpoint = s->u._raw.endpoint;
+ l->chtype = s->u._raw.subtype;
+ break;
+
+ default:
+ CFLOG_TRACE(("CAIF_CHTY: Specified bad channel type '%d'\n",
+ s->type));
+ return -CFGLU_EINVAL;
+ }
+ return 0;
+}
diff --git a/net/caif/chnl_chr.c b/net/caif/chnl_chr.c
new file mode 100644
index 0000000..b45e93c
--- /dev/null
+++ b/net/caif/chnl_chr.c
@@ -0,0 +1,1161 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Per Sigmond / Per.Sigmond@...ricsson.com
+*
+* License terms: GNU General Public License (GPL), version 2.
+*
+*/
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/poll.h>
+#include <linux/uaccess.h>
+#include <asm/atomic.h>
+
+/* Caif header files. */
+#include "caif_layer.h"
+#include "cfcnfg.h"
+#include "cfpkt.h"
+#include "cffrml.h"
+#include "caif_chr.h"
+#include "caif_config.h"
+#include "caif_config_util.h"
+#include "caif_actions.h"
+#include "caif_log.h"
+MODULE_LICENSE("GPL");
+
+#define COUNTER_DEBUG 0
+
+/* Seconds */
+#define CAIF_CONNECT_TIMEOUT 30
+
+#define CHNL_CHR_READ_QUEUE_HIGH 2000
+#define CHNL_CHR_READ_QUEUE_LOW 100
+
+static LIST_HEAD(caif_chrdev_list);
+static spinlock_t list_lock;
+
+#define STATE_IS_OPEN_BIT 1
+#define STATE_OPEN_PENDING_BIT 2
+#define STATE_IS_CLOSED_BIT 3
+#define STATE_CLOSE_PENDING_BIT 4
+#define STATE_TX_FLOW_ON 5
+#define STATE_RX_FLOW_OFF 6
+
+
+
+#define CHR_READ_FLAG 0x01
+#define CHR_WRITE_FLAG 0x02
+
+#define chnl_assert(assert) BUG_ON(!(assert))
+
+
+struct caif_char_dev {
+ layer_t layer;
+ unsigned int status;
+ cfpktq_t *pktq;
+ char name[256]; /* Redundnt! Already in struct miscdevice */
+ struct miscdevice misc;
+ int file_mode;
+ caif_packet_funcs_t pktf;
+ struct caif_channel_config config;
+ /* Access to this struct and below layers */
+ struct mutex mutex;
+ int read_queue_len;
+ spinlock_t read_queue_len_lock;
+ wait_queue_head_t read_wq;
+ wait_queue_head_t mgmt_wq;
+ /* List of misc test devices */
+ struct list_head list_field;
+#if COUNTER_DEBUG
+ unsigned long counter;
+ int mismatch_reported;
+#endif
+
+};
+
+/** Packet Receive Callback function called from CAIF Stack */
+
+static int caif_chrrecv_cb(layer_t *layr, cfpkt_t *pkt)
+{
+ struct caif_char_dev *dev;
+#if COUNTER_DEBUG
+ unsigned long *data_p;
+#endif
+
+ CAIFLOG_ENTER("");
+ dev = container_of(layr, struct caif_char_dev, layer);
+
+ CAIFLOG_TRACE2("[%s] data received: %d bytes.\n",
+ __func__, dev->pktf.cfpkt_getlen(pkt));
+
+ /** NOTE: This function may be called in Tasklet context! */
+
+
+
+#if COUNTER_DEBUG
+ dev->pktf.cfpkt_raw_extract(pkt, (void **) &data_p, 0);
+
+ if (data_p[0] == 1) {
+ dev->counter = data_p[0];
+ dev->mismatch_reported = 0;
+ }
+
+ if ((dev->counter != data_p[0]) && !dev->mismatch_reported) {
+ CAIFLOG_TRACE
+ ("WARNING: caif_chrrecv_cb() - " "sequence: expected "
+ "%ld, got %ld\n", dev->counter, data_p[0]);
+ dev->mismatch_reported = 1;
+ }
+
+ if (!(dev->counter % 100000))
+ CAIFLOG_TRACE("caif_chrrecv_cb(): %ld\n", dev->counter);
+
+
+ dev->counter++;
+#endif
+
+ /* The queue has its own lock */
+ dev->pktf.cfpkt_queue(dev->pktq, pkt, 0);
+ spin_lock(&dev->read_queue_len_lock);
+ dev->read_queue_len++;
+
+ if (!test_bit(STATE_RX_FLOW_OFF, (void *) &dev->status) &&
+ (dev->read_queue_len > CHNL_CHR_READ_QUEUE_HIGH)) {
+ set_bit(STATE_RX_FLOW_OFF, (void *) &dev->status);
+
+ /* Todo: send flow off (NOTE: must not sleep) */
+ CAIFLOG_TRACE2
+ ("caif_chrrecv_cb(): sending flow OFF (queue len = %d)\n",
+ dev->read_queue_len);
+ chnl_assert(dev->layer.dn);
+ chnl_assert(dev->layer.dn->ctrlcmd);
+ (void) dev->layer.dn->modemcmd(dev->layer.dn,
+ CAIF_MODEMCMD_FLOW_OFF_REQ);
+ }
+ spin_unlock(&dev->read_queue_len_lock);
+
+ /* Signal reader that data is available. */
+ wake_up_interruptible(&dev->read_wq);
+
+ return 0;
+}
+
+/** Packet Flow Control Callback function called from CAIF */
+static void caif_chrflowctrl_cb(layer_t *layr, caif_ctrlcmd_t flow, int phyid)
+{
+ struct caif_char_dev *dev;
+
+ CAIFLOG_ENTER("");
+
+ /** NOTE: This function may be called in Tasklet context! */
+ CAIFLOG_TRACE("AT flowctrl func called flow: %s.\n",
+ flow == CAIF_CTRLCMD_FLOW_ON_IND ? "ON" :
+ flow == CAIF_CTRLCMD_FLOW_OFF_IND ? "OFF" :
+ flow == CAIF_CTRLCMD_INIT_RSP ? "INIT_RSP" :
+ flow == CAIF_CTRLCMD_DEINIT_RSP ? "DEINIT_RSP" :
+ flow == CAIF_CTRLCMD_INIT_FAIL_RSP ? "INIT_FAIL_RSP" :
+ flow ==
+ CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND ? "REMOTE_SHUTDOWN" :
+ "UKNOWN CTRL COMMAND");
+
+ dev = container_of(layr, struct caif_char_dev, layer);
+
+ switch (flow) {
+ case CAIF_CTRLCMD_FLOW_ON_IND:
+ CAIFLOG_TRACE("[%s:%d] CAIF_CTRLCMD_FLOW_ON_IND\n",
+ __func__, __LINE__);
+ /* Signal reader that data is available. */
+ set_bit(STATE_TX_FLOW_ON, (void *) &dev->status);
+ wake_up_interruptible(&dev->mgmt_wq);
+ break;
+
+ case CAIF_CTRLCMD_FLOW_OFF_IND:
+ CAIFLOG_TRACE("[%s:%d] CAIF_CTRLCMD_FLOW_OFF_IND\n",
+ __func__, __LINE__);
+ clear_bit(STATE_TX_FLOW_ON, (void *) &dev->status);
+ break;
+
+ case CAIF_CTRLCMD_INIT_RSP:
+ CAIFLOG_TRACE("[%s:%d] CAIF_CTRLCMD_INIT_RSP\n",
+ __func__, __LINE__);
+ /* Signal reader that data is available. */
+ set_bit(STATE_TX_FLOW_ON, (void *) &dev->status);
+ set_bit(STATE_IS_OPEN_BIT, (void *) &dev->status);
+ clear_bit(STATE_OPEN_PENDING_BIT, (void *) &dev->status);
+ wake_up_interruptible(&dev->mgmt_wq);
+ break;
+
+ case CAIF_CTRLCMD_DEINIT_RSP:
+ CAIFLOG_TRACE("[%s:%d] CAIF_CTRLCMD_DEINIT_RSP\n",
+ __func__, __LINE__);
+ clear_bit(STATE_TX_FLOW_ON, (void *) &dev->status);
+ set_bit(STATE_IS_CLOSED_BIT, (void *) &dev->status);
+ clear_bit(STATE_CLOSE_PENDING_BIT, (void *) &dev->status);
+ wake_up_interruptible(&dev->mgmt_wq);
+ break;
+
+ case CAIF_CTRLCMD_INIT_FAIL_RSP:
+ CAIFLOG_TRACE("[%s:%d] CAIF_CTRLCMD_INIT_FAIL_RSP\n",
+ __func__, __LINE__);
+ clear_bit(STATE_TX_FLOW_ON, (void *) &dev->status);
+ set_bit(STATE_IS_CLOSED_BIT, (void *) &dev->status);
+ clear_bit(STATE_OPEN_PENDING_BIT, (void *) &dev->status);
+ wake_up_interruptible(&dev->mgmt_wq);
+ break;
+
+ case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND:
+ CAIFLOG_TRACE("[%s:%d] CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND\n",
+ __func__, __LINE__);
+ clear_bit(STATE_TX_FLOW_ON, (void *) &dev->status);
+ set_bit(STATE_IS_CLOSED_BIT, (void *) &dev->status);
+ clear_bit(STATE_CLOSE_PENDING_BIT, (void *) &dev->status);
+ clear_bit(STATE_OPEN_PENDING_BIT, (void *) &dev->status);
+ wake_up_interruptible(&dev->mgmt_wq);
+ break;
+
+ default:
+ CAIFLOG_TRACE("[%s:%d] Unexpected flow command %d\n",
+ __func__, __LINE__, flow);
+
+ }
+}
+
+/** Device Read function called from Linux Kernel */
+ssize_t caif_chrread(struct file *filp, char __user *buf, size_t count,
+ loff_t *f_pos)
+{
+ cfpkt_t *pkt = NULL;
+ unsigned char *rxbuf = NULL;
+ size_t len;
+ int result;
+ struct caif_char_dev *dev = filp->private_data;
+ ssize_t ret = -EIO;
+
+ CAIFLOG_ENTER("");
+
+ if (dev == NULL) {
+ CAIFLOG_TRACE("[%s:%d] private_data not set!\n",
+ __func__, __LINE__);
+ return -EBADFD;
+ }
+
+
+ /* I want to be alone on dev (except status and queue) */
+ if (mutex_lock_interruptible(&dev->mutex)) {
+ CAIFLOG_TRACE
+ ("caif_chrread: mutex_lock_interruptible got signalled\n");
+ return -ERESTARTSYS;
+ }
+
+ chnl_assert(dev->pktq);
+
+ if (!test_bit(STATE_IS_OPEN_BIT, (void *) &dev->status)) {
+ CAIFLOG_TRACE("[%s:%d] device not open\n", __func__, __LINE__);
+ ret = -EINVAL;
+ goto read_error;
+ }
+
+ if (test_bit(STATE_IS_CLOSED_BIT, (void *) &dev->status)) {
+ CAIFLOG_TRACE("[%s:%d] remote end shutdown!\n",
+ __func__, __LINE__);
+ ret = -EPIPE;
+ goto read_error;
+ }
+
+ if (test_bit(STATE_CLOSE_PENDING_BIT, (void *) &dev->status)) {
+ CAIFLOG_TRACE("[%s:%d] device is closing...\n",
+ __func__, __LINE__);
+ ret = -EPIPE;
+ goto read_error;
+ }
+
+
+ /* Block if we don't have any received buffers. */
+ /* The queue has its own lock */
+ while ((pkt = dev->pktf.cfpkt_qpeek(dev->pktq)) == NULL) {
+
+ if (filp->f_flags & O_NONBLOCK) {
+ CAIFLOG_TRACE("caif_chrread: O_NONBLOCK\n");
+ ret = -EAGAIN;
+ goto read_error;
+ }
+ CAIFLOG_TRACE2("[%s:%d] wait_event\n", __func__, __LINE__);
+
+ /* Let writers in */
+ mutex_unlock(&dev->mutex);
+
+ /* Block reader until data arrives. */
+ if (wait_event_interruptible(dev->read_wq,
+ dev->pktf.cfpkt_qpeek(dev->pktq)
+ || test_bit(STATE_IS_CLOSED_BIT,
+ (void *)
+ &dev->status)) ==
+ -ERESTARTSYS) {
+ CAIFLOG_TRACE
+ ("caif_chrread: wait_event_interruptible woken by "
+ "a signal, signal_pending(current) = %d\n",
+ signal_pending(current));
+ return -ERESTARTSYS;
+ }
+
+ CAIFLOG_TRACE2("[%s:%d] awake\n", __func__, __LINE__);
+
+ /* I want to be alone on dev (except status and queue) */
+ if (mutex_lock_interruptible(&dev->mutex)) {
+ CAIFLOG_TRACE
+ ("caif_chrread: "
+ "mutex_lock_interruptible got signalled\n");
+ return -ERESTARTSYS;
+ }
+
+ if (test_bit(STATE_IS_CLOSED_BIT, (void *) &dev->status)) {
+ /* someone closed the link, report error */
+ CAIFLOG_TRACE("[%s:%d] remote end shutdown!\n",
+ __func__, __LINE__);
+ ret = -EPIPE;
+ goto read_error;
+ }
+
+ }
+
+ /* The queue has its own lock */
+ len = dev->pktf.cfpkt_getlen(pkt);
+
+ /* Check max length that can be copied. */
+ if (len > count) {
+ CAIFLOG_TRACE("caif_chrread: user buffer too small\n");
+ ret = -EINVAL;
+ goto read_error;
+ }
+
+ /* Get packet from queue */
+ /* The queue has its own lock */
+ pkt = dev->pktf.cfpkt_dequeue(dev->pktq);
+
+ spin_lock(&dev->read_queue_len_lock);
+ dev->read_queue_len--;
+ if (test_bit(STATE_RX_FLOW_OFF, (void *) &dev->status) &&
+ (dev->read_queue_len < CHNL_CHR_READ_QUEUE_LOW)) {
+
+ clear_bit(STATE_RX_FLOW_OFF, (void *) &dev->status);
+
+ /* Todo: send flow on */
+ CAIFLOG_TRACE2
+ ("caif_chrread(): sending flow ON (queue len = %d)\n",
+ dev->read_queue_len);
+ chnl_assert(dev->layer.dn);
+ chnl_assert(dev->layer.dn->ctrlcmd);
+ (void) dev->layer.dn->modemcmd(dev->layer.dn,
+ CAIF_MODEMCMD_FLOW_ON_REQ);
+
+ chnl_assert(dev->read_queue_len >= 0);
+
+ }
+ spin_unlock(&dev->read_queue_len_lock);
+
+ result = dev->pktf.cfpkt_raw_extract(pkt, (void **) &rxbuf, len);
+
+ chnl_assert(result >= 0);
+
+ if (result < 0) {
+ CAIFLOG_TRACE("caif_chrread: cfpkt_raw_extract failed\n");
+ dev->pktf.cfpkt_destroy(pkt);
+ ret = -EINVAL;
+ goto read_error;
+ }
+
+ /* Copy data from the RX buffer to the user buffer. */
+ if (copy_to_user(buf, rxbuf, len)) {
+ CAIFLOG_TRACE("caif_chrread: copy_to_user returned non zero.");
+ dev->pktf.cfpkt_destroy(pkt);
+ ret = -EINVAL;
+ goto read_error;
+ }
+
+
+ /* Liberate packet. */
+ dev->pktf.cfpkt_destroy(pkt);
+
+
+ /* Let the others in */
+ mutex_unlock(&dev->mutex);
+ CAIFLOG_EXIT("");
+ return len;
+
+read_error:
+ mutex_unlock(&dev->mutex);
+ CAIFLOG_EXIT("");
+ return ret;
+}
+
+/** Device write function called from Linux Kernel (misc device) */
+ssize_t caif_chrwrite(struct file *filp, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ cfpkt_t *pkt = NULL;
+ struct caif_char_dev *dev = filp->private_data;
+ transmt_info info;
+ unsigned char *txbuf;
+ ssize_t ret = -EIO;
+ CAIFLOG_ENTER("");
+
+ if (dev == NULL) {
+ CAIFLOG_TRACE("[%s:%d] private_data not set!\n",
+ __func__, __LINE__);
+ ret = -EBADFD;
+ goto write_error_no_unlock;
+ }
+
+
+ if (count > CAIF_MAX_PAYLOAD_SIZE) {
+ CAIFLOG_TRACE("[%s:%d] buffer too long\n", __func__, __LINE__);
+ ret = -EINVAL;
+ goto write_error_no_unlock;
+ }
+
+ /* I want to be alone on dev (except status and queue) */
+ if (mutex_lock_interruptible(&dev->mutex)) {
+ CAIFLOG_TRACE
+ ("caif_chrwrite: mutex_lock_interruptible got signalled");
+ ret = -ERESTARTSYS;
+ goto write_error_no_unlock;
+ }
+
+ chnl_assert(dev->pktq);
+
+ if (!test_bit(STATE_IS_OPEN_BIT, (void *) &dev->status)) {
+ CAIFLOG_TRACE("[%s:%d] device not open", __func__, __LINE__);
+ ret = -EINVAL;
+ goto write_error;
+ }
+
+ if (test_bit(STATE_IS_CLOSED_BIT, (void *) &dev->status)) {
+ CAIFLOG_TRACE("[%s:%d] remote end shutdown!",
+ __func__, __LINE__);
+ ret = -EPIPE;
+ goto write_error;
+ }
+
+ if (test_bit(STATE_CLOSE_PENDING_BIT, (void *) &dev->status)) {
+ CAIFLOG_TRACE("[%s:%d] device is closing...",
+ __func__, __LINE__);
+ ret = -EPIPE;
+ goto write_error;
+ }
+
+ if (!test_bit(STATE_TX_FLOW_ON, (void *) &dev->status) &&
+ (filp->f_flags & O_NONBLOCK)) {
+ CAIFLOG_TRACE("caif_chrwrite: O_NONBLOCK");
+ ret = -EAGAIN;
+ goto write_error;
+ }
+
+ /* Let readers in */
+ mutex_unlock(&dev->mutex);
+
+ if (wait_event_interruptible(dev->mgmt_wq,
+ test_bit(STATE_TX_FLOW_ON,
+ (void *) &dev->status)
+ || test_bit(STATE_IS_CLOSED_BIT,
+ (void *) &dev->status)) ==
+ -ERESTARTSYS) {
+ CAIFLOG_TRACE
+ ("caif_chrwrite:"
+ " wait_event_interruptible woken by a signal");
+ ret = -ERESTARTSYS;
+ goto write_error_no_unlock;
+ }
+
+ /* I want to be alone on dev (except status and queue) */
+ if (mutex_lock_interruptible(&dev->mutex)) {
+ CAIFLOG_TRACE
+ ("caif_chrwrite: mutex_lock_interruptible got signalled\n");
+ ret = -ERESTARTSYS;
+ goto write_error_no_unlock;
+ }
+
+ if (test_bit(STATE_IS_CLOSED_BIT, (void *) &dev->status)) {
+ /* someone closed the link, report error */
+ CAIFLOG_TRACE("[%s:%d] remote end shutdown!\n",
+ __func__, __LINE__);
+ ret = -EPIPE;
+ goto write_error;
+ }
+
+ /* Create packet, buf=NULL means no copying */
+ pkt = dev->pktf.cfpkt_create_xmit_pkt((const unsigned char *) NULL,
+ count);
+
+ if (pkt == NULL) {
+ CAIFLOG_TRACE
+ ("caif_chrwrite: cfpkt_create_pkt returned NULL\n");
+ ret = -EIO;
+ goto write_error;
+ }
+
+ if (!dev->pktf.cfpkt_raw_append(pkt, (void **) &txbuf, count)) {
+ CAIFLOG_TRACE("caif_chrwrite: cfpkt_raw_append failed\n");
+ dev->pktf.cfpkt_destroy(pkt);
+ ret = -EINVAL;
+ goto write_error;
+ }
+
+ /* Copy data into buffer. */
+
+ if (copy_from_user(txbuf, buf, count)) {
+ CAIFLOG_TRACE
+ ("caif_chrwrite: copy_from_user returned non zero.\n");
+ dev->pktf.cfpkt_destroy(pkt);
+ ret = -EINVAL;
+ goto write_error;
+ }
+
+ memset(&info, 0, sizeof(info));
+
+ /* Send the packet down the stack. */
+ chnl_assert(dev->layer.dn);
+ chnl_assert(dev->layer.dn->transmit);
+
+ ret = dev->layer.dn->transmit(dev->layer.dn, &info, pkt);
+ if (ret < 0) {
+ dev->pktf.cfpkt_destroy(pkt);
+ CAIFLOG_TRACE("caif_chrwrite: transmit failed, error = %d\n",
+ ret);
+ goto write_error;
+ }
+
+
+ mutex_unlock(&dev->mutex);
+ CAIFLOG_EXIT("");
+ return count;
+
+write_error:
+ mutex_unlock(&dev->mutex);
+write_error_no_unlock:
+ CAIFLOG_EXIT("");
+ return ret;
+}
+
+
+static unsigned int caif_chrpoll(struct file *filp, poll_table *waittab)
+{
+ struct caif_char_dev *dev = filp->private_data;
+ unsigned int mask = 0;
+
+ CAIFLOG_ENTER("");
+
+ if (dev == NULL) {
+ CAIFLOG_TRACE("[%s:%d] private_data not set!\n",
+ __func__, __LINE__);
+ return -EBADFD;
+ }
+
+
+ /* I want to be alone on dev (except status and queue) */
+ if (mutex_lock_interruptible(&dev->mutex)) {
+ CAIFLOG_TRACE
+ ("caif_chrpoll: mutex_lock_interruptible got signalled\n");
+ goto poll_error;
+ }
+
+ chnl_assert(dev->pktq);
+
+ if (!test_bit(STATE_IS_OPEN_BIT, (void *) &dev->status)) {
+ CAIFLOG_TRACE("[%s:%d] device not open\n", __func__, __LINE__);
+ goto poll_error;
+ }
+
+ if (test_bit(STATE_IS_CLOSED_BIT, (void *) &dev->status)) {
+ CAIFLOG_TRACE("[%s:%d] remote end shutdown!\n",
+ __func__, __LINE__);
+ goto poll_error;
+ }
+
+ if (test_bit(STATE_CLOSE_PENDING_BIT, (void *) &dev->status)) {
+ CAIFLOG_TRACE("[%s:%d] device is closing...\n",
+ __func__, __LINE__);
+ goto poll_error;
+ }
+
+ poll_wait(filp, &dev->read_wq, waittab);
+
+ if (dev->pktf.cfpkt_qpeek(dev->pktq) != NULL)
+ mask |= (POLLIN | POLLRDNORM);
+
+
+ if (test_bit(STATE_TX_FLOW_ON, (void *) &dev->status))
+ mask |= (POLLOUT | POLLWRNORM);
+
+
+ mutex_unlock(&dev->mutex);
+ CAIFLOG_TRACE("[%s:%d] caif_chrpoll mask=0x%04x...\n",
+ __func__, __LINE__, mask);
+
+ CAIFLOG_EXIT("");
+ return mask;
+
+poll_error:
+ mask |= POLLERR;
+ mutex_unlock(&dev->mutex);
+ CAIFLOG_EXIT("");
+ return mask;
+}
+
+/* Usage:
+ minor >= 0 : find from minor
+ minor < 0 and name == name : find from name
+ minor < 0 and name == NULL : get first
+*/
+
+static struct caif_char_dev *find_device(int minor, char *name,
+ int remove_from_list)
+{
+ struct list_head *list_node;
+ struct list_head *n;
+ struct caif_char_dev *dev = NULL;
+ struct caif_char_dev *tmp;
+ CAIFLOG_ENTER("");
+ spin_lock(&list_lock);
+ CAIFLOG_TRACE("[%s:%d] start looping \n", __func__, __LINE__);
+ list_for_each_safe(list_node, n, &caif_chrdev_list) {
+ tmp = list_entry(list_node, struct caif_char_dev, list_field);
+ CAIFLOG_TRACE("[%s:%d] check %d,%d, %s, %s \n",
+ __func__, __LINE__, tmp->misc.minor, minor,
+ tmp->name, name);
+ if (minor >= 0) { /* find from minor */
+ if (tmp->misc.minor == minor)
+ dev = tmp;
+
+ } else if (name) { /* find from name */
+ if (!strncmp(tmp->name, name, sizeof(tmp->name)))
+ dev = tmp;
+ } else { /* take first */
+ dev = tmp;
+ }
+
+ if (dev) {
+ CAIFLOG_TRACE("[%s:%d] match %d, %s \n",
+ __func__, __LINE__, minor, name);
+ if (remove_from_list)
+ list_del(list_node);
+ break;
+ }
+ }
+ spin_unlock(&list_lock);
+ return dev;
+}
+
+#if CAIFLOG_ON
+static void print_device_list(void)
+{
+ int i = 0;
+ struct list_head *list_node;
+ struct caif_char_dev *tmp;
+ CAIFLOG_ENTER("");
+ spin_lock(&list_lock);
+ CAIFLOG_TRACE("List of devices:\n");
+ list_for_each(list_node, &caif_chrdev_list) {
+ tmp = list_entry(list_node, struct caif_char_dev, list_field);
+ CAIFLOG_TRACE("i = %d, minor= %d, name = %s, open = %d\n", i,
+ tmp->misc.minor, tmp->name,
+ test_bit(STATE_IS_OPEN_BIT,
+ (void *) &tmp->status) ? 1 : 0);
+ i++;
+ }
+ if (i == 0)
+ CAIFLOG_TRACE("list is empty\n");
+
+ spin_unlock(&list_lock);
+}
+#endif
+
+
+static void drain_queue(struct caif_char_dev *dev)
+{
+ cfpkt_t *pkt;
+
+ /* Empty the queue */
+ do {
+ /* The queue has its own lock */
+ pkt = dev->pktf.cfpkt_dequeue(dev->pktq);
+
+ if (!pkt)
+ break;
+
+ CAIFLOG_TRACE
+ ("[%s:%d] drain_queue(): freeing packet from read queue\n",
+ __func__, __LINE__);
+ dev->pktf.cfpkt_destroy(pkt);
+
+ } while (1);
+}
+
+
+int caif_chropen(struct inode *inode, struct file *filp)
+{
+ struct caif_char_dev *dev = NULL;
+ int result = -1;
+ int minor = iminor(inode);
+ int mode = 0;
+ int ret = -EIO;
+
+ CAIFLOG_ENTER("");
+
+ IF_CAIF_TRACE(print_device_list());
+
+ dev = find_device(minor, NULL, 0);
+
+ if (dev == NULL) {
+ CAIFLOG_TRACE("[%s] COULD NOT FIND DEVICE\n", __func__);
+ return -EBADF;
+ }
+
+ CAIFLOG_TRACE("[%s:%d] dev=%p \n", __func__, __LINE__, dev);
+
+ /* I want to be alone on dev (except status and queue) */
+ if (mutex_lock_interruptible(&dev->mutex)) {
+ CAIFLOG_TRACE
+ ("caif_chropen: mutex_lock_interruptible got signalled\n");
+ return -ERESTARTSYS;
+ }
+
+ filp->private_data = dev;
+
+ switch (filp->f_flags & O_ACCMODE) {
+ case O_RDONLY:
+ mode = CHR_READ_FLAG;
+ break;
+ case O_WRONLY:
+ mode = CHR_WRITE_FLAG;
+ break;
+ case O_RDWR:
+ mode = CHR_READ_FLAG | CHR_WRITE_FLAG;
+ break;
+ }
+
+ /* If close is pending we need to wait for its conclusion */
+ result = wait_event_interruptible_timeout(dev->mgmt_wq,
+ !test_bit
+ (STATE_CLOSE_PENDING_BIT,
+ (void *) &dev->status),
+ CAIF_CONNECT_TIMEOUT * HZ);
+ if (result == -ERESTARTSYS) {
+ CAIFLOG_TRACE
+ ("caif_chropen:"
+ " wait_event_interruptible woken by a signal (1)");
+ ret = -ERESTARTSYS;
+ goto open_error;
+ } else if (result == 0) {
+ /* Timed out */
+ /* Timeout */
+ CAIFLOG_TRACE("caif_chropen: close pending timed out\n");
+ ret = -ETIMEDOUT;
+ goto open_error;
+ }
+
+ if (!test_bit(STATE_IS_OPEN_BIT, (void *) &dev->status)) {
+
+ /* Test if we have already registered the channel,
+ * we could have been interrupted by a signal last time...
+ */
+ if (!test_bit(STATE_OPEN_PENDING_BIT, (void *) &dev->status)) {
+
+ /* First opening of file; connect lower layers: */
+
+ dev->layer.receive = caif_chrrecv_cb;
+
+ clear_bit(STATE_IS_CLOSED_BIT, (void *) &dev->status);
+ clear_bit(STATE_TX_FLOW_ON, (void *) &dev->status);
+ set_bit(STATE_OPEN_PENDING_BIT, (void *) &dev->status);
+
+ /* Register this channel. */
+ result =
+ caifdev_adapt_register(&dev->config, &dev->layer);
+ if (result < 0) {
+ CAIFLOG_TRACE
+ ("caif_chropen: can't register channel\n");
+ ret = -EIO;
+ goto open_error;
+ }
+ }
+
+ CAIFLOG_TRACE("[%s:%d] WAIT FOR CONNECT RESPONSE \n", __func__,
+ __LINE__);
+
+ result = wait_event_interruptible_timeout(dev->mgmt_wq,
+ test_bit
+ (STATE_IS_OPEN_BIT,
+ (void *)
+ &dev->status)
+ ||
+ test_bit
+ (STATE_IS_CLOSED_BIT,
+ (void *)
+ &dev->status),
+ CAIF_CONNECT_TIMEOUT
+ * HZ);
+ if (result == -ERESTARTSYS) {
+ CAIFLOG_TRACE
+ ("caif_chropen: "
+ "wait_event_interruptible woken by a signal (2)");
+ ret = -ERESTARTSYS;
+ goto open_error;
+ } else if (result == 0) {
+ /* Timed out */
+ /* Timeout */
+ CAIFLOG_TRACE("caif_open: connect timed out\n");
+ ret = -ETIMEDOUT;
+ goto open_error;
+ }
+
+ if (test_bit(STATE_IS_CLOSED_BIT, (void *) &dev->status)
+ ) {
+ /* Lower layers said "no" */
+ CAIFLOG_TRACE
+ ("[%s:%d] caif_chropen: CLOSED RECEIVED\n",
+ __func__, __LINE__);
+ ret = -EPIPE;
+ goto open_error;
+ }
+
+ CAIFLOG_TRACE("[%s:%d] caif_chropen: CONNECT RECEIVED\n",
+ __func__, __LINE__);
+ } else {
+ /* Already open */
+ CAIFLOG_TRACE
+ ("[%s:%d] Device is already opened (dev=%p) check access "
+ "f_flags = 0x%x file_mode = 0x%x\n",
+ __func__, __LINE__, dev, mode, dev->file_mode);
+
+ if (mode & dev->file_mode) {
+ CAIFLOG_TRACE
+ ("[%s:%d] Access mode already in use 0x%x \n",
+ __func__, __LINE__, mode);
+ ret = -EBUSY;
+ goto open_error;
+ }
+ }
+
+ /* Open is ok */
+ dev->file_mode |= mode;
+
+ CAIFLOG_TRACE("[%s:%d] Open - file mode = %x\n",
+ __func__, __LINE__, dev->file_mode);
+
+ CAIFLOG_TRACE("[%s:%d] CONNECTED \n", __func__, __LINE__);
+
+ mutex_unlock(&dev->mutex);
+ CAIFLOG_EXIT("");
+ return 0;
+
+open_error:
+ mutex_unlock(&dev->mutex);
+ CAIFLOG_EXIT("");
+ return ret;
+}
+
+int caif_chrrelease(struct inode *inode, struct file *filp)
+{
+ struct caif_char_dev *dev = NULL;
+ int minor = iminor(inode);
+ int result;
+ int mode = 0;
+ CAIFLOG_ENTER("");
+
+ IF_CAIF_TRACE(print_device_list());
+ dev = find_device(minor, NULL, 0);
+ if (dev == NULL) {
+ CAIFLOG_TRACE("[%s] COULD NOT FIND DEVICE\n", __func__);
+ return -EBADF;
+ }
+
+ /* I want to be alone on dev (except status queue) */
+ if (mutex_lock_interruptible(&dev->mutex)) {
+ CAIFLOG_TRACE
+ ("caif_chrrelease: "
+ "mutex_lock_interruptible got signalled\n");
+ return -ERESTARTSYS;
+ }
+
+ /* Don't need to test for CLOSE _PENDING because if set IS_OPEN is
+ always cleared
+ */
+ if (!test_bit(STATE_IS_OPEN_BIT, (void *) &dev->status)) {
+ CAIFLOG_TRACE("[%s:%d] Device not open (dev=%p) \n",
+ __func__, __LINE__, dev);
+ mutex_unlock(&dev->mutex);
+ return 0;
+ }
+
+ switch (filp->f_flags & O_ACCMODE) {
+ case O_RDONLY:
+ mode = CHR_READ_FLAG;
+ break;
+ case O_WRONLY:
+ mode = CHR_WRITE_FLAG;
+ break;
+ case O_RDWR:
+ mode = CHR_READ_FLAG | CHR_WRITE_FLAG;
+ break;
+ }
+
+
+ dev->file_mode &= ~mode;
+ if (dev->file_mode) {
+ CAIFLOG_TRACE
+ ("[%s:%d] Don't yet close CAIF connection - file_mode "
+ "= %x\n", __func__, __LINE__, dev->file_mode);
+ mutex_unlock(&dev->mutex);
+ return 0;
+ }
+
+ /* IS_CLOSED have double meaning:
+ * 1) Spontanous Remote Shutdown Request.
+ * 2) Ack on a channel teardown(disconnect)
+ * Must clear bit in case we previously received
+ * remote shudown request.
+ */
+
+ clear_bit(STATE_IS_CLOSED_BIT, (void *) &dev->status);
+ set_bit(STATE_CLOSE_PENDING_BIT, (void *) &dev->status);
+
+ result = caifdev_adapt_unregister(&dev->layer);
+
+ if (result < 0) {
+ /* Timeout */
+ CAIFLOG_TRACE
+ ("caif_chrrelease: caifdev_adapt_unregister() failed\n");
+ mutex_unlock(&dev->mutex);
+ return -EIO;
+ }
+
+ /* wait for flow control callback with flow == shutdown */
+ result = wait_event_interruptible_timeout(dev->mgmt_wq,
+ test_bit(STATE_IS_CLOSED_BIT,
+ (void *)
+ &dev->status),
+ CAIF_CONNECT_TIMEOUT * HZ);
+ if (result == -ERESTARTSYS) {
+ CAIFLOG_TRACE
+ ("caif_chrrelease: "
+ "wait_event_interruptible woken by signal,"
+ " signal_pending(current) = %d\n",
+ signal_pending(current));
+ } else if (result == 0) {
+ /* Timeout */
+ CAIFLOG_TRACE("caif_chrrelease: disconnect timed out\n");
+ mutex_unlock(&dev->mutex);
+ return -ETIMEDOUT;
+ } else {
+ CAIFLOG_TRACE("[%s:%d] caif_chrrelease: DISCONNECT RECEIVED\n",
+ __func__, __LINE__);
+ }
+
+ /* Empty the queue */
+ drain_queue(dev);
+ dev->file_mode = 0;
+ clear_bit(STATE_IS_OPEN_BIT, (void *) &dev->status);
+ clear_bit(STATE_TX_FLOW_ON, (void *) &dev->status);
+ IF_CAIF_TRACE(print_device_list());
+
+ mutex_unlock(&dev->mutex);
+ return 0;
+}
+
+const struct file_operations caif_chrfops = {
+ .owner = THIS_MODULE,
+ .read = caif_chrread,
+ .write = caif_chrwrite,
+ .open = caif_chropen,
+ .release = caif_chrrelease,
+ .poll = caif_chrpoll,
+};
+
+int chrdev_create(struct caif_channel_create_action *action)
+{
+
+ struct caif_char_dev *dev = NULL;
+ int result;
+ CAIFLOG_ENTER("");
+
+ IF_CAIF_TRACE(print_device_list());
+
+ /* Allocate device */
+ dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+
+ if (!dev) {
+ CAIFLOG_TRACE(KERN_ERR "chnl_chr: kmalloc failed.\n");
+ return -ENOMEM;
+ }
+
+ CAIFLOG_TRACE("[%s:%d] dev=%p \n", __func__, __LINE__, dev);
+ memset(dev, 0, sizeof(*dev));
+
+ mutex_init(&dev->mutex);
+ init_waitqueue_head(&dev->read_wq);
+ init_waitqueue_head(&dev->mgmt_wq);
+ spin_lock_init(&dev->read_queue_len_lock);
+
+ /* Fill in some information concerning the misc device. */
+ dev->misc.minor = MISC_DYNAMIC_MINOR;
+ strncpy(dev->name, action->name.name, sizeof(dev->name));
+ dev->misc.name = dev->name;
+ dev->misc.fops = &caif_chrfops;
+
+ /* Register the device */
+ result = misc_register(&dev->misc);
+
+ /* Lock in order to try to stop someone from opening the device
+ too early. The misc device has its own lock. We cannot take our
+ lock until misc_register() is finished, because in open() the
+ locks are taken in this order (misc first and then dev).
+ So anyone managing to open the device between the misc_register
+ and the mutex_lock will get a "device not found" error. Don't
+ think it can be avoided.
+ */
+ mutex_lock_interruptible(&dev->mutex);
+
+ if (result < 0) {
+ CAIFLOG_ERROR("chnl_chr: error - %d, can't register misc.\n",
+ result);
+ mutex_unlock(&dev->mutex);
+ goto err_failed;
+ }
+
+
+ dev->pktf = cfcnfg_get_packet_funcs();
+
+ dev->pktq = dev->pktf.cfpktq_create();
+ if (!dev->pktq) {
+ CAIFLOG_ERROR("chnl_chr: queue create failed.\n");
+ result = -ENOMEM;
+ mutex_unlock(&dev->mutex);
+ misc_deregister(&dev->misc);
+ goto err_failed;
+ }
+
+ CAIFLOG_TRACE("[%s:%d] pktf=%p\n", __func__, __LINE__, &dev->pktf);
+ CAIFLOG_TRACE("[%s:%d] cfpkt_create_xmit=%p\n", __func__,
+ __LINE__, dev->pktf.cfpkt_create_xmit_pkt);
+
+ strncpy(action->name.name, dev->misc.name, sizeof(action->name.name));
+ action->major = MISC_MAJOR;
+ action->minor = dev->misc.minor;
+
+ dev->config = action->config;
+ CAIFLOG_TRACE("dev: Registered dev with name=%s minor=%d, dev=%p\n",
+ dev->misc.name, dev->misc.minor, dev->misc.this_device);
+
+ dev->layer.ctrlcmd = caif_chrflowctrl_cb;
+
+ /* Add the device */
+ spin_lock(&list_lock);
+ list_add(&dev->list_field, &caif_chrdev_list);
+ spin_unlock(&list_lock);
+
+ IF_CAIF_TRACE(print_device_list());
+
+ CAIFLOG_EXIT("");
+ mutex_unlock(&dev->mutex);
+ return 0;
+err_failed:
+ action->name.name[0] = '\0';
+ action->major = -1;
+ action->minor = -1;
+ kfree(dev);
+ CAIFLOG_EXIT("");
+ return result;
+}
+
+
+int chrdev_remove(char *name)
+{
+ int ret = 0;
+
+ struct caif_char_dev *dev = NULL;
+ CAIFLOG_ENTER("");
+
+ IF_CAIF_TRACE(print_device_list());
+
+ /* Find device from name */
+ dev = find_device(-1, name, 0);
+ if (!dev)
+ return -EBADF;
+
+
+ mutex_lock_interruptible(&dev->mutex);
+
+ if (test_bit(STATE_IS_OPEN_BIT, (void *) &dev->status)) {
+ CAIFLOG_TRACE
+ ("[%s:%d] Device is opened (dev=%p) file_mode = 0x%x\n",
+ __func__, __LINE__, dev, dev->file_mode);
+ mutex_unlock(&dev->mutex);
+ return -EBUSY;
+ }
+
+ /* Remove from list */
+ (void) find_device(-1, name, 1);
+
+ drain_queue(dev);
+ ret = misc_deregister(&dev->misc);
+
+ cfglu_free(dev->pktq);
+ mutex_unlock(&dev->mutex);
+ kfree(dev);
+
+ IF_CAIF_TRACE(print_device_list());
+
+ return ret;
+}
+
+
+int chrdev_mgmt(int action, union caif_action *param)
+{
+
+ switch (action) {
+ case CAIF_ACT_CREATE_DEVICE:
+ return chrdev_create(¶m->create_channel);
+ case CAIF_ACT_DELETE_DEVICE:
+ return chrdev_remove(param->delete_channel.name);
+ default:
+ return -EINVAL;
+ }
+}
+
+int caif_chrinit_module(void)
+{
+ CAIFLOG_ENTER("");
+ CAIFLOG_TRACE("\nCompiled:%s:%s\n", __DATE__, __TIME__);
+ spin_lock_init(&list_lock);
+ caif_register_chrdev(chrdev_mgmt);
+ return 0;
+}
+
+void caif_chrexit_module(void)
+{
+ int result;
+
+ CAIFLOG_ENTER("");
+ IF_CAIF_TRACE(print_device_list());
+
+ do {
+ /* Remove any device (the first in the list) */
+ result = chrdev_remove(NULL);
+ } while (result == 0);
+
+ caif_unregister_chrdev();
+
+ IF_CAIF_TRACE(print_device_list());
+ CAIFLOG_EXIT("");
+}
+
+module_init(caif_chrinit_module);
+module_exit(caif_chrexit_module);
diff --git a/net/caif/chnl_net.c b/net/caif/chnl_net.c
new file mode 100644
index 0000000..1d80f09
--- /dev/null
+++ b/net/caif/chnl_net.c
@@ -0,0 +1,464 @@
+/*
+* Copyright (C) ST-Ericsson AB 2009
+*
+* Author: Daniel Martensson / Daniel.Martensson@...ricsson.com
+*
+* License terms: GNU General Public License (GPL), version 2 or later.
+*
+*/
+
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+
+#include <linux/if_ether.h>
+#include <linux/moduleparam.h>
+#include <linux/ip.h>
+
+/* Caif header files. */
+#include "caif_layer.h"
+#include "cfcnfg.h"
+#include "cfpkt.h"
+
+#include "caif_chr.h"
+#include "caif_log.h"
+
+
+#define SIZE_MTU 1500
+#define SIZE_MTU_MAX 4080 /*FIXME: check this number*/
+#define SIZE_MTU_MIN 68 /*FIXME: check this number*/
+
+static LIST_HEAD(chnl_net_list);
+static spinlock_t list_lock;
+
+MODULE_LICENSE("GPL");
+
+static int loop;
+module_param(loop, bool, S_IRUGO);
+MODULE_PARM_DESC(loop,
+ "Loop enabled or not (looping will switch src "
+ "and dest of transmitted packages)");
+/*Flow status to remember and control the transmission*/
+static bool flowenabled;
+
+struct chnl_net {
+ layer_t chnl;
+ struct net_device_stats stats;
+ spinlock_t lock;
+ struct caif_channel_config config;
+ struct list_head list_field;
+ struct net_device *netdev;
+ char name[256];
+ bool deviceOpen;
+};
+
+struct net_device *netdevptr;
+
+static struct chnl_net *find_device(char *name, bool remove_from_list)
+{
+ struct list_head *list_node;
+ struct list_head *n;
+ struct chnl_net *dev = NULL;
+ struct chnl_net *tmp;
+ CAIFLOG_ENTER("");
+ spin_lock(&list_lock);
+ CAIFLOG_TRACE("[%s:%d] start looping \n", __func__,
+ __LINE__);
+ list_for_each_safe(list_node, n, &chnl_net_list) {
+ tmp =
+ list_entry(list_node, struct chnl_net,
+ list_field);
+ if (name) { /* find from name */
+ if (!strncmp(tmp->name, name, sizeof(tmp->name)))
+ dev = tmp;
+ else
+ /* Get the first element if name
+ * is not specified*/
+ dev = tmp;
+
+ if (dev) {
+ CAIFLOG_TRACE("[%s:%d] match %s \n",
+ __func__, __LINE__, name);
+ if (remove_from_list)
+ list_del(list_node);
+
+ break;
+ }
+ }
+ }
+ spin_unlock(&list_lock);
+ return dev;
+}
+
+
+
+static int chnl_recv_cb(layer_t *layr, cfpkt_t *pkt)
+{
+ struct sk_buff *skb;
+ caif_packet_funcs_t f;
+ struct chnl_net *priv = NULL;
+ int pktlen;
+
+#ifdef CAIF_USE_SKB
+#else
+ int actual_len;
+#endif
+ int err = 0;
+
+ priv = container_of(layr, struct chnl_net, chnl);
+
+ if (!priv) {
+ printk(KERN_INFO "chnl_recv_cb: netdev container not found\n");
+ return -EINVAL;
+ }
+
+ /* Get caif packet functions. */
+ f = cfcnfg_get_packet_funcs();
+
+ /* Get length of caif packet. */
+ pktlen = f.cfpkt_getlen(pkt);
+
+
+#ifdef CAIF_USE_SKB
+ /* TODO: Don't we make to much assumptions here ?
+ * Cast received packet to a native sk_buff. */
+ skb = (struct sk_buff *) f.cfpkt_tonative(pkt);
+#else
+ skb = dev_alloc_skb(pktlen);
+
+ if (!skb) {
+ printk(KERN_INFO "chnl_recv_cb: dev_alloc_skb failed.\n");
+ /* Update statistics. */
+ priv->netdev->stats.rx_dropped++;
+ err = -ENOMEM;
+ goto err_alloc_skb;
+ }
+
+ /* Extract data from the caif packet and copy it to the skb. */
+ f.cfpkt_extract(pkt, skb_put(skb, pktlen), pktlen, &actual_len);
+#endif
+ /* Pass some minimum information and send the
+ * packet to the net stack. */
+ skb->dev = priv->netdev;
+ skb->protocol = htons(ETH_P_IP);
+ /*FIXME: This is wrong checksum is needed!!*/
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ /*netif_rx(skb);*/
+ if (in_interrupt())
+ netif_rx(skb);
+ else
+ netif_rx_ni(skb);
+
+ /* Update statistics. */
+ priv->netdev->stats.rx_packets++;
+ priv->netdev->stats.rx_bytes += pktlen;
+
+#ifdef CAIF_USE_SKB
+#else
+
+err_alloc_skb:
+ f.cfpkt_destroy(pkt);
+#endif
+
+ return err;
+}
+
+static void chnl_flowctrl_cb(layer_t *layr, caif_ctrlcmd_t flow, int phyid)
+{
+ struct chnl_net *priv = NULL;
+ CAIFLOG_TRACE("NET flowctrl func called flow: %s.\n",
+ flow == CAIF_CTRLCMD_FLOW_ON_IND ? "ON" :
+ flow == CAIF_CTRLCMD_INIT_RSP ? "INIT" :
+ flow == CAIF_CTRLCMD_FLOW_OFF_IND ? "OFF" :
+ flow == CAIF_CTRLCMD_DEINIT_RSP ? "CLOSE/DEINIT" :
+ flow == CAIF_CTRLCMD_INIT_FAIL_RSP ? "OPEN_FAIL" :
+ flow == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND ?
+ "REMOTE_SHUTDOWN" : "UKNOWN CTRL COMMAND");
+
+ priv = container_of(layr, struct chnl_net, chnl);
+
+ switch (flow) {
+ case CAIF_CTRLCMD_FLOW_OFF_IND:
+ case CAIF_CTRLCMD_DEINIT_RSP:
+ case CAIF_CTRLCMD_INIT_FAIL_RSP:
+ case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND:
+ flowenabled = false;
+ netif_tx_disable(priv->netdev);
+ break;
+ case CAIF_CTRLCMD_FLOW_ON_IND:
+ case CAIF_CTRLCMD_INIT_RSP:
+ flowenabled = true;
+ netif_wake_queue(priv->netdev);
+ break;
+
+ default:
+ break;
+ }
+
+
+}
+
+int chnl_net_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct chnl_net *priv;
+ cfpkt_t *pkt = NULL;
+ int len;
+ caif_packet_funcs_t f;
+ int result = -1;
+
+ if (!flowenabled) {
+ #ifndef CAIF_USE_SKB
+ dev_kfree_skb(skb);
+ #endif
+ CAIFLOG_TRACE("Dropping packets Flow OFF\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ if (loop) {
+ struct iphdr *hdr;
+ __be32 swap;
+ /* Retrieve IP header. */
+ hdr = ip_hdr(skb);
+ /* Change source and destination address. */
+ swap = hdr->saddr;
+ hdr->saddr = hdr->daddr;
+ hdr->daddr = swap;
+ }
+
+ /* Store original skb length. */
+ len = skb->len;
+
+ /* Get caif packet functions. */
+ f = cfcnfg_get_packet_funcs();
+
+#ifdef CAIF_USE_SKB
+ /* TODO: Don't we make to much assumptions here ?
+ * Cast sk_buff to cfpck. */
+ pkt = f.cfpkt_fromnative(CAIF_DIR_OUT, (void *) skb);
+#else
+ /* Create a caif packet based on the skbuf. */
+ pkt = f.cfpkt_create_xmit_pkt(skb->data, skb->len);
+
+ if (!pkt) {
+ printk(KERN_INFO "chnl_write: cfpkt_create failed.\n");
+ goto err_cfpkt_create;
+ }
+#endif
+
+ /* Get our private data. */
+ priv = (struct chnl_net *)netdev_priv(dev);
+
+ if (!priv) {
+ printk(KERN_INFO "chnl_write: priv not found\n");
+ #ifndef CAIF_USE_SKB
+ goto err_cfpkt_create;
+ #else
+ return -ENOSPC;
+ #endif
+ }
+
+ /* Send the packet down the stack. */
+ result = priv->chnl.dn->transmit(priv->chnl.dn, NULL, pkt);
+ if (result) {
+ if (result == CFGLU_ERETRY)
+ result = NETDEV_TX_BUSY;
+
+ #ifndef CAIF_USE_SKB
+ f.cfpkt_destroy(pkt);
+ #endif
+ return result;
+ }
+ /* Update statistics. */
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += len;
+
+#ifndef CAIF_USE_SKB
+ dev_kfree_skb(skb);
+#endif
+ return NETDEV_TX_OK;
+
+#ifndef CAIF_USE_SKB
+err_cfpkt_create:
+ dev_kfree_skb(skb);/*FIXME: Freeing necessary here?*/
+ /*FIXME: Check if ENOMEM or ENOSPC should be used in CAIF?*/
+ return -ENOSPC;
+#endif
+}
+
+/*FIXME: MTU is not used, so why do we handle it in this function?
+ * who calls it?*/
+int chnl_net_change_mtu(struct net_device *dev, int mtu)
+{
+ unsigned long flags;
+ struct chnl_net *priv = netdev_priv(dev);
+ spinlock_t *lock = &priv->lock;
+
+ /* check range.
+ * MTU can not exceed maximum CAIF frame size (4095 bytes) */
+ if ((mtu < SIZE_MTU_MIN) || (mtu > SIZE_MTU_MAX))
+ return -EINVAL;
+
+ spin_lock_irqsave(lock, flags);
+ dev->mtu = mtu;
+ spin_unlock_irqrestore(lock, flags);
+ return 0;
+}
+
+
+int chnl_net_open(struct net_device *dev)
+{
+ struct chnl_net *priv = NULL;
+ int result;
+ CAIFLOG_ENTER("chnl_net_open:");
+ priv = (struct chnl_net *)netdev_priv(dev);
+ printk(KERN_INFO "chnl_net_open dev name: %s\n", priv->name);
+
+ if (!priv) {
+ printk(KERN_WARNING "chnl_net_open: no priv\n");
+ return -ENODEV;
+ }
+ result = caifdev_adapt_register(&priv->config, &priv->chnl);
+ if (result != 0) {
+ printk(KERN_WARNING
+ "chnl_net_open: err: Unable to open device, Err:%d\n", result);
+ return -ENODEV;
+ }
+ priv->deviceOpen = true;
+ CAIFLOG_EXIT("chnl_net_open:\n");
+ return 0;
+}
+
+int chnl_net_stop(struct net_device *dev)
+{
+ struct chnl_net *priv;
+ int result;
+ CAIFLOG_ENTER("chnl_net_stop:");
+ printk("chnl_net_stop: %s \n", dev->name);
+ priv = (struct chnl_net *)netdev_priv(dev);
+ result = caifdev_adapt_unregister(&priv->chnl);
+ if (result != 0) {
+ printk(KERN_WARNING
+ "chnl_net_stop: err: Unable to STOP device, Err:%d\n", result);
+ return -EBUSY;
+ }
+ priv->deviceOpen = false;
+ CAIFLOG_EXIT("chnl_net_stop:\n");
+ return 0;
+}
+
+
+void chnl_net_init(struct net_device *dev)
+{
+ CAIFLOG_ENTER("chnl_net_init:");
+ dev->open = chnl_net_open;
+ dev->stop = chnl_net_stop;
+ dev->hard_start_xmit = chnl_net_hard_start_xmit;
+ dev->flags |= IFF_NOARP; /*FIXME: Check additional flags*/
+ dev->mtu = SIZE_MTU;
+ CAIFLOG_EXIT("chnl_net_init:\n");
+}
+
+
+int netdev_create(struct caif_channel_create_action *action)
+{
+ struct chnl_net *priv;
+
+ int result = -1;
+
+ netdevptr = alloc_netdev(sizeof(struct chnl_net),
+ action->name.name, chnl_net_init);
+ priv = (struct chnl_net *)netdev_priv(netdevptr);
+ memset(&priv->config, 0, sizeof(priv->config));
+ priv->chnl.receive = chnl_recv_cb;
+ priv->chnl.ctrlcmd = chnl_flowctrl_cb;
+ priv->config = action->config;
+ priv->netdev = netdevptr;
+ strcpy(priv->name, action->name.name);
+ priv->deviceOpen = false;
+
+ /* Make sure flow is disabled until INIT_RSP is received. */
+ netif_tx_disable(priv->netdev);
+
+ spin_lock_init(&priv->lock);
+ /* Add the device */
+ spin_lock(&list_lock);
+ list_add(&priv->list_field, &chnl_net_list);
+ spin_unlock(&list_lock);
+
+ printk(KERN_WARNING "netdev_create: Creating channel: %s\n",
+ action->name.name);
+
+ if (!priv->netdev) {
+ printk(KERN_WARNING "chnl: can't allocate netdev.\n");
+ return -ENODEV;
+ }
+ result = register_netdev(priv->netdev);
+ if (result < 0) {
+ printk(KERN_WARNING
+ "chnl: err: %d, can't register netdev.\n", result);
+ free_netdev(priv->netdev);
+ return -ENODEV;
+ }
+ return 0;
+
+}
+
+int delete_device(struct chnl_net *dev)
+{
+ if (dev->netdev) {
+ unregister_netdev(dev->netdev);
+ free_netdev(dev->netdev);
+ }
+ printk(KERN_WARNING
+ "delete_device: Removing Device: %s\n", dev->name);
+ return 0;
+}
+
+int netdev_remove(char *name)
+{
+ struct chnl_net *dev = NULL;
+ /* Find device from name */
+ dev = find_device(name, true);
+ if (!dev)
+ return -EBADF;
+ else
+ if (delete_device(dev) != 0)
+ return -EBUSY;
+
+ return 0;
+}
+
+
+int netdev_mgmt(int action, union caif_action *param)
+{
+
+ switch (action) {
+ case CAIF_ACT_CREATE_DEVICE:
+ return netdev_create(¶m->create_channel);
+ case CAIF_ACT_DELETE_DEVICE:
+ return netdev_remove(param->delete_channel.name);
+ default:
+ return -EINVAL;
+ }
+}
+
+int chnl_init_module(void)
+{
+ caif_register_netdev(netdev_mgmt);
+ return 0; /*FIXME: Return error code for success*/
+}
+
+void chnl_exit_module(void)
+{
+ struct chnl_net *dev = NULL;
+
+ while ((dev = find_device(NULL, true)) != NULL) {
+ /* Remove any device (the first in the list) */
+ delete_device(dev);
+ };
+ caif_unregister_netdev();
+}
+
+module_init(chnl_init_module);
+module_exit(chnl_exit_module);
--
1.6.0.4
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists