* [PATCH net-next-2.6 00/13] net-caif: introducing CAIF protocol stack
@ 2010-01-20 22:55 sjur.brandeland
2010-01-20 22:55 ` [PATCH net-next-2.6 01/13] net-caif: add CAIF protocol definitions sjur.brandeland
` (13 more replies)
0 siblings, 14 replies; 47+ messages in thread
From: sjur.brandeland @ 2010-01-20 22:55 UTC (permalink / raw)
To: netdev; +Cc: davem, marcel, stefano.babic, randy.dunlap
This patch-set introduces the CAIF protocol Stack.
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.
* CAIF provides a socket interface which can be used to open virtual AT
channels, create and configure GPRS Data connections.
* A CAIF Network device is also provided for GPRS access.
This interface can be managed with socket IOCTLs or RTNL.
* A kernel interface is provided allowing Kernel modules to use CAIF.
* A serial link layer layer implemented as a line discipline is currently
implemented. Other link interfaces may be contributed at a later stage.
RTNL/IOCTL
!
! +------+ +------+ +------+
! +------+! +------+! +------+!
! ! Sock !! !Kernel!! ! Net !!
! ! API !+ ! API !+ ! Dev !+ <- CAIF "Client" APIs
! +------+ +------! +------+
! ! ! !
! +----------!----------+
! +------+ <- CAIF Protocol Implementation
+-------> ! CAIF !
+------+
+--------!--------+
! !
+------+ +-----+
! ldisc! ! ... ! <- Link Layer (Net Devices)
+------+ +-----+
!
+------+
! tty ! <- Serial Layer
+------+
The patch set is based on net-next-2.6.
Sjur Braendeland (13):
net-caif: add CAIF protocol definitions
net-caif: add CAIF header files
net-caif: add CAIF generic protocol stack header files
net-caif: add CAIF header files
net-caif: add CAIF generic protocol stack
net-caif: add CAIF generic caif support functions
net-caif: add CAIF device registration functionality
net-caif: add CAIF socket implementation
net-caif: add CAIF netdevice
net-caif: add kernel-client API for CAIF
net-caif: add CAIF Kconfig and Makefiles
net-caif: add CAIF documentation
net-caif-driver: add CAIF serial driver (ldisc)
Documentation/networking/caif/Linux-CAIF.txt | 235 +++++
Documentation/networking/caif/README | 27 +
drivers/net/Kconfig | 2 +
drivers/net/Makefile | 1 +
drivers/net/caif/Kconfig | 15 +
drivers/net/caif/Makefile | 14 +
drivers/net/caif/caif_serial.c | 420 ++++++++
include/linux/caif/caif_config.h | 155 +++
include/linux/caif/caif_socket.h | 161 +++
include/linux/caif/if_caif.h | 71 ++
include/linux/if_arp.h | 1 +
include/linux/if_ether.h | 1 +
include/linux/socket.h | 5 +-
include/linux/tty.h | 4 +-
include/net/caif/caif_dev.h | 29 +
include/net/caif/caif_device.h | 34 +
include/net/caif/caif_kernel.h | 309 ++++++
include/net/caif/generic/caif_layer.h | 225 ++++
include/net/caif/generic/cfcnfg.h | 110 ++
include/net/caif/generic/cfctrl.h | 136 +++
include/net/caif/generic/cffrml.h | 19 +
include/net/caif/generic/cfglue.h | 110 ++
include/net/caif/generic/cflst.h | 19 +
include/net/caif/generic/cfmuxl.h | 22 +
include/net/caif/generic/cfpkt.h | 274 +++++
include/net/caif/generic/cfserl.h | 14 +
include/net/caif/generic/cfsrvl.h | 33 +
net/Kconfig | 2 +
net/Makefile | 1 +
net/caif/Kconfig | 57 +
net/caif/Makefile | 29 +
net/caif/caif_chnlif.c | 177 ++++
net/caif/caif_config_util.c | 121 +++
net/caif/caif_dev.c | 417 ++++++++
net/caif/caif_socket.c | 1427 ++++++++++++++++++++++++++
net/caif/chnl_net.c | 566 ++++++++++
net/caif/generic/cfcnfg.c | 538 ++++++++++
net/caif/generic/cfctrl.c | 699 +++++++++++++
net/caif/generic/cfdgml.c | 106 ++
net/caif/generic/cffrml.c | 148 +++
net/caif/generic/cflist.c | 88 ++
net/caif/generic/cfmuxl.c | 225 ++++
net/caif/generic/cfpkt_skbuff.c | 596 +++++++++++
net/caif/generic/cfrfml.c | 104 ++
net/caif/generic/cfserl.c | 198 ++++
net/caif/generic/cfsrvl.c | 182 ++++
net/caif/generic/cfutill.c | 112 ++
net/caif/generic/cfveil.c | 106 ++
net/caif/generic/cfvidl.c | 62 ++
49 files changed, 8404 insertions(+), 3 deletions(-)
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH net-next-2.6 01/13] net-caif: add CAIF protocol definitions
2010-01-20 22:55 [PATCH net-next-2.6 00/13] net-caif: introducing CAIF protocol stack sjur.brandeland
@ 2010-01-20 22:55 ` sjur.brandeland
2010-01-20 22:55 ` [PATCH net-next-2.6 02/13] net-caif: add CAIF header files sjur.brandeland
` (12 subsequent siblings)
13 siblings, 0 replies; 47+ messages in thread
From: sjur.brandeland @ 2010-01-20 22:55 UTC (permalink / raw)
To: netdev; +Cc: davem, marcel, stefano.babic, randy.dunlap, Sjur Braendeland
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
Add CAIF definitions to existing header files.
Files: if_arp.h, if_ether.h, socket.h.
Types: ARPHRD_CAIF, ETH_P_CAIF, AF_CAIF, PF_CAIF, SOL_CAIF, N_CAIF
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
include/linux/if_arp.h | 1 +
include/linux/if_ether.h | 1 +
include/linux/socket.h | 5 ++++-
3 files changed, 6 insertions(+), 1 deletions(-)
diff --git a/include/linux/if_arp.h b/include/linux/if_arp.h
index e80b7f8..6d722f4 100644
--- a/include/linux/if_arp.h
+++ b/include/linux/if_arp.h
@@ -90,6 +90,7 @@
#define ARPHRD_PHONET 820 /* PhoNet media type */
#define ARPHRD_PHONET_PIPE 821 /* PhoNet pipe header */
+#define ARPHRD_CAIF 822 /* CAIF media type */
#define ARPHRD_VOID 0xFFFF /* Void type, nothing is known */
#define ARPHRD_NONE 0xFFFE /* zero header length */
diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h
index 299b412..bed7a46 100644
--- a/include/linux/if_ether.h
+++ b/include/linux/if_ether.h
@@ -109,6 +109,7 @@
#define ETH_P_TRAILER 0x001C /* Trailer switch tagging */
#define ETH_P_PHONET 0x00F5 /* Nokia Phonet frames */
#define ETH_P_IEEE802154 0x00F6 /* IEEE802.15.4 frame */
+#define ETH_P_CAIF 0x00F7 /* ST-Ericsson CAIF protocol */
/*
* This is an Ethernet frame header.
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 7b3aae2..960659b 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -189,7 +189,8 @@ struct ucred {
#define AF_ISDN 34 /* mISDN sockets */
#define AF_PHONET 35 /* Phonet sockets */
#define AF_IEEE802154 36 /* IEEE802154 sockets */
-#define AF_MAX 37 /* For now.. */
+#define AF_CAIF 37 /* CAIF sockets */
+#define AF_MAX 38 /* For now.. */
/* Protocol families, same as address families. */
#define PF_UNSPEC AF_UNSPEC
@@ -229,6 +230,7 @@ struct ucred {
#define PF_ISDN AF_ISDN
#define PF_PHONET AF_PHONET
#define PF_IEEE802154 AF_IEEE802154
+#define PF_CAIF AF_CAIF
#define PF_MAX AF_MAX
/* Maximum queue length specifiable by listen. */
@@ -300,6 +302,7 @@ struct ucred {
#define SOL_PNPIPE 275
#define SOL_RDS 276
#define SOL_IUCV 277
+#define SOL_CAIF 278
/* IPX options */
#define IPX_TYPE 1
--
1.6.3.3
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PATCH net-next-2.6 02/13] net-caif: add CAIF header files
2010-01-20 22:55 [PATCH net-next-2.6 00/13] net-caif: introducing CAIF protocol stack sjur.brandeland
2010-01-20 22:55 ` [PATCH net-next-2.6 01/13] net-caif: add CAIF protocol definitions sjur.brandeland
@ 2010-01-20 22:55 ` sjur.brandeland
2010-01-20 23:27 ` Randy Dunlap
` (2 more replies)
2010-01-20 22:55 ` [PATCH net-next-2.6 03/13] net-caif: add CAIF generic protocol stack " sjur.brandeland
` (11 subsequent siblings)
13 siblings, 3 replies; 47+ messages in thread
From: sjur.brandeland @ 2010-01-20 22:55 UTC (permalink / raw)
To: netdev; +Cc: davem, marcel, stefano.babic, randy.dunlap, Sjur Braendeland
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
Add CAIF types for Socket Address, Socket Options,
and configuration parameters for the GPRS IP network interface.
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
include/linux/caif/caif_config.h | 155 ++++++++++++++++++++++++++++++++++++
include/linux/caif/caif_socket.h | 161 ++++++++++++++++++++++++++++++++++++++
include/linux/caif/if_caif.h | 71 +++++++++++++++++
3 files changed, 387 insertions(+), 0 deletions(-)
diff --git a/include/linux/caif/caif_config.h b/include/linux/caif/caif_config.h
new file mode 100644
index 0000000..5de8bd2
--- /dev/null
+++ b/include/linux/caif/caif_config.h
@@ -0,0 +1,155 @@
+/*
+ * CAIF Channel Configuration definitions.
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/ sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef CAIF_CONFIG_H_
+#define CAIF_CONFIG_H_
+
+/**
+ * enum caif_phy_preference - Types of physical HW interfaces
+ * towards modem defined in CAIF stack
+ * @CAIF_PHYPREF_UNSPECIFIED: Default physical interface
+ * @CAIF_PHYPREF_LOW_LAT: Default physical interface for low-latency
+ * traffic
+ * @CAIF_PHYPREF_HIGH_BW: Default physical interface for high-bandwidth
+ * traffic
+ * @CAIF_PHYPREF_LOOP: TEST Loopback interface, simulating modem
+ * responses
+ *
+ * For client convenience, two special types are defined:
+ * CAIF_PHYPREF_LOW_LAT is the preferred low-latency physical link.
+ * Typically used for "control" purposes.
+ * CAIF_PHYPREF_HIGH_BW is the preferred high-bandwidth physical link.
+ * Typically used for "payload" purposes.
+ */
+enum caif_phy_preference {
+ CAIF_PHYPREF_UNSPECIFIED,
+ CAIF_PHYPREF_LOW_LAT,
+ CAIF_PHYPREF_HIGH_BW,
+ CAIF_PHYPREF_LOOP
+};
+
+/*
+ * Define CAIF channel priority.
+ * Used when setting up a channel to specify the
+ * priority level of the channel.
+ */
+#define CAIF_PRIO_MIN 0x01 /* Min priority for a channel */
+#define CAIF_PRIO_LOW 0x04 /* low-priority channel. */
+#define CAIF_PRIO_NORMAL 0x0f /* normal/default priority level. */
+#define CAIF_PRIO_HIGH 0x14 /* high priority level */
+#define CAIF_PRIO_MAX 0x1F /* Max priority for channel */
+
+
+/**
+ * enum caif_channel_type - Types of CAIF channel type defined in CAIF Stack.
+ * @CAIF_CHTY_AT: Classical AT
+ * @CAIF_CHTY_AT_CTRL: AT control only
+ * @CAIF_CHTY_AT_PAIRED: Paired control and data
+ * @CAIF_CHTY_DATAGRAM: Datagram. Requires: connection_id
+ * @CAIF_CHTY_DATAGRAM_LOOP: Datagram loopback (testing purposes only)
+ * @CAIF_CHTY_VIDEO: Video channel
+ * @CAIF_CHTY_DEBUG: Debug service (Debug server and
+ * interactive debug)
+ * @CAIF_CHTY_DEBUG_TRACE: Debug server only
+ * @CAIF_CHTY_DEBUG_INTERACT: Debug interactive
+ * @CAIF_CHTY_RFM: RFM service. Params: connection_id, volume
+ * @CAIF_CHTY_UTILITY: Utility (Psock) service.
+ * Params: fifo_kb,fifo_pkt, name, psock_param
+ *
+ * This is used for channel configuration, specifying the type of channel.
+ */
+enum caif_channel_type {
+ CAIF_CHTY_AT,
+ CAIF_CHTY_AT_CTRL,
+ CAIF_CHTY_AT_PAIRED,
+ CAIF_CHTY_DATAGRAM,
+ CAIF_CHTY_DATAGRAM_LOOP,
+ CAIF_CHTY_VIDEO,
+ CAIF_CHTY_DEBUG,
+ CAIF_CHTY_DEBUG_TRACE,
+ CAIF_CHTY_DEBUG_INTERACT,
+ CAIF_CHTY_RFM,
+ CAIF_CHTY_UTILITY
+};
+
+/**
+ *struct caif_channel_config - This structure is used for configuring
+ * CAIF channels.
+ * @name: Mandatory - Nickname for this device
+ * @type: Mandatory - Define the type of caif service
+ * @priority: Mandatory - Value between CAIF_PRIO_MIN and
+ * CAIF_PRIO_MAX.
+ * CAIF_PRIO_LOW, CAIF_PRIO_NORMAL, CAIF_PRIO_HIGH
+ * are suggested values.
+ * @phy_pref: Either: Specify type of physical interface to use.
+ * @phy_name: Or: Specify identity of the physical interface.
+ *
+ * @u: Union of channel type-specific configuration
+ * parameters
+ *
+ * @u.dgm: CAIF_CHTYPE_DATAGRAM
+ * @u.dgm.connection_id: Mandatory - Connection ID must be specified.
+ *
+ * @u.video: CAIF_CHTYPE_VIDEO
+ * @u.video.connection_id: Mandatory - Connection ID must be specified.
+ *
+ * @u.rfm CAIF_CHTYPE_RFM
+ * @u.rfm.connection_id: Mandatory - Connection ID must be specified.
+ * @u.rfm.volume: Mandatory - Volume to mount.
+ *
+ * @u.utility: CAIF_CHTYPE_UTILITY
+ * @u.utility.fifosize_kb: Psock: FIFO size in KB
+ * @u.utility.fifosize_bufs: Psock: Number of signal buffers
+ * @u.utility.name: Psock: Name of service
+ * @u.utility.params: Psock: Channel config parameters
+ * @u.utility.paramlen: Psock: Length of channel config parameters
+ *
+ *
+ * It holds configuration parameters for setting up all defined CAIF
+ * channel types.
+ * The four first fields are mandatory, then physical interface can be specified
+ * either by name or by prefered characteristics.
+ * The rest of the configuration fields are held in a union for each
+ * channel type and are channel type specific.
+ */
+struct caif_channel_config {
+ char name[16];
+ enum caif_channel_type type;
+ unsigned priority;
+ enum caif_phy_preference phy_pref;
+ char phy_name[16];
+
+ /** Union of channel type-specific configuration parameters.
+ * 'switched' by attribute type.
+ */
+ union {
+ /* CAIF_CHTYPE_DATAGRAM */
+ struct {
+ unsigned connection_id;
+ } dgm;
+ /* CAIF_CHTYPE_VIDEO */
+ struct {
+ unsigned connection_id;
+ } video;
+ /* CAIF_CHTYPE_RFM */
+ struct {
+ unsigned connection_id;
+ char volume[20];
+ } rfm;
+ /* CAIF_CHTYPE_UTILITY */
+ struct {
+ unsigned fifosize_kb;
+ unsigned fifosize_bufs;
+ char name[16];
+ unsigned char params[256];
+ int paramlen;
+ } utility;
+
+ } u;
+};
+
+#endif /* CAIF_CONFIG_H_ */
diff --git a/include/linux/caif/caif_socket.h b/include/linux/caif/caif_socket.h
new file mode 100644
index 0000000..44774e8
--- /dev/null
+++ b/include/linux/caif/caif_socket.h
@@ -0,0 +1,161 @@
+/* linux/caif_socket.h
+ * CAIF Definitions for CAIF socket and network layer
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/ sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef _LINUX_CAIF_SOCKET_H
+#define _LINUX_CAIF_SOCKET_H
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#include <linux/socket.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * enum caif_link_selector - Physical Link Selection.
+ * @CAIF_LINK_HIGH_BANDW: Default physical interface for high-bandwidth
+ * traffic.
+ * @CAIF_LINK_LOW_LATENCY: Default physical interface for low-latency
+ * traffic.
+ */
+enum caif_link_selector {
+ CAIF_LINK_HIGH_BANDW,
+ CAIF_LINK_LOW_LATENCY
+};
+
+/**
+ * enum caif_protocol_type - Types of CAIF protocols in the CAIF Stack.
+ * @CAIFPROTO_AT: Classic AT channel.
+ * @CAIFPROTO_DATAGRAM: Datagram channel.
+ * @CAIFPROTO_DATAGRAM_LOOP: Datagram loopback channel, used for testing.
+ * @CAIFPROTO_UTIL: Utility (Psock) channel.
+ * @CAIFPROTO_RFM: Remote File Manager
+ */
+enum caif_protocol_type {
+ CAIFPROTO_AT,
+ CAIFPROTO_DATAGRAM,
+ CAIFPROTO_DATAGRAM_LOOP,
+ CAIFPROTO_UTIL,
+ CAIFPROTO_RFM,
+ _CAIFPROTO_MAX
+};
+#define CAIFPROTO_MAX _CAIFPROTO_MAX
+
+/**
+ * enum caif_at_type - AT Service Endpoint
+ * @CAIF_ATTYPE_PLAIN: Connects to a plain vanilla AT channel.
+ */
+enum caif_at_type {
+ CAIF_ATTYPE_PLAIN
+};
+
+/**
+ * struct sockaddr_caif - the sockaddr structure for CAIF sockets.
+ * @u: Union of address data 'switched' by familty.
+ * @at: Applies when family = CAIFPROTO_AT.
+ * @at.type: Type of AT link to set up (enum caif_at_type).
+ * @util: Applies when family = CAIFPROTO_UTIL
+ * @util.service: Service name.
+ * @dgm: Applies when family = CAIFPROTO_DATAGRAM
+ * @dgm.connection_id: Datagram connection id.
+ * @dgm.nsapi: NSAPI of the PDP-Context.
+ * @rfm: Applies when family = CAIFPROTO_RFM
+ * @rfm.connection_id: Connection ID for RFM.
+ * @rfm.volume: Volume to mount.
+ */
+struct sockaddr_caif {
+ sa_family_t family;
+ union {
+ struct {
+ u_int8_t type; /* type: enum caif_at_type */
+ } at; /* CAIFPROTO_AT */
+ struct {
+ char service[16];
+ } util; /* CAIFPROTO_UTIL */
+ union {
+ u_int32_t connection_id;
+ u_int8_t nsapi;
+ } dgm; /* CAIFPROTO_DATAGRAM(_LOOP)*/
+ struct {
+ u_int32_t connection_id;
+ char volume[16];
+ } rfm; /* CAIFPROTO_RFM */
+ } u;
+};
+
+/**
+ * struct caif_channel_opt - CAIF channel connect options.
+ * @priority: Priority of the channel (between 0 and 0x1f)
+ * @link_selector: Selector for the physical link.
+ * (see enum caif_phy_preference in caif_config.h)
+ * @link_name: Physical link to use. This is the instance name of the
+ * CAIF Physical Driver.
+ */
+struct caif_channel_opt {
+ u_int16_t priority;
+ u_int16_t link_selector;
+ char link_name[16];
+};
+
+/**
+ * struct caif_param - CAIF parameters.
+ * @size: Length of data
+ * @data: Binary Data Blob
+ */
+struct caif_param {
+ u_int16_t size;
+ u_int8_t data[256];
+};
+
+
+/** enum caif_socket_opts - CAIF option values for getsockopt and setsockopt
+ * @CAIFSO_CHANNEL: Used to set the connect options on a CAIF
+ * socket. (struct caif_config_opt). This can only
+ * be set before connecting.
+ * @CAIFSO_REQ_PARAM: Used to set the request parameters for a
+ * utility channel. (struct caif_param). This
+ * can only be set before connecting.
+ *
+ * @CAIFSO_RSP_PARAM: Gets the request parameters for a utility
+ * channel. (struct caif_param). This can only be
+ * fetched after connecting the socket.
+ *
+ * @CAIFSO_UTIL_FLOW: Sets the utility channels flow options.
+ * This can only be set before connecting.
+ * (struct caif_util_modem_flow_opt)
+ *
+ * @CAIFSO_CONN_ID: Gets the channel id on a CAIF Channel.
+ * This can only be done after connect.
+ * ( u_int32_t)
+ *
+ * @CAIFSO_NEXT_PAKCET_LEN: Gets the size of next received packet.
+ * Value is 0 if no packet is available.
+ * This can only be done after connect.
+ * ( u_int32_t)
+ *
+ * @CAIFSO_MAX_PAKCET_LEN: Gets the maximum packet size for this
+ * connection. ( u_int32_t)
+ */
+enum caif_socket_opts {
+ CAIFSO_CHANNEL_CONFIG = 127,
+ CAIFSO_REQ_PARAM = 128,
+ CAIFSO_RSP_PARAM = 129,
+ CAIFSO_UTIL_FLOW = 130,
+ CAIFSO_CONN_ID = 131,
+ CAIFSO_NEXT_PACKET_LEN = 132,
+ CAIFSO_MAX_PACKET_LEN = 133,
+};
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+#endif /* _LINUX_CAIF_SOCKET_H */
diff --git a/include/linux/caif/if_caif.h b/include/linux/caif/if_caif.h
new file mode 100644
index 0000000..e1991b1
--- /dev/null
+++ b/include/linux/caif/if_caif.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/ sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef IF_CAIF_H_
+#define IF_CAIF_H_
+#include <linux/sockios.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+
+/**
+ * enum sioc_caif - SOCKIO for creating new CAIF Net Devices.
+ * @SIOCCAIFNETNEW: Used to create a new instance of the CAIF IP Interface.
+ * struct ifreq containing struct ifcaif_param are used
+ * as parameters. ifr_name must be filled in.
+ * @SIOCCAIFNETCHANGE: As above, but changes a disconnected CAIF IP Inteface.
+ * @SIOCCAIFNETREMOVE: Removes a CAIF IP Interface.
+ *
+ * CAIF IP Interface can be created, changed and deleted,
+ * by this enum. In addition standard Socket IO Controls (SIGIOC*)
+ * can be used to manage standard IP Interface parameters.
+ * The struct ifreq are used to carry parameters.
+ */
+enum sioc_caif {
+ SIOCCAIFNETNEW = SIOCPROTOPRIVATE,
+ SIOCCAIFNETCHANGE,
+ SIOCCAIFNETREMOVE
+};
+
+
+/**
+ * struct ifcaif_param - Parameters for creating CAIF Network Interface.
+ *
+ * When using SIOCCAIFNETNEW to create a CAIF IP interface, this structure
+ * is used for configuration data.
+ * The attribute ifr_ifru.ifru_data in struct struct ifreq must be set
+ * point at an instance of struct ifcaif_param.
+ *
+ * @ipv4_connid: Connection ID for IPv4 PDP Context.
+ * @ipv6_connid: Connection ID for IPv6 PDP Context.
+ * @loop: If different from zero, device is doing loopback
+ */
+struct ifcaif_param {
+ __u32 ipv4_connid;
+ __u32 ipv6_connid;
+ __u8 loop;
+};
+
+/**
+ * enum ifla_caif
+ * When using RT Netlink to create, destroy or configure a CAIF IP interface,
+ * enum ifla_caif is used to specify the configuration attributes.
+ *
+ * @IFLA_CAIF_IPV4_CONNID: Connection ID for IPv4 PDP Context.
+ * The type of attribute is NLA_U32.
+ * @IFLA_CAIF_IPV6_CONNID: Connection ID for IPv6 PDP Context.
+ * The type of attribute is NLA_U32.
+ * @IFLA_CAIF_LOOPBACK: If different from zero, device is doing loopback
+ * The type of attribute is NLA_U8.
+ */
+enum ifla_caif {
+ IFLA_CAIF_IPV4_CONNID,
+ IFLA_CAIF_IPV6_CONNID,
+ IFLA_CAIF_LOOPBACK,
+ __IFLA_CAIF_MAX
+};
+#define IFLA_CAIF_MAX (__IFLA_CAIF_MAX-1)
+
+#endif /*IF_CAIF_H_*/
--
1.6.3.3
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PATCH net-next-2.6 03/13] net-caif: add CAIF generic protocol stack header files
2010-01-20 22:55 [PATCH net-next-2.6 00/13] net-caif: introducing CAIF protocol stack sjur.brandeland
2010-01-20 22:55 ` [PATCH net-next-2.6 01/13] net-caif: add CAIF protocol definitions sjur.brandeland
2010-01-20 22:55 ` [PATCH net-next-2.6 02/13] net-caif: add CAIF header files sjur.brandeland
@ 2010-01-20 22:55 ` sjur.brandeland
2010-01-21 8:13 ` Patrick McHardy
2010-01-22 9:28 ` Marcel Holtmann
2010-01-20 22:55 ` [PATCH net-next-2.6 04/13] net-caif: add CAIF " sjur.brandeland
` (10 subsequent siblings)
13 siblings, 2 replies; 47+ messages in thread
From: sjur.brandeland @ 2010-01-20 22:55 UTC (permalink / raw)
To: netdev; +Cc: davem, marcel, stefano.babic, randy.dunlap, Sjur Braendeland
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
Add include files for the generic CAIF protocol stack. This layer
is somewhat generic in order to be able to use and test it outside
the Linux Kernel.
caif_layer.h - Defines the structure of the CAIF protocol layers
cfcnfg.h - CAIF Configuration Module for services and link layers
cfctrl.h - CAIF Control Protocol Layer
cffrml.h - CAIF Framing Layer
cfglue.h - CAIF Glue Layer for allocation, logging etc
cflist.h - CAIF List implementation
cfmuxl.h - CAIF Muxing Layer
cfpkt.h - CAIF Packet layer (skb helper functions)
cfserl.h - CAIF Serial Layer
cfsrvl.h - CAIF Service Layer
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
include/net/caif/generic/caif_layer.h | 225 +++++++++++++++++++++++++++
include/net/caif/generic/cfcnfg.h | 110 +++++++++++++
include/net/caif/generic/cfctrl.h | 136 ++++++++++++++++
include/net/caif/generic/cffrml.h | 19 +++
include/net/caif/generic/cfglue.h | 110 +++++++++++++
include/net/caif/generic/cflst.h | 19 +++
include/net/caif/generic/cfmuxl.h | 22 +++
include/net/caif/generic/cfpkt.h | 274 +++++++++++++++++++++++++++++++++
include/net/caif/generic/cfserl.h | 14 ++
include/net/caif/generic/cfsrvl.h | 33 ++++
10 files changed, 962 insertions(+), 0 deletions(-)
diff --git a/include/net/caif/generic/caif_layer.h b/include/net/caif/generic/caif_layer.h
new file mode 100644
index 0000000..2ae3236
--- /dev/null
+++ b/include/net/caif/generic/caif_layer.h
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland / sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef CAIF_LAYER_H_
+#define CAIF_LAYER_H_
+
+#include <net/caif/generic/cfglue.h>
+
+struct layer;
+struct cfpkt;
+struct cfpktq;
+struct payload_info;
+struct caif_packet_funcs;
+
+#define CAIF_MAX_FRAMESIZE 4096
+#define CAIF_MAX_PAYLOAD_SIZE (4096 - 64)
+#define CAIF_NEEDED_HEADROOM (10)
+#define CAIF_NEEDED_TAILROOM (2)
+
+
+#define CAIF_LAYER_NAME_SZ 16
+#define CAIF_SUCCESS 1
+#define CAIF_FAILURE 0
+
+/*
+ * CAIF Control Signaling.
+ * These commands are sent upwards in the CAIF stack. They are used for
+ * signaling originating from the modem.
+ * These are either responses (*_RSP) or events (*_IND).
+ */
+enum caif_ctrlcmd {
+ /* Flow Control is OFF, transmit function should stop sending data */
+ CAIF_CTRLCMD_FLOW_OFF_IND,
+ /* Flow Control is ON, transmit function can start sending data */
+ CAIF_CTRLCMD_FLOW_ON_IND,
+ /* Remote end modem has decided to close down channel */
+ CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND,
+ /* Called initially when the layer below has finished initialization */
+ CAIF_CTRLCMD_INIT_RSP,
+ /* Called when de-initialization is complete */
+ CAIF_CTRLCMD_DEINIT_RSP,
+ /* Called if initialization fails */
+ CAIF_CTRLCMD_INIT_FAIL_RSP,
+ /* Called if physical interface cannot send more packets. */
+ _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND,
+ /* Called if physical interface is able to send packets again. */
+ _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND,
+ /* Called if physical interface is going down. */
+ _CAIF_CTRLCMD_PHYIF_DOWN_IND,
+};
+
+/*
+ * Modem Control Signaling.
+ * These are requests sent 'downwards' in the stack.
+ * Flow ON, OFF can be indicated to the modem.
+ */
+enum caif_modemcmd {
+ /* Flow Control is ON, transmit function can start sending data */
+ CAIF_MODEMCMD_FLOW_ON_REQ = 0,
+ /* Flow Control is OFF, transmit function should stop sending data */
+ CAIF_MODEMCMD_FLOW_OFF_REQ = 1,
+ /* Notify physical layer that it is in use */
+ _CAIF_MODEMCMD_PHYIF_USEFULL = 3,
+ /* Notify physical layer that it is no longer in use */
+ _CAIF_MODEMCMD_PHYIF_USELESS = 4
+};
+
+/*
+ * CAIF Packet Direction.
+ * Indicate if a packet is to be sent out or to be received in.
+ */
+enum caif_direction {
+ CAIF_DIR_IN = 0, /* Incoming packet received. */
+ CAIF_DIR_OUT = 1 /* Outgoing packet to be transmitted. */
+};
+
+/*
+ * This structure defines the generic layered structure in CAIF.
+ * It is inspired by the "Protocol Layer Design Pattern" (Streams).
+ *
+ * It defines a generic layering structure, used by all CAIF Layers and the
+ * layers interfacing CAIF.
+ *
+ * In order to integrate with CAIF an adaptation layer on top of the CAIF stack
+ * and PHY layer below the CAIF stack
+ * must be implemented. These layer must follow the design principles below.
+ *
+ * Principles for layering of protocol layers:
+ * -# All layers must use this structure. If embedding it, then place this
+ * structure first in the layer specific structure.
+ * -# Each layer should not depend on any others layer private data.
+ * -# In order to send data upwards do
+ * layer->up->receive(layer->up, packet);
+ * -# In order to send data downwards do
+ * layer->dn->transmit(layer->dn, info, packet);
+ */
+struct layer {
+
+ struct layer *up; /* Pointer to the layer above */
+ struct layer *dn; /* Pointer to the layer below */
+ /*
+ * Receive Function.
+ * Contract: Each layer must implement a receive function passing the
+ * CAIF packets upwards in the stack.
+ * Packet handling rules:
+ * -# The CAIF packet (cfpkt) cannot be accessed after
+ * passing it to the next layer using up->receive().
+ * -# If parsing of the packet fails, the packet must be
+ * destroyed and -1 returned from the function.
+ * -# If parsing succeeds (and above layers return OK) then
+ * the function must return a value > 0.
+ *
+ * @param[in] layr Pointer to the current layer the receive function is
+ * implemented for (this pointer).
+ * @param[in] cfpkt Pointer to CaifPacket to be handled.
+ * @return result < 0 indicates an error, 0 or positive value
+ * indicates success.
+ */
+ int (*receive)(struct layer *layr, struct cfpkt *cfpkt);
+
+ /*
+ * Transmit Function.
+ * Contract: Each layer must implement a transmit function passing the
+ * CAIF packet downwards in the stack.
+ * Packet handling rules:
+ * -# The CAIF packet (cfpkt) ownership is passed to the
+ * transmit function. This means that the the packet
+ * cannot be accessed after passing it to the below
+ * layer using dn->transmit().
+ *
+ * -# If transmit fails, however, the ownership is returned
+ * to thecaller. The caller of "dn->transmit()" must
+ * destroy or resend packet.
+ *
+ * -# Return value less than zero means error, zero or
+ * greater than zero means OK.
+ *
+ * @param[in] layr Pointer to the current layer the receive function
+ * is implemented for (this pointer).
+ * @param[in] cfpkt Pointer to CaifPacket to be handled.
+ * @return result < 0 indicates an error, 0 or positive value
+ * indicate success.
+ */
+ int (*transmit) (struct layer *layr, struct cfpkt *cfpkt);
+
+ /*
+ * Control Function used to signal upwards in the CAIF stack.
+ * Used for signaling responses (CAIF_CTRLCMD_*_RSP)
+ * and asynchronous events from the modem (CAIF_CTRLCMD_*_IND)
+ *
+ * @param[in] layr Pointer to the current layer the receive function
+ * is implemented for (this pointer).
+ * @param[in] ctrl Control Command.
+ */
+ void (*ctrlcmd) (struct layer *layr, enum caif_ctrlcmd ctrl,
+ int phyid);
+
+ /*
+ * Control Function used for controlling the modem. Used to signal
+ * down-wards in the CAIF stack.
+ * @returns 0 on success, < 0 upon failure.
+ * @param[in] layr Pointer to the current layer the receive function
+ * is implemented for (this pointer).
+ * @param[in] ctrl Control Command.
+ */
+ int (*modemcmd) (struct layer *layr, enum caif_modemcmd ctrl);
+
+ struct layer *next; /*
+ * Pointer to chain of layers, up/dn will
+ * then point at the first element of a
+ * which then should be iterated through
+ * the next pointer.
+ */
+ unsigned short prio; /* Priority of this layer */
+ unsigned int id; /* The identity of this layer. */
+ unsigned int type; /* The type of this layer */
+ char name[CAIF_LAYER_NAME_SZ]; /* Name of the layer */
+};
+/*
+ * Set the up pointer for a specified layer.
+ * @param layr Layer where up pointer shall be set.
+ * @param above Layer above.
+ */
+#define layer_set_up(layr, above) ((layr)->up = (struct layer *)(above))
+
+/*
+ * Set the dn pointer for a specified layer.
+ * @param layr Layer where down pointer shall be set.
+ * @param below Layer below.
+ */
+#define layer_set_dn(layr, below) ((layr)->dn = (struct layer *)(below))
+
+/* Physical Device info, holding information about physical layer. */
+struct dev_info {
+ /* Pointer to native physical device */
+ void *dev;
+
+ /*
+ * Physical ID of the physical connection used by the logical CAIF
+ * connection. Used by service layers to identify their physical id
+ * to Caif MUX (CFMUXL)so that the MUX can add the correct physical
+ * ID to the packet.
+ */
+ unsigned int id;
+};
+
+/* Transmit info, passed downwards in protocol layers. */
+struct payload_info {
+ /* Information about the receiving device */
+ struct dev_info *dev_info;
+
+ /* Header length, used to align pay load on 32bit boundary. */
+ unsigned short hdr_len;
+
+ /*
+ * Channel ID of the logical CAIF connection.
+ * Used by mux to insert channel id into the caif packet.
+ */
+ unsigned short channel_id;
+};
+
+#endif /* CAIF_LAYER_H_ */
diff --git a/include/net/caif/generic/cfcnfg.h b/include/net/caif/generic/cfcnfg.h
new file mode 100644
index 0000000..3461235
--- /dev/null
+++ b/include/net/caif/generic/cfcnfg.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef CFCNFG_H_
+#define CFCNFG_H_
+#include <net/caif/generic/caif_layer.h>
+#include <net/caif/generic/cfctrl.h>
+
+struct cfcnfg;
+
+/* Types of physical layers defined in CAIF Stack */
+enum cfcnfg_phy_type {
+ /* Fragmented frames physical interface */
+ CFPHYTYPE_FRAG = 1,
+ /* Generic CAIF physical interface */
+ CFPHYTYPE_CAIF,
+ CFPHYTYPE_MAX
+};
+
+/* Physical preference - HW Abstraction */
+enum cfcnfg_phy_preference {
+ /* Default physical interface */
+ CFPHYPREF_UNSPECIFIED,
+ /* Default physical interface for low-latency traffic */
+ CFPHYPREF_LOW_LAT,
+ /* Default physical interface for high-bandwidth traffic */
+ CFPHYPREF_HIGH_BW,
+ /* TEST only Loopback interface simulating modem responses */
+ CFPHYPREF_LOOP
+};
+
+/*
+ * Create the CAIF configuration object.
+ * @return The created instance of a CFCNFG object.
+ */
+struct cfcnfg *cfcnfg_create(void);
+
+/* Remove the CFCNFG object */
+void cfcnfg_remove(struct cfcnfg *cfg);
+
+/*
+ * Adds a physical layer to the CAIF stack.
+ * @param cnfg Pointer to a CAIF configuration object, created by
+ * cfcnfg_create().
+ * @param phy_type Specifies the type of physical interface, e.g.
+ * CFPHYTYPE_FRAG.
+ * @param phy_layer Specify the physical layer. The transmit function
+ * MUST be set in the structure.
+ * @param phyid [out] The assigned physical ID for this layer,
+ * used in \ref cfcnfg_add_adapt_layer to specify
+ * PHY for the link.
+ */
+
+void
+cfcnfg_add_phy_layer(struct cfcnfg *cnfg, enum cfcnfg_phy_type phy_type,
+ void *dev, struct layer *phy_layer, uint16 *phyid,
+ enum cfcnfg_phy_preference pref,
+ bool fcs, bool stx);
+
+/*
+ * Deletes an phy layer from the CAIF stack.
+ *
+ * @param cnfg Pointer to a CAIF configuration object, created by
+ * cfcnfg_create().
+ * @param phy_layer Adaptation layer to be removed.
+ * @return 0 on success.
+ */
+int cfcnfg_del_phy_layer(struct cfcnfg *cnfg, struct layer *phy_layer);
+
+
+/*
+ * Deletes an adaptation layer from the CAIF stack.
+ *
+ * @param cnfg Pointer to a CAIF configuration object, created by
+ * cfcnfg_create().
+ * @param adap_layer Adaptation layer to be removed.
+ * @return 0 on success.
+ */
+int cfcnfg_del_adapt_layer(struct cfcnfg *cnfg, struct layer *adap_layer);
+
+
+/*
+ * Adds an adaptation layer to the CAIF stack.
+ * The adaptation Layer is where the interface to application or higher-level
+ * driver functionality is implemented.
+ *
+ * @param cnfg Pointer to a CAIF configuration object, created by
+ * cfcnfg_create().
+ * @param param Link setup parameters.
+ * @param adap_layer Specify the adaptation layer; the receive and flow-control
+ functions MUST be set in the structure.
+ * @return true on success, false upon failure.
+ */
+bool
+cfcnfg_add_adaptation_layer(struct cfcnfg *cnfg,
+ struct cfctrl_link_param *param,
+ struct layer *adap_layer);
+/*
+ * Get physical ID, given type.
+ * @return Returns one of the physical interfaces matching the given type.
+ * Zero if no match is found.
+ */
+struct dev_info *cfcnfg_get_phyid(struct cfcnfg *cnfg,
+ enum cfcnfg_phy_preference phy_pref);
+int cfcnfg_get_named(struct cfcnfg *cnfg, char *name);
+
+#endif /* CFCNFG_H_ */
diff --git a/include/net/caif/generic/cfctrl.h b/include/net/caif/generic/cfctrl.h
new file mode 100644
index 0000000..5c02de8
--- /dev/null
+++ b/include/net/caif/generic/cfctrl.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef CFCTRL_H_
+#define CFCTRL_H_
+#include <net/caif/generic/caif_layer.h>
+#include <net/caif/generic/cfsrvl.h>
+
+/* CAIF Control packet commands */
+enum cfctrl_cmd {
+ CFCTRL_CMD_LINK_SETUP = 0,
+ CFCTRL_CMD_LINK_DESTROY = 1,
+ CFCTRL_CMD_LINK_ERR = 2,
+ CFCTRL_CMD_ENUM = 3,
+ CFCTRL_CMD_SLEEP = 4,
+ CFCTRL_CMD_WAKE = 5,
+ CFCTRL_CMD_LINK_RECONF = 6,
+ CFCTRL_CMD_START_REASON = 7,
+ CFCTRL_CMD_RADIO_SET = 8,
+ CFCTRL_CMD_MODEM_SET = 9,
+ CFCTRL_CMD_MASK = 0xf
+};
+
+/* Channel types */
+enum cfctrl_srv {
+ CFCTRL_SRV_DECM = 0,
+ CFCTRL_SRV_VEI = 1,
+ CFCTRL_SRV_VIDEO = 2,
+ CFCTRL_SRV_DBG = 3,
+ CFCTRL_SRV_DATAGRAM = 4,
+ CFCTRL_SRV_RFM = 5,
+ CFCTRL_SRV_UTIL = 6,
+ CFCTRL_SRV_MASK = 0xf
+};
+
+#define CFCTRL_RSP_BIT 0x20
+#define CFCTRL_ERR_BIT 0x10
+
+struct cfctrl_rsp {
+ void (*linksetup_rsp)(struct layer *layer, uint8 linkid,
+ enum cfctrl_srv serv, uint8 phyid,
+ struct layer *adapt_layer);
+ void (*linkdestroy_rsp)(struct layer *layer, uint8 linkid,
+ struct layer *client_layer);
+ void (*linkerror_ind)(void);
+ void (*enum_rsp)(void);
+ void (*sleep_rsp)(void);
+ void (*wake_rsp)(void);
+ void (*restart_rsp)(void);
+ void (*radioset_rsp)(void);
+ void (*reject_rsp)(struct layer *layer, uint8 linkid,
+ struct layer *client_layer);;
+};
+
+/* Link Setup Parameters for CAIF-Links. */
+struct cfctrl_link_param {
+ enum cfctrl_srv linktype;/* (T3,T0) Type of Channel */
+ uint8 priority; /* (P4,P0) Priority of the channel */
+ uint8 phyid; /* (U2-U0) Physical interface to connect */
+ uint8 endpoint; /* (E1,E0) Endpoint for data channels */
+ uint8 chtype; /* (H1,H0) Channel-Type, applies to
+ * VEI, DEBUG */
+ union {
+ struct {
+ uint8 connid; /* (D7,D0) Video LinkId */
+ } video;
+
+ struct {
+ uint32 connid; /* (N31,Ngit0) Connection ID used
+ * for Datagram */
+ } datagram;
+
+ struct {
+ uint32 connid; /* Connection ID used for RFM */
+ char volume[20]; /* Volume to mount for RFM */
+ } rfm; /* Configuration for RFM */
+
+ struct {
+ uint16 fifosize_kb; /* Psock FIFO size in KB */
+ uint16 fifosize_bufs; /* Psock # signal buffers */
+ char name[16]; /* Name of the PSOCK service */
+ uint8 params[255]; /* Link setup Parameters> */
+ uint16 paramlen; /* Length of Link Setup
+ * Parameters */
+ } utility; /* Configuration for Utility Links (Psock) */
+ } u;
+};
+
+/* This structure is used internally in CFCTRL */
+struct cfctrl_request_info {
+ int sequence_no;
+ enum cfctrl_cmd cmd;
+ uint8 channel_id;
+ struct cfctrl_link_param param;
+ struct cfctrl_request_info *next;
+ struct layer *client_layer;
+};
+
+struct cfctrl {
+ struct cfsrvl serv;
+ struct cfctrl_rsp res;
+ cfglu_atomic_t req_seq_no;
+ cfglu_atomic_t rsp_seq_no;
+ struct cfctrl_request_info *first_req;
+ cfglu_lock_t info_list_lock;
+#ifndef CAIF_NO_LOOP
+ uint8 loop_linkid;
+ int loop_linkused[256];
+ cfglu_lock_t loop_linkid_lock;
+#endif
+
+};
+
+void cfctrl_enum_req(struct layer *cfctrl, uint8 physlinkid);
+void cfctrl_linkup_request(struct layer *cfctrl,
+ struct cfctrl_link_param *param,
+ struct layer *user_layer);
+int cfctrl_linkdown_req(struct layer *cfctrl, uint8 linkid,
+ struct layer *client);
+void cfctrl_sleep_req(struct layer *cfctrl);
+void cfctrl_wake_req(struct layer *cfctrl);
+void cfctrl_getstartreason_req(struct layer *cfctrl);
+struct layer *cfctrl_create(void);
+void cfctrl_set_dnlayer(struct layer *this, struct layer *dn);
+void cfctrl_set_uplayer(struct layer *this, struct layer *up);
+struct cfctrl_rsp *cfctrl_get_respfuncs(struct layer *layer);
+bool cfctrl_req_eq(struct cfctrl_request_info *r1,
+ struct cfctrl_request_info *r2);
+void cfctrl_insert_req(struct cfctrl *ctrl,
+ struct cfctrl_request_info *req);
+struct cfctrl_request_info *cfctrl_remove_req(struct cfctrl *ctrl,
+ struct cfctrl_request_info *req);
+#endif /* CFCTRL_H_ */
diff --git a/include/net/caif/generic/cffrml.h b/include/net/caif/generic/cffrml.h
new file mode 100644
index 0000000..83aab48
--- /dev/null
+++ b/include/net/caif/generic/cffrml.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef CFFRML_H_
+#define CFFRML_H_
+#include <net/caif/generic/cfglue.h>
+#include <net/caif/generic/caif_layer.h>
+#include <net/caif/generic/cflst.h>
+
+struct cffrml;
+struct layer *cffrml_create(uint16 phyid, bool DoFCS);
+void cffrml_set_uplayer(struct layer *this, struct layer *up);
+void cffrml_set_dnlayer(struct layer *this, struct layer *dn);
+void cffrml_destroy(struct layer *layer);
+
+#endif /* CFFRML_H_ */
diff --git a/include/net/caif/generic/cfglue.h b/include/net/caif/generic/cfglue.h
new file mode 100644
index 0000000..3b573f6
--- /dev/null
+++ b/include/net/caif/generic/cfglue.h
@@ -0,0 +1,110 @@
+/*
+ * This file contains the OS and HW dependencies for CAIF.
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef CFGLUE_H_
+#define CFGLUE_H_
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
+#include <linux/hardirq.h>
+#include <linux/stddef.h>
+#include <linux/types.h>
+#include <linux/crc-ccitt.h>
+
+/* Unsigned 8 bit */
+typedef __u8 uint8;
+
+/* Unsigned 16 bit */
+typedef __u16 uint16;
+
+/* Unsigned 32 bit */
+typedef __u32 uint32;
+
+/*
+ * Handling endiannes:
+ * CAIF uses little-endian byte order.
+ */
+
+/* CAIF Endian handling Net to Host of 16 bits unsigned */
+#define cfglu_le16_to_cpu(v) le16_to_cpu(v)
+
+/* CAIF Endian handling Host to Net of 16 bits unsigned */
+#define cfglu_cpu_to_le16(v) cpu_to_le16(v)
+
+/* CAIF Endian handling Host to Net of 32 bits unsigned */
+#define cfglu_le32_to_cpu(v) le32_to_cpu(v)
+
+/* CAIF Endian handling Net to Host of 32 bits unsigned */
+#define cfglu_cpu_to_le32(v) cpu_to_le32(v)
+
+
+/* Critical Section support, one thread only between startsync
+ * and endsync
+ */
+#define cfglu_lock_t spinlock_t
+#define cfglu_init_lock(sync) spin_lock_init(&(sync))
+#define cfglu_lock(sync) spin_lock(&(sync))
+#define cfglu_unlock(sync) spin_unlock(&(sync))
+#define cfglu_deinit_lock(sync)
+
+/* Atomic counting */
+#define cfglu_atomic_t atomic_t
+#define cfglu_atomic_read(a) atomic_read(&a)
+#define cfglu_atomic_set(a, val) atomic_set(&a, val)
+#define cfglu_atomic_inc(a) atomic_inc(&a)
+#define cfglu_atomic_dec(a) atomic_dec(&a)
+
+/* HEAP */
+static inline void *cfglu_alloc(size_t size)
+{
+ if (in_interrupt())
+ return kmalloc(size, GFP_ATOMIC);
+ else
+ return kmalloc(size, GFP_KERNEL);
+}
+
+#define cfglu_free(ptr) kfree(ptr)
+#define cfglu_container_of(p, t, m) container_of(p, t, m)
+
+/* Checksum */
+static inline uint16 fcs16(uint16 fcs, uint8 *cp, uint16 len)
+{
+ return crc_ccitt(fcs, cp, len);
+}
+
+
+#ifndef caif_assert
+#define caif_assert(assert)\
+do if (!(assert)) { \
+ pr_err("caif:Assert detected:'%s'\n", #assert); \
+ WARN_ON(!(assert));\
+} while (0)
+#endif
+
+/*FIXME: Comment error codes*/
+enum cfglu_errno {
+ CFGLU_EOK = 0,
+ CFGLU_EPKT = -EPROTO,
+ CFGLU_EADDRINUSE = -EADDRINUSE,
+ CFGLU_EIO = -EIO,
+ CFGLU_EFCS = -EILSEQ,
+ CFGLU_EBADPARAM = -EINVAL,
+ CFGLU_EINVAL = -EINVAL,
+ CFGLU_ENODEV = -ENODEV,
+ CFGLU_ENOTCONN = -ENOTCONN,
+ CFGLU_EPROTO = -EPROTO,
+ CFGLU_EOVERFLOW = -EOVERFLOW,
+ CFGLU_ENOMEM = -ENOMEM,
+ CFGLU_ERETRY = -EAGAIN,
+ CFGLU_ENOSPC = -ENOSPC,
+ CFGLU_ENXIO = -ENXIO
+};
+
+#endif /* CFGLUE_H_ */
diff --git a/include/net/caif/generic/cflst.h b/include/net/caif/generic/cflst.h
new file mode 100644
index 0000000..6ff7168
--- /dev/null
+++ b/include/net/caif/generic/cflst.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef CFLST_H_
+#define CFLST_H_
+
+#include <net/caif/generic/cfglue.h>
+
+int cflst_put(struct layer **lst, uint8 id, struct layer *node);
+struct layer *cflst_get(struct layer **lst, uint8 id);
+struct layer *cflst_del(struct layer **lst, uint8 id);
+#define CFLST_FIRST(lst) lst
+#define CFLST_MORE(node) ((node) != NULL)
+#define CFLST_NEXT(node) ((node)->next)
+void cflst_init(struct layer **lst);
+#endif /* CFLST_H_ */
diff --git a/include/net/caif/generic/cfmuxl.h b/include/net/caif/generic/cfmuxl.h
new file mode 100644
index 0000000..2520693
--- /dev/null
+++ b/include/net/caif/generic/cfmuxl.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef CFMUXL_H_
+#define CFMUXL_H_
+#include <net/caif/generic/caif_layer.h>
+
+struct cfsrvl;
+struct cffrml;
+
+struct layer *cfmuxl_create(void);
+int cfmuxl_set_uplayer(struct layer *layr, struct layer *up, uint8 linkid);
+struct layer *cfmuxl_remove_dnlayer(struct layer *layr, uint8 phyid);
+int cfmuxl_set_dnlayer(struct layer *layr, struct layer *up, uint8 phyid);
+struct layer *cfmuxl_remove_uplayer(struct layer *layr, uint8 linkid);
+bool cfmuxl_is_phy_inuse(struct layer *layr, uint8 phyid);
+uint8 cfmuxl_get_phyid(struct layer *layr, uint8 channel_id);
+
+#endif /* CFMUXL_H_ */
diff --git a/include/net/caif/generic/cfpkt.h b/include/net/caif/generic/cfpkt.h
new file mode 100644
index 0000000..adaa84a
--- /dev/null
+++ b/include/net/caif/generic/cfpkt.h
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef CFPKT_H_
+#define CFPKT_H_
+#include <net/caif/generic/caif_layer.h>
+
+struct cfpkt;
+
+/* Create a CAIF packet.
+ * len: Length of packet to be created
+ * @return New packet.
+ */
+struct cfpkt *cfpkt_create(uint16 len);
+
+/* Create a CAIF packet.
+ * data Data to copy.
+ * len Length of packet to be created
+ * @return New packet.
+ */
+struct cfpkt *cfpkt_create_uplink(const unsigned char *data, unsigned int len);
+/*
+ * Destroy a CAIF Packet.
+ * pkt Packet to be destoyed.
+ */
+void cfpkt_destroy(struct cfpkt *pkt);
+
+/*
+ * Extract header from packet.
+ *
+ * pkt Packet to extract header data from.
+ * data Pointer to copy the header data into.
+ * len Length of head data to copy.
+ * @return zero on success and error code upon failure
+ */
+int cfpkt_extr_head(struct cfpkt *pkt, void *data, uint16 len);
+
+/*
+ * Peek header from packet.
+ * Reads data from packet without changing packet.
+ *
+ * pkt Packet to extract header data from.
+ * data Pointer to copy the header data into.
+ * len Length of head data to copy.
+ * @return zero on success and error code upon failure
+ */
+int cfpkt_peek_head(struct cfpkt *pkt, void *data, uint16 len);
+
+/*
+ * Extract header from trailer (end of packet).
+ *
+ * pkt Packet to extract header data from.
+ * data Pointer to copy the trailer data into.
+ * len Length of header data to copy.
+ * @return zero on success and error code upon failure
+ */
+int cfpkt_extr_trail(struct cfpkt *pkt, void *data, uint16 len);
+
+/*
+ * Add header to packet.
+ *
+ *
+ * pkt Packet to add header data to.
+ * data Pointer to data to copy into the header.
+ * len Length of header data to copy.
+ * @return zero on success and error code upon failure
+ */
+int cfpkt_add_head(struct cfpkt *pkt, const void *data, uint16 len);
+
+/*
+ * Add trailer to packet.
+ *
+ *
+ * pkt Packet to add trailer data to.
+ * data Pointer to data to copy into the trailer.
+ * len Length of trailer data to copy.
+ * @return zero on success and error code upon failure
+ */
+int cfpkt_add_trail(struct cfpkt *pkt, const void *data, uint16 len);
+
+/*
+ * Pad trailer on packet.
+ * Moves data pointer in packet, no content copied.
+ *
+ * pkt Packet in which to pad trailer.
+ * len Length of padding to add.
+ * @return zero on success and error code upon failure
+ */
+int cfpkt_pad_trail(struct cfpkt *pkt, uint16 len);
+
+/*
+ * Add a single byte to packet body (tail).
+ *
+ * pkt Packet in which to add byte.
+ * data Byte to add.
+ * @return zero on success and error code upon failure
+ */
+int cfpkt_addbdy(struct cfpkt *pkt, const uint8 data);
+
+/*
+ * Add a data to packet body (tail).
+ *
+ * pkt Packet in which to add data.
+ * data Pointer to data to copy into the packet body.
+ * len Length of data to add.
+ * @return zero on success and error code upon failure
+ */
+int cfpkt_add_body(struct cfpkt *pkt, const void *data, uint16 len);
+
+/*
+ * Checks whether there are more data to process in packet.
+ * pkt Packet to check.
+ * @return true if more data are available in packet false otherwise
+ */
+bool cfpkt_more(struct cfpkt *pkt);
+
+/*
+ * Checks whether the packet is erroneous,
+ * i.e. if it has been attempted to extract more data than available in packet
+ * or writing more data than has been allocated in cfpkt_create().
+ * pkt Packet to check.
+ * @return true on error false otherwise
+ */
+bool cfpkt_erroneous(struct cfpkt *pkt);
+
+/*
+ * Get the packet length.
+ * pkt Packet to get length from.
+ * @return Number of bytes in packet.
+ */
+uint16 cfpkt_getlen(struct cfpkt *pkt);
+
+/*
+ * Set the packet length, by adjusting the trailer pointer according to length.
+ * pkt Packet to set length.
+ * len Packet length.
+ * @return Number of bytes in packet.
+ */
+int cfpkt_setlen(struct cfpkt *pkt, uint16 len);
+
+/*
+ * cfpkt_append - Appends a packet's data to another packet.
+ * dstpkt: Packet to append data into, WILL BE FREED BY THIS FUNCTION
+ * addpkt: Packet to be appended and automatically released,
+ * WILL BE FREED BY THIS FUNCTION.
+ * expectlen: Packet's expected total length. This should be considered
+ * as a hint.
+ * NB: Input packets will be destroyed after appending and cannot be used
+ * after calling this function.
+ * @return The new appended packet.
+ */
+struct cfpkt *cfpkt_append(struct cfpkt *dstpkt, struct cfpkt *addpkt,
+ uint16 expectlen);
+
+/*
+ * cfpkt_split - Split a packet into two packets at the specified split point.
+ * pkt: Packet to be split (will contain the first part of the data on exit)
+ * pos: Position to split packet in two parts.
+ * @return The new packet, containing the second part of the data.
+ */
+struct cfpkt *cfpkt_split(struct cfpkt *pkt, uint16 pos);
+
+/*
+ * Iteration function, iterates the packet buffers from start to end.
+ *
+ * Checksum iteration function used to iterate buffers
+ * (we may have packets consisting of a chain of buffers)
+ * pkt: Packet to calculate checksum for
+ * iter_func: Function pointer to iteration function
+ * chks: Checksum calculated so far.
+ * buf: Pointer to the buffer to checksum
+ * len: Length of buf.
+ * data: Initial checksum value.
+ * @return Checksum of buffer.
+ */
+
+uint16 cfpkt_iterate(struct cfpkt *pkt,
+ uint16 (*iter_func)(uint16 chks, void *buf, uint16 len),
+ uint16 data);
+
+/* Append by giving user access to packet buffer
+ * cfpkt Packet to append to
+ * buf Buffer inside pkt that user shall copy data into
+ * buflen Length of buffer and number of bytes added to packet
+ * @return 0 on error, 1 on success
+ */
+int cfpkt_raw_append(struct cfpkt *cfpkt, void **buf, unsigned int buflen);
+
+/* Extract by giving user access to packet buffer
+ * cfpkt Packet to extract from
+ * buf Buffer inside pkt that user shall copy data from
+ * buflen Length of buffer and number of bytes removed from packet
+ * @return 0 on error, 1 on success
+ */
+int cfpkt_raw_extract(struct cfpkt *cfpkt, void **buf, unsigned int buflen);
+
+/* Map from a "native" packet (e.g. Linux Socket Buffer) to a CAIF packet.
+ * dir - Direction indicating whether this packet is to be sent or received.
+ * nativepkt - The native packet to be transformed to a CAIF packet
+ * @return The mapped CAIF Packet CFPKT.
+ */
+struct cfpkt *cfpkt_fromnative(enum caif_direction dir, void *nativepkt);
+
+/* Map from a CAIF packet to a "native" packet (e.g. Linux Socket Buffer).
+ * pkt - The CAIF packet to be transformed into a "native" packet.
+ * @return The native packet transformed from a CAIF packet.
+ */
+void *cfpkt_tonative(struct cfpkt *pkt);
+
+/*
+ * Insert a packet in the packet queue.
+ * pktq Packet queue to insert into
+ * pkt Packet to be inserted in queue
+ * prio Priority of packet
+ */
+void cfpkt_queue(struct cfpktq *pktq, struct cfpkt *pkt,
+ unsigned short prio);
+
+/*
+ * Remove a packet from the packet queue.
+ * pktq Packet queue to fetch packets from.
+ * @return Dequeued packet.
+ */
+struct cfpkt *cfpkt_dequeue(struct cfpktq *pktq);
+
+/*
+ * Peek into a packet from the packet queue.
+ * pktq Packet queue to fetch packets from.
+ * @return Peeked packet.
+ */
+struct cfpkt *cfpkt_qpeek(struct cfpktq *pktq);
+
+/*
+ * Initiates the packet queue.
+ * @return Pointer to new packet queue.
+ */
+struct cfpktq *cfpktq_create(void);
+
+/*
+ * Get the number of packets in the queue.
+ * pktq Packet queue to fetch count from.
+ * @return Number of packets in queue.
+ */
+int cfpkt_qcount(struct cfpktq *pktq);
+
+/*
+ * Put content of packet into buffer for debuging purposes.
+ * pkt Packet to copy data from
+ * buf Buffer to copy data into
+ * buflen Length of data to copy
+ * @return Pointer to copied data
+ */
+char *cfpkt_log_pkt(struct cfpkt *pkt, char *buf, int buflen);
+
+/*
+ * Clones a packet and releases the original packet.
+ * This is used for taking ownership of a packet e.g queueing.
+ * pkt Packet to clone and release.
+ * @return Cloned packet.
+ */
+struct cfpkt *cfpkt_clone_release(struct cfpkt *pkt);
+
+
+/*
+ * Returns packet information for a packet.
+ * pkt Packet to get info from;
+ * @return Packet information
+ */
+struct payload_info *cfpkt_info(struct cfpkt *pkt);
+/*! @} */
+#endif /* CFPKT_H_ */
diff --git a/include/net/caif/generic/cfserl.h b/include/net/caif/generic/cfserl.h
new file mode 100644
index 0000000..715c82d
--- /dev/null
+++ b/include/net/caif/generic/cfserl.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef CFSERL_H_
+#define CFSERL_H_
+#include <net/caif/generic/caif_layer.h>
+#include <net/caif/generic/cfglue.h>
+
+struct layer *cfserl_create(int type, int instance, bool use_stx);
+
+#endif /* CFSERL_H_ */
diff --git a/include/net/caif/generic/cfsrvl.h b/include/net/caif/generic/cfsrvl.h
new file mode 100644
index 0000000..f5a83f8
--- /dev/null
+++ b/include/net/caif/generic/cfsrvl.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef CFSRVL_H_
+#define CFSRVL_H_
+#include <net/caif/generic/cfglue.h>
+#include <stddef.h>
+
+struct cfsrvl {
+ struct layer layer;
+ bool open;
+ bool phy_flow_on;
+ bool modem_flow_on;
+ struct dev_info dev_info;
+};
+
+struct layer *cfvei_create(uint8 linkid, struct dev_info *dev_info);
+struct layer *cfdgml_create(uint8 linkid, struct dev_info *dev_info);
+struct layer *cfutill_create(uint8 linkid, struct dev_info *dev_info);
+struct layer *cfvidl_create(uint8 linkid, struct dev_info *dev_info);
+struct layer *cfrfml_create(uint8 linkid, struct dev_info *dev_info);
+bool cfsrvl_phyid_match(struct layer *layer, int phyid);
+void cfservl_destroy(struct layer *layer);
+void cfsrvl_init(struct cfsrvl *service,
+ uint8 channel_id,
+ struct dev_info *dev_info);
+bool cfsrvl_ready(struct cfsrvl *service, int *err);
+uint8 cfsrvl_getphyid(struct layer *layer);
+
+#endif /* CFSRVL_H_ */
--
1.6.3.3
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PATCH net-next-2.6 04/13] net-caif: add CAIF header files
2010-01-20 22:55 [PATCH net-next-2.6 00/13] net-caif: introducing CAIF protocol stack sjur.brandeland
` (2 preceding siblings ...)
2010-01-20 22:55 ` [PATCH net-next-2.6 03/13] net-caif: add CAIF generic protocol stack " sjur.brandeland
@ 2010-01-20 22:55 ` sjur.brandeland
2010-01-20 22:55 ` [PATCH net-next-2.6 05/13] net-caif: add CAIF generic protocol stack sjur.brandeland
` (9 subsequent siblings)
13 siblings, 0 replies; 47+ messages in thread
From: sjur.brandeland @ 2010-01-20 22:55 UTC (permalink / raw)
To: netdev; +Cc: davem, marcel, stefano.babic, randy.dunlap, Sjur Braendeland
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
Header files for CAIF Socket, Net Device,
and link-layer registration.
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
include/net/caif/caif_dev.h | 29 +++++++++++++++++++++++++++++
include/net/caif/caif_device.h | 34 ++++++++++++++++++++++++++++++++++
2 files changed, 63 insertions(+), 0 deletions(-)
diff --git a/include/net/caif/caif_dev.h b/include/net/caif/caif_dev.h
new file mode 100644
index 0000000..eee7f3a
--- /dev/null
+++ b/include/net/caif/caif_dev.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/ sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef CAIF_DEV_H_
+#define CAIF_DEV_H_
+
+#include <net/caif/generic/caif_layer.h>
+#include <net/caif/generic/cfcnfg.h>
+#include <linux/caif/caif_config.h>
+#include <linux/if.h>
+
+struct caif_service_config;
+int caifdev_adapt_register(struct caif_channel_config *config,
+ struct layer *adap_layer);
+int caifdev_adapt_unregister(struct layer *adap_layer);
+struct cfcnfg *get_caif_conf(void);
+void caif_register_ioctl(int (*ioctl)(unsigned int cmd,
+ unsigned long arg,
+ bool));
+int caif_ioctl(unsigned int cmd, unsigned long arg, bool from_use_land);
+void caif_unregister_netdev(void);
+int channel_config_2_link_param(struct cfcnfg *cnfg,
+ struct caif_channel_config *s,
+ struct cfctrl_link_param *l);
+
+#endif /* CAIF_DEV_H_ */
diff --git a/include/net/caif/caif_device.h b/include/net/caif/caif_device.h
new file mode 100644
index 0000000..6752bb7
--- /dev/null
+++ b/include/net/caif/caif_device.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/ sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef CAIF_DEVICE_H_
+#define CAIF_DEVICE_H_
+#include <linux/kernel.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/caif/caif_socket.h>
+#include <net/caif/caif_device.h>
+
+/**
+ * struct caif_dev_common - shared between CAIF drivers and stack .
+ * @flowctrl: Flow Control callback function.
+ * @link_select: Indicate bandwidth and latency of device.
+ * @use_frag: CAIF Frames may be framented.
+ * @use_fcs: Indicate if Frame CheckSum (fcs) is used.
+ * @use_stx: Indicate STart of frame eXtension (stx) in use.
+ *
+ * This structure is shared between the CAIF drivers and the CAIF stack.
+ */
+struct caif_dev_common {
+ void (*flowctrl)(struct net_device *net, int on);
+ enum caif_link_selector link_select;
+ int use_frag;
+ int use_fcs;
+ int use_stx;
+};
+
+#endif /* CAIF_DEVICE_H_ */
+
--
1.6.3.3
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PATCH net-next-2.6 05/13] net-caif: add CAIF generic protocol stack
2010-01-20 22:55 [PATCH net-next-2.6 00/13] net-caif: introducing CAIF protocol stack sjur.brandeland
` (3 preceding siblings ...)
2010-01-20 22:55 ` [PATCH net-next-2.6 04/13] net-caif: add CAIF " sjur.brandeland
@ 2010-01-20 22:55 ` sjur.brandeland
2010-01-20 22:55 ` [PATCH net-next-2.6 06/13] net-caif: add CAIF generic caif support functions sjur.brandeland
` (8 subsequent siblings)
13 siblings, 0 replies; 47+ messages in thread
From: sjur.brandeland @ 2010-01-20 22:55 UTC (permalink / raw)
To: netdev; +Cc: davem, marcel, stefano.babic, randy.dunlap, Sjur Braendeland
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
CAIF generic protocol implementation. This layer is
somewhat generic in order to be able to use and test it outside
the Linux Kernel.
cfctrl.c - CAIF control protocol layer
cfdgml.c - CAIF datagram protocol layer
cffrml.c - CAIF framing protocol layer
cfmuxl.c - CAIF mux protocol layer
cfrfml.c - CAIF remote file manager protocol layer
cfserl.c - CAIF serial (fragmentation) protocol layer
cfsrvl.c - CAIF generic service layer functions
cfutill.c - CAIF utility protocol layer
cfveil.c - CAIF AT protocol layer
cfvidl.c - CAIF video protocol layer
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
net/caif/generic/cfctrl.c | 699 ++++++++++++++++++++++++++++++++++++++++++++
net/caif/generic/cfdgml.c | 106 +++++++
net/caif/generic/cffrml.c | 148 ++++++++++
net/caif/generic/cfmuxl.c | 225 ++++++++++++++
net/caif/generic/cfrfml.c | 104 +++++++
net/caif/generic/cfserl.c | 198 +++++++++++++
net/caif/generic/cfsrvl.c | 182 ++++++++++++
net/caif/generic/cfutill.c | 112 +++++++
net/caif/generic/cfveil.c | 106 +++++++
net/caif/generic/cfvidl.c | 62 ++++
10 files changed, 1942 insertions(+), 0 deletions(-)
diff --git a/net/caif/generic/cfctrl.c b/net/caif/generic/cfctrl.c
new file mode 100644
index 0000000..d2e0034
--- /dev/null
+++ b/net/caif/generic/cfctrl.c
@@ -0,0 +1,699 @@
+/*
+ * 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/caif_layer.h>
+#include <net/caif/generic/cfpkt.h>
+#include <net/caif/generic/cfctrl.h>
+
+#define container_obj(layr) cfglu_container_of(layr, struct cfctrl, serv.layer)
+#define UTILITY_NAME_LENGTH 16
+#define CFPKT_CTRL_PKT_LEN 20
+
+
+#ifdef CAIF_NO_LOOP
+static inline int handle_loop(struct cfctrl *ctrl,
+ int cmd, struct cfpkt *pkt){
+ return CAIF_FAILURE;
+}
+#else
+static int handle_loop(struct cfctrl *ctrl,
+ int cmd, struct cfpkt *pkt);
+#endif
+static int cfctrl_recv(struct layer *layr, struct cfpkt *pkt);
+static void cfctrl_ctrlcmd(struct layer *layr, enum caif_ctrlcmd ctrl,
+ int phyid);
+
+
+struct layer *cfctrl_create()
+{
+ struct cfctrl *this =
+ (struct cfctrl *) cfglu_alloc(sizeof(struct cfctrl));
+ if (!this) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return NULL;
+ }
+ caif_assert(offsetof(struct cfctrl, serv.layer) == 0);
+ memset(this, 0, sizeof(*this));
+ cfglu_init_lock(this->info_list_lock);
+ cfglu_atomic_set(this->req_seq_no, 1);
+ cfglu_atomic_set(this->rsp_seq_no, 1);
+ this->serv.dev_info.id = 0xff;
+ this->serv.layer.id = 0;
+ this->serv.layer.receive = cfctrl_recv;
+ sprintf(this->serv.layer.name, "ctrl");
+ this->serv.layer.ctrlcmd = cfctrl_ctrlcmd;
+ cfglu_init_lock(this->loop_linkid_lock);
+ this->loop_linkid = 1;
+ return &this->serv.layer;
+}
+
+bool param_eq(struct cfctrl_link_param *p1, struct cfctrl_link_param *p2)
+{
+ bool eq =
+ p1->linktype == p2->linktype &&
+ p1->priority == p2->priority &&
+ p1->phyid == p2->phyid &&
+ p1->endpoint == p2->endpoint && p1->chtype == p2->chtype;
+
+ if (!eq)
+ return false;
+
+ switch (p1->linktype) {
+ case CFCTRL_SRV_VEI:
+ return true;
+ case CFCTRL_SRV_DATAGRAM:
+ return p1->u.datagram.connid == p2->u.datagram.connid;
+ case CFCTRL_SRV_RFM:
+ return
+ p1->u.rfm.connid == p2->u.rfm.connid &&
+ strcmp(p1->u.rfm.volume, p2->u.rfm.volume) == 0;
+ case CFCTRL_SRV_UTIL:
+ return
+ p1->u.utility.fifosize_kb == p2->u.utility.fifosize_kb
+ && p1->u.utility.fifosize_bufs ==
+ p2->u.utility.fifosize_bufs
+ && strcmp(p1->u.utility.name, p2->u.utility.name) == 0
+ && p1->u.utility.paramlen == p2->u.utility.paramlen
+ && memcmp(p1->u.utility.params, p2->u.utility.params,
+ p1->u.utility.paramlen) == 0;
+
+ case CFCTRL_SRV_VIDEO:
+ return p1->u.video.connid == p2->u.video.connid;
+ case CFCTRL_SRV_DBG:
+ return true;
+ case CFCTRL_SRV_DECM:
+ return false;
+ default:
+ return false;
+ }
+ return false;
+}
+
+bool cfctrl_req_eq(struct cfctrl_request_info *r1,
+ struct cfctrl_request_info *r2)
+{
+ if (r1->cmd != r2->cmd)
+ return false;
+ if (r1->cmd == CFCTRL_CMD_LINK_SETUP)
+ return param_eq(&r1->param, &r2->param);
+ else
+ return r1->channel_id == r2->channel_id;
+}
+
+/* Insert request at the end */
+void cfctrl_insert_req(struct cfctrl *ctrl,
+ struct cfctrl_request_info *req)
+{
+ struct cfctrl_request_info *p;
+ cfglu_lock(ctrl->info_list_lock);
+ req->next = NULL;
+ cfglu_atomic_inc(ctrl->req_seq_no);
+ req->sequence_no = cfglu_atomic_read(ctrl->req_seq_no);
+ if (ctrl->first_req == NULL) {
+ ctrl->first_req = req;
+ cfglu_unlock(ctrl->info_list_lock);
+ return;
+ }
+ p = ctrl->first_req;
+ while (p->next != NULL)
+ p = p->next;
+ p->next = req;
+ cfglu_unlock(ctrl->info_list_lock);
+}
+
+static void cfctrl_insert_req2(struct cfctrl *ctrl, enum cfctrl_cmd cmd,
+ uint8 linkid, struct layer *user_layer)
+{
+ struct cfctrl_request_info *req = cfglu_alloc(sizeof(*req));
+ if (!req) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return;
+ }
+ req->client_layer = user_layer;
+ req->cmd = cmd;
+ req->channel_id = linkid;
+ cfctrl_insert_req(ctrl, req);
+}
+
+/* Compare and remove request */
+struct cfctrl_request_info *cfctrl_remove_req(struct cfctrl *ctrl,
+ struct cfctrl_request_info *req)
+{
+ struct cfctrl_request_info *p;
+ struct cfctrl_request_info *ret;
+
+ cfglu_lock(ctrl->info_list_lock);
+ if (ctrl->first_req == NULL) {
+ cfglu_unlock(ctrl->info_list_lock);
+ return NULL;
+ }
+
+ if (cfctrl_req_eq(req, ctrl->first_req)) {
+ ret = ctrl->first_req;
+ cfglu_atomic_set(ctrl->rsp_seq_no,
+ ctrl->first_req->sequence_no);
+ ctrl->first_req = ctrl->first_req->next;
+ cfglu_unlock(ctrl->info_list_lock);
+ return ret;
+ }
+
+ pr_warning("CAIF: %s(): Requests are not received in order/matching\n",
+ __func__);
+
+ p = ctrl->first_req;
+
+ while (p->next != NULL) {
+ if (cfctrl_req_eq(req, p->next)) {
+ ret = p->next;
+ cfglu_atomic_set(ctrl->rsp_seq_no,
+ p->next->sequence_no);
+ p = p->next;
+ cfglu_unlock(ctrl->info_list_lock);
+ return ret;
+ }
+ p = p->next;
+ }
+ cfglu_unlock(ctrl->info_list_lock);
+ return NULL;
+}
+
+/* Compare and remove old requests based on sequence no. */
+void cfctrl_prune_req(struct cfctrl *ctrl)
+{
+ struct cfctrl_request_info *p;
+ struct cfctrl_request_info *del;
+
+ cfglu_lock(ctrl->info_list_lock);
+ if (ctrl->first_req == NULL) {
+ cfglu_unlock(ctrl->info_list_lock);
+ return;
+ }
+
+ if (ctrl->first_req->sequence_no <
+ cfglu_atomic_read(ctrl->req_seq_no)) {
+ del = ctrl->first_req;
+ ctrl->first_req = ctrl->first_req->next;
+ cfglu_free(del);
+ }
+ p = ctrl->first_req;
+ while (p->next != NULL) {
+ if (p->next->sequence_no <
+ cfglu_atomic_read(ctrl->rsp_seq_no)) {
+ del = p->next;
+ p = p->next;
+ cfglu_atomic_set(ctrl->rsp_seq_no,
+ ctrl->first_req->sequence_no);
+ cfglu_free(del);
+ }
+ p = p->next;
+ }
+ cfglu_unlock(ctrl->info_list_lock);
+}
+
+struct cfctrl_rsp *cfctrl_get_respfuncs(struct layer *layer)
+{
+ struct cfctrl *this = container_obj(layer);
+ return &this->res;
+}
+
+void cfctrl_set_dnlayer(struct layer *this, struct layer *dn)
+{
+ this->dn = dn;
+}
+
+void cfctrl_set_uplayer(struct layer *this, struct layer *up)
+{
+ this->up = up;
+}
+
+void init_info(struct payload_info *info, struct cfctrl *cfctrl)
+{
+ info->hdr_len = 0;
+ info->channel_id = cfctrl->serv.layer.id;
+ info->dev_info = &cfctrl->serv.dev_info;
+}
+
+void cfctrl_enum_req(struct layer *layer, uint8 physlinkid)
+{
+ struct cfctrl *cfctrl = container_obj(layer);
+ int ret;
+ struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
+ if (!pkt) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return;
+ }
+ caif_assert(offsetof(struct cfctrl, serv.layer) == 0);
+ init_info(cfpkt_info(pkt), cfctrl);
+ cfpkt_info(pkt)->dev_info->id = physlinkid;
+ cfctrl->serv.dev_info.id = physlinkid;
+ cfpkt_addbdy(pkt, CFCTRL_CMD_ENUM);
+ cfpkt_addbdy(pkt, physlinkid);
+ ret =
+ cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
+ if (ret < 0) {
+ pr_err("CAIF: %s(): Could not transmit enum message\n",
+ __func__);
+ cfpkt_destroy(pkt);
+ }
+}
+
+void cfctrl_linkup_request(struct layer *layer, struct cfctrl_link_param *param,
+ struct layer *user_layer)
+{
+ struct cfctrl *cfctrl = container_obj(layer);
+ uint32 tmp32;
+ uint16 tmp16;
+ uint8 tmp8;
+ struct cfctrl_request_info *req;
+ int ret;
+ char utility_name[16];
+ struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
+ if (!pkt) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return;
+ }
+ cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_SETUP);
+ cfpkt_addbdy(pkt, (param->chtype << 4) + param->linktype);
+ cfpkt_addbdy(pkt, (param->priority << 3) + param->phyid);
+ cfpkt_addbdy(pkt, param->endpoint & 0x03);
+ switch (param->linktype) {
+ case CFCTRL_SRV_VEI:
+ break;
+ case CFCTRL_SRV_VIDEO:
+ cfpkt_addbdy(pkt, (uint8) param->u.video.connid);
+ break;
+ case CFCTRL_SRV_DBG:
+ break;
+ case CFCTRL_SRV_DATAGRAM:
+ tmp32 = cfglu_cpu_to_le32(param->u.datagram.connid);
+ cfpkt_add_body(pkt, &tmp32, 4);
+ break;
+ case CFCTRL_SRV_RFM:
+ /* Construct a frame, convert DatagramConnectionID to network
+ * format long and copy it out...
+ */
+ tmp32 = cfglu_cpu_to_le32(param->u.rfm.connid);
+ cfpkt_add_body(pkt, &tmp32, 4);
+ /* Add volume name, including zero termination... */
+ cfpkt_add_body(pkt, param->u.rfm.volume,
+ strlen(param->u.rfm.volume) + 1);
+ break;
+ case CFCTRL_SRV_UTIL:
+ tmp16 = cfglu_cpu_to_le16(param->u.utility.fifosize_kb);
+ cfpkt_add_body(pkt, &tmp16, 2);
+ tmp16 = cfglu_cpu_to_le16(param->u.utility.fifosize_bufs);
+ cfpkt_add_body(pkt, &tmp16, 2);
+ memset(utility_name, 0, sizeof(utility_name));
+ strncpy(utility_name, param->u.utility.name,
+ UTILITY_NAME_LENGTH - 1);
+ cfpkt_add_body(pkt, utility_name, UTILITY_NAME_LENGTH);
+ tmp8 = param->u.utility.paramlen;
+ cfpkt_add_body(pkt, &tmp8, 1);
+ cfpkt_add_body(pkt, param->u.utility.params,
+ param->u.utility.paramlen);
+ break;
+ default:
+ pr_warning("CAIF: %s():Request setup of bad link type = %d\n",
+ __func__, param->linktype);
+ }
+ req = cfglu_alloc(sizeof(*req));
+ if (!req) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return;
+ }
+ memset(req, 0, sizeof(*req));
+ req->client_layer = user_layer;
+ req->cmd = CFCTRL_CMD_LINK_SETUP;
+ req->param = *param;
+ cfctrl_insert_req(cfctrl, req);
+ init_info(cfpkt_info(pkt), cfctrl);
+ cfpkt_info(pkt)->dev_info->id = param->phyid;
+ ret =
+ cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
+ if (ret < 0) {
+ pr_err("CAIF: %s(): Could not transmit linksetup request\n",
+ __func__);
+ cfpkt_destroy(pkt);
+ }
+}
+
+int cfctrl_linkdown_req(struct layer *layer, uint8 channelid,
+ struct layer *client)
+{
+ int ret;
+ struct cfctrl *cfctrl = container_obj(layer);
+ struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
+ if (!pkt) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return CFGLU_ENOMEM;
+ }
+ cfctrl_insert_req2(cfctrl, CFCTRL_CMD_LINK_DESTROY, channelid, client);
+ cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_DESTROY);
+ cfpkt_addbdy(pkt, channelid);
+ init_info(cfpkt_info(pkt), cfctrl);
+ ret =
+ cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
+ if (ret < 0) {
+ pr_err("CAIF: %s(): Could not transmit link-down request\n",
+ __func__);
+ cfpkt_destroy(pkt);
+ }
+ return ret;
+}
+
+void cfctrl_sleep_req(struct layer *layer)
+{
+ int ret;
+ struct cfctrl *cfctrl = container_obj(layer);
+ struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
+ if (!pkt) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return;
+ }
+ cfpkt_addbdy(pkt, CFCTRL_CMD_SLEEP);
+ init_info(cfpkt_info(pkt), cfctrl);
+ ret =
+ cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
+ if (ret < 0)
+ cfpkt_destroy(pkt);
+}
+
+void cfctrl_wake_req(struct layer *layer)
+{
+ int ret;
+ struct cfctrl *cfctrl = container_obj(layer);
+ struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
+ if (!pkt) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return;
+ }
+ cfpkt_addbdy(pkt, CFCTRL_CMD_WAKE);
+ init_info(cfpkt_info(pkt), cfctrl);
+ ret =
+ cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
+ if (ret < 0)
+ cfpkt_destroy(pkt);
+}
+
+void cfctrl_getstartreason_req(struct layer *layer)
+{
+ int ret;
+ struct cfctrl *cfctrl = container_obj(layer);
+ struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
+ if (!pkt) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return;
+ }
+ cfpkt_addbdy(pkt, CFCTRL_CMD_START_REASON);
+ init_info(cfpkt_info(pkt), cfctrl);
+ ret =
+ cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
+ if (ret < 0)
+ cfpkt_destroy(pkt);
+}
+
+void cfctrl_setmode_req(struct layer *layer, uint8 mode)
+{
+ int ret;
+ struct cfctrl *cfctrl = container_obj(layer);
+ struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
+ if (!pkt) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return;
+ }
+ cfpkt_addbdy(pkt, CFCTRL_CMD_RADIO_SET);
+ cfpkt_addbdy(pkt, mode);
+ init_info(cfpkt_info(pkt), cfctrl);
+ ret =
+ cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
+ if (ret < 0)
+ cfpkt_destroy(pkt);
+}
+
+static int cfctrl_recv(struct layer *layer, struct cfpkt *pkt)
+{
+ uint8 cmdrsp;
+ uint8 cmd;
+ int ret = -1;
+ uint16 tmp16;
+ uint8 len;
+ uint8 param[255];
+ uint8 linkid;
+ struct cfctrl *cfctrl = container_obj(layer);
+ struct cfctrl_request_info rsp, *req;
+
+
+ cfpkt_extr_head(pkt, &cmdrsp, 1);
+ cmd = cmdrsp & CFCTRL_CMD_MASK;
+ if (cmd != CFCTRL_CMD_LINK_ERR
+ && CFCTRL_RSP_BIT != (CFCTRL_RSP_BIT & cmdrsp)) {
+ if (handle_loop(cfctrl, cmd, pkt) == CAIF_FAILURE) {
+ pr_info("CAIF: %s() CAIF Protocol error:"
+ "Response bit not set\n", __func__);
+ goto error;
+ }
+ }
+
+ switch (cmd) {
+ case CFCTRL_CMD_LINK_SETUP:
+ {
+ enum cfctrl_srv serv;
+ enum cfctrl_srv servtype;
+ uint8 endpoint;
+ uint8 physlinkid;
+ uint8 prio;
+ uint8 tmp;
+ uint32 tmp32;
+ uint8 *cp;
+ int i;
+ struct cfctrl_link_param linkparam;
+ memset(&linkparam, 0, sizeof(linkparam));
+
+ cfpkt_extr_head(pkt, &tmp, 1);
+
+ serv = tmp & CFCTRL_SRV_MASK;
+ linkparam.linktype = serv;
+
+ servtype = tmp >> 4;
+ linkparam.chtype = servtype;
+
+ cfpkt_extr_head(pkt, &tmp, 1);
+ physlinkid = tmp & 0x07;
+ prio = tmp >> 3;
+
+ linkparam.priority = prio;
+ linkparam.phyid = physlinkid;
+ cfpkt_extr_head(pkt, &endpoint, 1);
+ linkparam.endpoint = endpoint & 0x03;
+
+ switch (serv) {
+ case CFCTRL_SRV_VEI:
+ case CFCTRL_SRV_DBG:
+ /* Link ID */
+ cfpkt_extr_head(pkt, &linkid, 1);
+ break;
+ case CFCTRL_SRV_VIDEO:
+ cfpkt_extr_head(pkt, &tmp, 1);
+ linkparam.u.video.connid = tmp;
+ /* Link ID */
+ cfpkt_extr_head(pkt, &linkid, 1);
+ break;
+
+ case CFCTRL_SRV_DATAGRAM:
+ cfpkt_extr_head(pkt, &tmp32, 4);
+ linkparam.u.datagram.connid =
+ cfglu_le32_to_cpu(tmp32);
+ /* Link ID */
+ cfpkt_extr_head(pkt, &linkid, 1);
+ break;
+ case CFCTRL_SRV_RFM:
+ /* Construct a frame, convert
+ * DatagramConnectionID
+ * to network format long and copy it out...
+ */
+ cfpkt_extr_head(pkt, &tmp32, 4);
+ linkparam.u.rfm.connid =
+ cfglu_le32_to_cpu(tmp32);
+ cp = (uint8 *) linkparam.u.rfm.volume;
+ for (cfpkt_extr_head(pkt, &tmp, 1);
+ cfpkt_more(pkt) && tmp != '\0';
+ cfpkt_extr_head(pkt, &tmp, 1))
+ *cp++ = tmp;
+ *cp = '\0';
+
+ /* Link ID */
+ cfpkt_extr_head(pkt, &linkid, 1);
+
+ break;
+ case CFCTRL_SRV_UTIL:
+ /* Construct a frame, convert
+ * DatagramConnectionID
+ * to network format long and copy it out...
+ */
+ /* Fifosize KB */
+ cfpkt_extr_head(pkt, &tmp16, 2);
+ linkparam.u.utility.fifosize_kb =
+ cfglu_le16_to_cpu(tmp16);
+ /* Fifosize bufs */
+ cfpkt_extr_head(pkt, &tmp16, 2);
+ linkparam.u.utility.fifosize_bufs =
+ cfglu_le16_to_cpu(tmp16);
+ /* name */
+ cp = (uint8 *) linkparam.u.utility.name;
+ caif_assert(sizeof(linkparam.u.utility.name)
+ >= UTILITY_NAME_LENGTH);
+ for (i = 0;
+ i < UTILITY_NAME_LENGTH
+ && cfpkt_more(pkt); i++) {
+ cfpkt_extr_head(pkt, &tmp, 1);
+ *cp++ = tmp;
+ }
+ /* Length */
+ cfpkt_extr_head(pkt, &len, 1);
+ linkparam.u.utility.paramlen = len;
+ /* Param Data */
+ cp = linkparam.u.utility.params;
+ while (cfpkt_more(pkt) && len--) {
+ cfpkt_extr_head(pkt, &tmp, 1);
+ *cp++ = tmp;
+ }
+ /* Link ID */
+ cfpkt_extr_head(pkt, &linkid, 1);
+ /* Length */
+ cfpkt_extr_head(pkt, &len, 1);
+ /* Param Data */
+ cfpkt_extr_head(pkt, ¶m, len);
+ break;
+ default:
+ pr_warning("CAIF: %s(): Request setup "
+ "- invalid link type (%d)",
+ __func__, serv);
+ goto error;
+ }
+ if (cfpkt_erroneous(pkt)) {
+ pr_err("CAIF: %s(): Packet is erroneous!",
+ __func__);
+ goto error;
+ }
+ rsp.cmd = cmd;
+ rsp.param = linkparam;
+ req = cfctrl_remove_req(cfctrl, &rsp);
+
+ if (CFCTRL_ERR_BIT == (CFCTRL_ERR_BIT & cmdrsp)) {
+ pr_err("CAIF: %s(): Invalid O/E bit "
+ "on CAIF control channel", __func__);
+ cfctrl->res.reject_rsp(cfctrl->serv.layer.up,
+ 0,
+ req ? req->client_layer
+ : NULL);
+ } else {
+ cfctrl->res.linksetup_rsp(cfctrl->serv.
+ layer.up, linkid,
+ serv, physlinkid,
+ req ? req->
+ client_layer : NULL);
+ }
+
+ if (req != NULL)
+ cfglu_free(req);
+ }
+ break;
+ case CFCTRL_CMD_LINK_DESTROY:
+ cfpkt_extr_head(pkt, &linkid, 1);
+ rsp.cmd = cmd;
+ rsp.channel_id = linkid;
+ req = cfctrl_remove_req(cfctrl, &rsp);
+ cfctrl->res.linkdestroy_rsp(cfctrl->serv.layer.up, linkid,
+ req ? req->client_layer : NULL);
+ if (req != NULL)
+ cfglu_free(req);
+ break;
+ case CFCTRL_CMD_LINK_ERR:
+ pr_err("CAIF: %s(): Frame Error Indication received \n",
+ __func__);
+ cfctrl->res.linkerror_ind();
+ break;
+ case CFCTRL_CMD_ENUM:
+ cfctrl->res.enum_rsp();
+ break;
+ case CFCTRL_CMD_SLEEP:
+ cfctrl->res.sleep_rsp();
+ break;
+ case CFCTRL_CMD_WAKE:
+ cfctrl->res.wake_rsp();
+ break;
+ case CFCTRL_CMD_LINK_RECONF:
+ cfctrl->res.restart_rsp();
+ break;
+ case CFCTRL_CMD_RADIO_SET:
+ cfctrl->res.radioset_rsp();
+ break;
+ default:
+ pr_err("CAIF: %s(): Unrecognized Control Frame\n", __func__);
+ goto error;
+ break;
+ }
+ ret = 0;
+error:
+ cfpkt_destroy(pkt);
+ return ret;
+}
+
+static void cfctrl_ctrlcmd(struct layer *layr, enum caif_ctrlcmd ctrl,
+ int phyid)
+{
+ struct cfctrl *this = container_obj(layr);
+ switch (ctrl) {
+ case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND:
+ case CAIF_CTRLCMD_FLOW_OFF_IND:
+ cfglu_lock(this->info_list_lock);
+ if (this->first_req != NULL) {
+ pr_warning("CAIF: %s(): Received flow off in "
+ "control layer", __func__);
+ }
+ cfglu_unlock(this->info_list_lock);
+ break;
+ default:
+ break;
+ }
+}
+
+#ifndef CAIF_NO_LOOP
+int handle_loop(struct cfctrl *ctrl, int cmd, struct cfpkt *pkt)
+{
+ uint8 linkid, linktype, tmp;
+ switch (cmd) {
+ case CFCTRL_CMD_LINK_SETUP:
+ cfglu_lock(ctrl->loop_linkid_lock);
+ for (linkid = 0x1; linkid < 255; linkid++) {
+ if (!ctrl->loop_linkused[linkid]) {
+ ctrl->loop_linkused[linkid] = 1;
+ break;
+ }
+ }
+ cfpkt_add_trail(pkt, &linkid, 1);
+ cfglu_unlock(ctrl->loop_linkid_lock);
+ cfpkt_peek_head(pkt, &linktype, 1);
+ if (linktype == CFCTRL_SRV_UTIL) {
+ tmp = 0x01;
+ cfpkt_add_trail(pkt, &tmp, 1);
+ cfpkt_add_trail(pkt, &tmp, 1);
+ }
+ break;
+
+ case CFCTRL_CMD_LINK_DESTROY:
+ cfglu_lock(ctrl->loop_linkid_lock);
+ cfpkt_peek_head(pkt, &linkid, 1);
+ ctrl->loop_linkused[linkid] = 0;
+ cfglu_unlock(ctrl->loop_linkid_lock);
+ break;
+ default:
+ break;
+ }
+ return CAIF_SUCCESS;
+}
+#endif
diff --git a/net/caif/generic/cfdgml.c b/net/caif/generic/cfdgml.c
new file mode 100644
index 0000000..1b0825b
--- /dev/null
+++ b/net/caif/generic/cfdgml.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <net/caif/generic/cfglue.h>
+#include <net/caif/generic/caif_layer.h>
+#include <net/caif/generic/cfsrvl.h>
+#include <net/caif/generic/cfpkt.h>
+
+#define container_obj(layr) ((struct cfsrvl *) layr)
+
+#define DGM_CMD_BIT 0x80
+#define DGM_FLOW_OFF 0x81
+#define DGM_FLOW_ON 0x80
+#define DGM_CTRL_PKT_SIZE 1
+
+static int cfdgml_receive(struct layer *layr, struct cfpkt *pkt);
+static int cfdgml_transmit(struct layer *layr, struct cfpkt *pkt);
+
+struct layer *cfdgml_create(uint8 channel_id, struct dev_info *dev_info)
+{
+ struct cfsrvl *dgm = cfglu_alloc(sizeof(struct cfsrvl));
+ if (!dgm) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return NULL;
+ }
+ caif_assert(offsetof(struct cfsrvl, layer) == 0);
+ memset(dgm, 0, sizeof(struct cfsrvl));
+ cfsrvl_init(dgm, channel_id, dev_info);
+ dgm->layer.receive = cfdgml_receive;
+ dgm->layer.transmit = cfdgml_transmit;
+ snprintf(dgm->layer.name, CAIF_LAYER_NAME_SZ - 1, "dgm%d", channel_id);
+ dgm->layer.name[CAIF_LAYER_NAME_SZ - 1] = '\0';
+ return &dgm->layer;
+}
+
+static int cfdgml_receive(struct layer *layr, struct cfpkt *pkt)
+{
+ uint8 cmd = -1;
+ uint8 dgmhdr[3];
+ int ret;
+ caif_assert(layr->up != NULL);
+ caif_assert(layr->receive != NULL);
+ caif_assert(layr->ctrlcmd != NULL);
+
+ if (cfpkt_extr_head(pkt, &cmd, 1) < 0) {
+ pr_err("CAIF: %s(): Packet is erroneous!\n", __func__);
+ cfpkt_destroy(pkt);
+ return CFGLU_EPROTO;
+ }
+
+ if ((cmd & DGM_CMD_BIT) == 0) {
+ if (cfpkt_extr_head(pkt, &dgmhdr, 3) < 0) {
+ pr_err("CAIF: %s(): Packet is erroneous!\n", __func__);
+ cfpkt_destroy(pkt);
+ return CFGLU_EPROTO;
+ }
+ ret = layr->up->receive(layr->up, pkt);
+ return ret;
+ }
+
+ switch (cmd) {
+ case DGM_FLOW_OFF: /* FLOW OFF */
+ layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_OFF_IND, 0);
+ cfpkt_destroy(pkt);
+ return 0;
+ case DGM_FLOW_ON: /* FLOW ON */
+ layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_ON_IND, 0);
+ cfpkt_destroy(pkt);
+ return 0;
+ default:
+ cfpkt_destroy(pkt);
+ pr_info("CAIF: %s(): Unknown datagram control %d (0x%x)\n",
+ __func__, cmd, cmd);
+ return CFGLU_EPROTO;
+ }
+}
+
+static int cfdgml_transmit(struct layer *layr, struct cfpkt *pkt)
+{
+ uint32 zero = 0;
+ struct payload_info *info;
+ struct cfsrvl *service = container_obj(layr);
+ int ret;
+ if (!cfsrvl_ready(service, &ret))
+ return ret;
+
+ cfpkt_add_head(pkt, &zero, 4);
+
+ /* Add info for MUX-layer to route the packet out. */
+ info = cfpkt_info(pkt);
+ info->channel_id = service->layer.id;
+ /* To optimize alignment, we add up the size of CAIF header
+ * before payload.
+ */
+ info->hdr_len = 4;
+ info->dev_info = &service->dev_info;
+ ret = layr->dn->transmit(layr->dn, pkt);
+ if (ret < 0) {
+ uint32 tmp32;
+ cfpkt_extr_head(pkt, &tmp32, 4);
+ }
+ return ret;
+}
diff --git a/net/caif/generic/cffrml.c b/net/caif/generic/cffrml.c
new file mode 100644
index 0000000..f7249d7
--- /dev/null
+++ b/net/caif/generic/cffrml.c
@@ -0,0 +1,148 @@
+/*
+ * CAIF Framing Layer.
+ *
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#define container_obj(layr) cfglu_container_of(layr, struct cffrml, layer)
+
+#include <net/caif/generic/cfglue.h>
+#include <net/caif/generic/caif_layer.h>
+#include <net/caif/generic/cfpkt.h>
+#include <net/caif/generic/cffrml.h>
+
+struct cffrml {
+ struct layer layer;
+ bool dofcs; /* !< FCS active */
+};
+
+static int cffrml_receive(struct layer *layr, struct cfpkt *pkt);
+static int cffrml_transmit(struct layer *layr, struct cfpkt *pkt);
+static void cffrml_ctrlcmd(struct layer *layr, enum caif_ctrlcmd ctrl,
+ int phyid);
+
+uint32 cffrml_rcv_error;
+uint32 cffrml_rcv_checsum_error;
+struct layer *cffrml_create(uint16 phyid, bool use_fcs)
+{
+ struct cffrml *this = cfglu_alloc(sizeof(struct cffrml));
+ if (!this) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return NULL;
+ }
+ caif_assert(offsetof(struct cffrml, layer) == 0);
+
+ memset(this, 0, sizeof(struct layer));
+ this->layer.receive = cffrml_receive;
+ this->layer.transmit = cffrml_transmit;
+ this->layer.ctrlcmd = cffrml_ctrlcmd;
+ snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "frm%d", phyid);
+ this->dofcs = use_fcs;
+ this->layer.id = phyid;
+ return (struct layer *) this;
+}
+
+void cffrml_set_uplayer(struct layer *this, struct layer *up)
+{
+ this->up = up;
+}
+
+void cffrml_set_dnlayer(struct layer *this, struct layer *dn)
+{
+ this->dn = dn;
+}
+
+static uint16 cffrml_checksum(uint16 chks, void *buf, uint16 len)
+{
+ /* FIXME: FCS should be moved to glue in order to use OS-Specific
+ * solutions
+ */
+ return fcs16(chks, buf, len);
+}
+
+static int cffrml_receive(struct layer *layr, struct cfpkt *pkt)
+{
+ uint16 tmp;
+ uint16 len;
+ uint16 hdrchks;
+ uint16 pktchks;
+ struct cffrml *this;
+ this = container_obj(layr);
+
+ cfpkt_extr_head(pkt, &tmp, 2);
+ len = cfglu_le16_to_cpu(tmp);
+
+ /* Subtract for FCS on length if FCS is not used. */
+ if (!this->dofcs)
+ len -= 2;
+
+ if (cfpkt_setlen(pkt, len) < 0) {
+ ++cffrml_rcv_error;
+ pr_err("CAIF: %s():Framing length error (%d)\n", __func__, len);
+ cfpkt_destroy(pkt);
+ return CFGLU_EPKT;
+ }
+ /*
+ * Don't do extract if FCS is false, rather do setlen - then we don't
+ * get a cache-miss.
+ */
+ if (this->dofcs) {
+ cfpkt_extr_trail(pkt, &tmp, 2);
+ hdrchks = cfglu_le16_to_cpu(tmp);
+ pktchks = cfpkt_iterate(pkt, cffrml_checksum, 0xffff);
+ if (pktchks != hdrchks) {
+ cfpkt_add_trail(pkt, &tmp, 2);
+ ++cffrml_rcv_error;
+ ++cffrml_rcv_checsum_error;
+ pr_info("CAIF: %s(): Frame checksum error "
+ "(0x%x != 0x%x)\n", __func__, hdrchks, pktchks);
+ return CFGLU_EFCS;
+ }
+ }
+ if (cfpkt_erroneous(pkt)) {
+ ++cffrml_rcv_error;
+ pr_err("CAIF: %s(): Packet is erroneous!\n", __func__);
+ cfpkt_destroy(pkt);
+ return CFGLU_EPKT;
+ }
+ return layr->up->receive(layr->up, pkt);
+}
+
+static int cffrml_transmit(struct layer *layr, struct cfpkt *pkt)
+{
+ int tmp;
+ uint16 chks;
+ uint16 len;
+ int ret;
+ struct cffrml *this = container_obj(layr);
+ if (this->dofcs) {
+ chks = cfpkt_iterate(pkt, cffrml_checksum, 0xffff);
+ tmp = cfglu_cpu_to_le16(chks);
+ cfpkt_add_trail(pkt, &tmp, 2);
+ } else {
+ cfpkt_pad_trail(pkt, 2);
+ }
+ len = cfpkt_getlen(pkt);
+ tmp = cfglu_cpu_to_le16(len);
+ cfpkt_add_head(pkt, &tmp, 2);
+ cfpkt_info(pkt)->hdr_len += 2;
+ if (cfpkt_erroneous(pkt)) {
+ pr_err("CAIF: %s(): Packet is erroneous!\n", __func__);
+ return CFGLU_EPROTO;
+ }
+ ret = layr->dn->transmit(layr->dn, pkt);
+ if (ret < 0) {
+ /* Remove header on faulty packet. */
+ cfpkt_extr_head(pkt, &tmp, 2);
+ }
+ return ret;
+}
+
+static void cffrml_ctrlcmd(struct layer *layr, enum caif_ctrlcmd ctrl,
+ int phyid)
+{
+ if (layr->up->ctrlcmd)
+ layr->up->ctrlcmd(layr->up, ctrl, layr->id);
+}
diff --git a/net/caif/generic/cfmuxl.c b/net/caif/generic/cfmuxl.c
new file mode 100644
index 0000000..bc6451b
--- /dev/null
+++ b/net/caif/generic/cfmuxl.c
@@ -0,0 +1,225 @@
+/*
+ * 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/cfpkt.h>
+#include <net/caif/generic/cflst.h>
+#include <net/caif/generic/cfmuxl.h>
+#include <net/caif/generic/cfsrvl.h>
+#include <net/caif/generic/cffrml.h>
+
+#define container_obj(layr) cfglu_container_of(layr, struct cfmuxl, layer)
+
+#define CAIF_CTRL_CHANNEL 0
+#define UP_CACHE_SIZE 8
+#define DN_CACHE_SIZE 8
+
+struct cfmuxl {
+ struct layer layer;
+ struct layer *up_cache[UP_CACHE_SIZE];
+ struct layer *dn_cache[DN_CACHE_SIZE];
+ /*
+ * Set when inserting or removing downwards layers.
+ */
+ cfglu_lock_t transmit_lock;
+
+ /*
+ * Set when inserting or removing upwards layers.
+ */
+ cfglu_lock_t receive_lock;
+
+};
+
+static int cfmuxl_receive(struct layer *layr, struct cfpkt *pkt);
+static int cfmuxl_transmit(struct layer *layr, struct cfpkt *pkt);
+static void cfmuxl_ctrlcmd(struct layer *layr, enum caif_ctrlcmd ctrl,
+ int phyid);
+static struct layer *get_up(struct cfmuxl *muxl, int id);
+
+struct layer *cfmuxl_create()
+{
+ struct cfmuxl *this = cfglu_alloc(sizeof(struct cfmuxl));
+ if (!this) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return NULL;
+ }
+ memset(this, 0, sizeof(*this));
+ this->layer.receive = cfmuxl_receive;
+ this->layer.transmit = cfmuxl_transmit;
+ this->layer.ctrlcmd = cfmuxl_ctrlcmd;
+ cfglu_init_lock(this->transmit_lock);
+ cfglu_init_lock(this->receive_lock);
+ snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "mux");
+ return &this->layer;
+}
+
+int cfmuxl_set_uplayer(struct layer *layr, struct layer *up, uint8 linkid)
+{
+ int res;
+ struct cfmuxl *muxl = container_obj(layr);
+ cfglu_lock(muxl->receive_lock);
+ res = cflst_put(&muxl->layer.up, linkid, up);
+ cfglu_unlock(muxl->receive_lock);
+ return res;
+}
+
+bool cfmuxl_is_phy_inuse(struct layer *layr, uint8 phyid)
+{
+ struct layer *p;
+ struct cfmuxl *muxl = container_obj(layr);
+ bool match = false;
+ cfglu_lock(muxl->receive_lock);
+
+ for (p = layr->up; p != NULL; p = p->next) {
+ if (cfsrvl_phyid_match(p, phyid)) {
+ match = true;
+ break;
+ }
+ }
+ cfglu_unlock(muxl->receive_lock);
+ return match;
+}
+
+uint8 cfmuxl_get_phyid(struct layer *layr, uint8 channel_id)
+{
+ struct layer *up;
+ int phyid;
+ struct cfmuxl *muxl = container_obj(layr);
+ cfglu_lock(muxl->receive_lock);
+ up = get_up(muxl, channel_id);
+ if (up != NULL)
+ phyid = cfsrvl_getphyid(up);
+ else
+ phyid = 0;
+ cfglu_unlock(muxl->receive_lock);
+ return phyid;
+}
+
+int cfmuxl_set_dnlayer(struct layer *layr, struct layer *dn, uint8 phyid)
+{
+ int ret;
+ struct cfmuxl *muxl = (struct cfmuxl *) layr;
+ cfglu_lock(muxl->transmit_lock);
+ ret = cflst_put(&muxl->layer.dn, phyid, dn);
+ cfglu_unlock(muxl->transmit_lock);
+ return ret;
+}
+
+struct layer *cfmuxl_remove_dnlayer(struct layer *layr, uint8 phyid)
+{
+ struct cfmuxl *muxl = container_obj(layr);
+ struct layer *dn;
+ cfglu_lock(muxl->transmit_lock);
+ memset(muxl->dn_cache, 0, sizeof(muxl->dn_cache));
+ dn = cflst_del(&muxl->layer.dn, phyid);
+ caif_assert(dn != NULL);
+ cfglu_unlock(muxl->transmit_lock);
+ return dn;
+}
+
+/* Invariant: lock is taken */
+static struct layer *get_up(struct cfmuxl *muxl, int id)
+{
+ struct layer *up;
+ int idx = id % UP_CACHE_SIZE;
+ up = muxl->up_cache[idx];
+ if (up == NULL || up->id != id) {
+ up = cflst_get(&muxl->layer.up, id);
+ muxl->up_cache[idx] = up;
+ }
+ return up;
+}
+
+/* Invariant: lock is taken */
+static struct layer *get_dn(struct cfmuxl *muxl, struct dev_info *dev_info)
+{
+ struct layer *dn;
+ int idx = dev_info->id % DN_CACHE_SIZE;
+ dn = muxl->dn_cache[idx];
+ if (dn == NULL || dn->id != dev_info->id) {
+ dn = cflst_get(&muxl->layer.dn, dev_info->id);
+ muxl->dn_cache[idx] = dn;
+ }
+ return dn;
+}
+
+struct layer *cfmuxl_remove_uplayer(struct layer *layr, uint8 id)
+{
+ struct layer *up;
+ struct cfmuxl *muxl = container_obj(layr);
+ cfglu_lock(muxl->receive_lock);
+ memset(muxl->up_cache, 0, sizeof(muxl->up_cache));
+ up = cflst_del(&muxl->layer.up, id);
+ cfglu_unlock(muxl->receive_lock);
+ return up;
+}
+
+static int cfmuxl_receive(struct layer *layr, struct cfpkt *pkt)
+{
+ int ret;
+ struct cfmuxl *muxl = container_obj(layr);
+ uint8 id;
+ struct layer *up;
+ if (cfpkt_extr_head(pkt, &id, 1) < 0) {
+ pr_err("CAIF: %s(): erroneous Caif Packet\n", __func__);
+ cfpkt_destroy(pkt);
+ return CFGLU_EPKT;
+ }
+
+ up = get_up(muxl, id);
+ if (up == NULL) {
+ pr_info("CAIF: %s():Received data on unknown link ID = %d "
+ "(0x%x) up == NULL", __func__, id, id);
+ cfpkt_destroy(pkt);
+ /*
+ * Don't return ERROR, since modem misbehaves and sends out
+ * flow before linksetup response.
+ */
+ return /* CFGLU_EPROT; */ CFGLU_EOK;
+ }
+
+ ret = up->receive(up, pkt);
+
+
+ return ret;
+}
+
+static int cfmuxl_transmit(struct layer *layr, struct cfpkt *pkt)
+{
+ int ret;
+ struct cfmuxl *muxl = container_obj(layr);
+ uint8 linkid;
+ struct layer *dn;
+ struct payload_info *info = cfpkt_info(pkt);
+ dn = get_dn(muxl, cfpkt_info(pkt)->dev_info);
+ if (dn == NULL) {
+ pr_warning("CAIF: %s(): Send data on unknown phy "
+ "ID = %d (0x%x)\n",
+ __func__, info->dev_info->id, info->dev_info->id);
+ return CFGLU_ENOTCONN;
+ }
+ info->hdr_len += 1;
+ linkid = info->channel_id;
+ cfpkt_add_head(pkt, &linkid, 1);
+ ret = dn->transmit(dn, pkt);
+ if (ret < 0) {
+ /* Remove MUX protocol header upon error. */
+ cfpkt_extr_head(pkt, &linkid, 1);
+ }
+
+ return ret;
+}
+
+static void cfmuxl_ctrlcmd(struct layer *layr, enum caif_ctrlcmd ctrl,
+ int phyid)
+{
+ struct layer *p;
+ struct cfmuxl *muxl = container_obj(layr);
+ for (p = muxl->layer.up; p != NULL; p = p->next) {
+ if (cfsrvl_phyid_match(p, phyid))
+ p->ctrlcmd(p, ctrl, phyid);
+ }
+}
diff --git a/net/caif/generic/cfrfml.c b/net/caif/generic/cfrfml.c
new file mode 100644
index 0000000..97bc972
--- /dev/null
+++ b/net/caif/generic/cfrfml.c
@@ -0,0 +1,104 @@
+/*
+ * 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/caif_layer.h>
+#include <net/caif/generic/cfsrvl.h>
+#include <net/caif/generic/cfpkt.h>
+
+#define container_obj(layr) cfglu_container_of(layr, struct cfsrvl, layer)
+
+#define RFM_SEGMENTATION_BIT 0x01
+#define RFM_PAYLOAD 0x00
+#define RFM_CMD_BIT 0x80
+#define RFM_FLOW_OFF 0x81
+#define RFM_FLOW_ON 0x80
+#define RFM_SET_PIN 0x82
+#define RFM_CTRL_PKT_SIZE 1
+
+static int cfrfml_receive(struct layer *layr, struct cfpkt *pkt);
+static int cfrfml_transmit(struct layer *layr, struct cfpkt *pkt);
+
+struct layer *cfrfml_create(uint8 channel_id, struct dev_info *dev_info)
+{
+ struct cfsrvl *rfm = cfglu_alloc(sizeof(struct cfsrvl));
+ if (!rfm) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return NULL;
+ }
+ caif_assert(offsetof(struct cfsrvl, layer) == 0);
+ memset(rfm, 0, sizeof(struct cfsrvl));
+ cfsrvl_init(rfm, channel_id, dev_info);
+ rfm->layer.receive = cfrfml_receive;
+ rfm->layer.transmit = cfrfml_transmit;
+ snprintf(rfm->layer.name, CAIF_LAYER_NAME_SZ, "rfm%d", channel_id);
+ return &rfm->layer;
+}
+
+void cffrml_destroy(struct layer *layer)
+{
+ cfglu_free(layer);
+}
+
+static int cfrfml_receive(struct layer *layr, struct cfpkt *pkt)
+{
+ uint8 tmp;
+ bool segmented;
+ int ret;
+ caif_assert(layr->up != NULL);
+ caif_assert(layr->receive != NULL);
+
+ /*
+ * RFM is taking care of segmentation and stripping of
+ * segmentation bit.
+ */
+ if (cfpkt_extr_head(pkt, &tmp, 1) < 0) {
+ pr_err("CAIF: %s(): Packet is erroneous!\n", __func__);
+ cfpkt_destroy(pkt);
+ return CFGLU_EPROTO;
+ }
+ segmented = tmp & RFM_SEGMENTATION_BIT;
+ caif_assert(!segmented);
+
+ ret = layr->up->receive(layr->up, pkt);
+ return ret;
+}
+
+static int cfrfml_transmit(struct layer *layr, struct cfpkt *pkt)
+{
+ uint8 tmp = 0;
+ int ret;
+ struct cfsrvl *service = container_obj(layr);
+
+ caif_assert(layr->dn != NULL);
+ caif_assert(layr->dn->transmit != NULL);
+
+ if (!cfsrvl_ready(service, &ret))
+ return ret;
+
+ if (!cfpkt_getlen(pkt) > CAIF_MAX_PAYLOAD_SIZE) {
+ pr_err("CAIF: %s():Packet too large - size=%d\n",
+ __func__, cfpkt_getlen(pkt));
+ return CFGLU_EOVERFLOW;
+ }
+ if (cfpkt_add_head(pkt, &tmp, 1) < 0) {
+ pr_err("CAIF: %s(): Packet is erroneous!\n", __func__);
+ return CFGLU_EPKT;
+ }
+
+ /* Add info for MUX-layer to route the packet out. */
+ cfpkt_info(pkt)->channel_id = service->layer.id;
+ /*
+ * To optimize alignment, we add up the size of CAIF header before
+ * payload.
+ */
+ cfpkt_info(pkt)->hdr_len = 1;
+ cfpkt_info(pkt)->dev_info = &service->dev_info;
+ ret = layr->dn->transmit(layr->dn, pkt);
+ if (ret < 0)
+ cfpkt_extr_head(pkt, &tmp, 1);
+ return ret;
+}
diff --git a/net/caif/generic/cfserl.c b/net/caif/generic/cfserl.c
new file mode 100644
index 0000000..baf80fc
--- /dev/null
+++ b/net/caif/generic/cfserl.c
@@ -0,0 +1,198 @@
+/*
+ * 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/caif_layer.h>
+#include <net/caif/generic/cfpkt.h>
+
+#define container_obj(layr) ((struct cfserl *) layr)
+
+#define CFSERL_STX 0x02
+#define CAIF_MINIUM_PACKET_SIZE 4
+struct cfserl {
+ struct layer layer;
+ struct cfpkt *incomplete_frm;
+ cfglu_lock_t sync;
+ bool usestx;
+};
+#define STXLEN(layr) (layr->usestx ? 1 : 0)
+
+static int cfserl_receive(struct layer *layr, struct cfpkt *pkt);
+static int cfserl_transmit(struct layer *layr, struct cfpkt *pkt);
+static void cfserl_ctrlcmd(struct layer *layr, enum caif_ctrlcmd ctrl,
+ int phyid);
+
+struct cfserl *cfserl_create(int type, int instance, bool use_stx)
+{
+ struct cfserl *this = cfglu_alloc(sizeof(struct cfserl));
+ if (!this) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return NULL;
+ }
+ caif_assert(offsetof(struct cfserl, layer) == 0);
+ memset(this, 0, sizeof(struct cfserl));
+ this->layer.receive = cfserl_receive;
+ this->layer.transmit = cfserl_transmit;
+ this->layer.ctrlcmd = cfserl_ctrlcmd;
+ this->layer.type = type;
+ this->usestx = use_stx;
+ cfglu_init_lock(this->sync);
+ snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "ser1");
+ return this;
+}
+
+void cfserl_set_uplayer(struct cfserl *this, struct layer *up)
+{
+ this->layer.up = up;
+}
+
+void cfserl_set_dnlayer(struct cfserl *this, struct layer *dn)
+{
+ this->layer.dn = dn;
+}
+
+static int cfserl_receive(struct layer *l, struct cfpkt *newpkt)
+{
+ struct cfserl *layr = container_obj(l);
+ uint16 pkt_len;
+ struct cfpkt *pkt = NULL;
+ struct cfpkt *tail_pkt = NULL;
+ uint8 tmp8;
+ uint16 tmp;
+ uint8 stx = CFSERL_STX;
+ int ret;
+ uint16 expectlen = 0;
+ caif_assert(newpkt != NULL);
+ cfglu_lock(layr->sync);
+
+ if (layr->incomplete_frm != NULL) {
+
+ layr->incomplete_frm =
+ cfpkt_append(layr->incomplete_frm, newpkt, expectlen);
+ pkt = layr->incomplete_frm;
+ } else {
+ pkt = newpkt;
+ }
+ layr->incomplete_frm = NULL;
+
+ do {
+ /* Search for STX at start of pkt if STX is used */
+ if (layr->usestx) {
+ cfpkt_extr_head(pkt, &tmp8, 1);
+ if (tmp8 != CFSERL_STX) {
+ while (cfpkt_more(pkt)
+ && tmp8 != CFSERL_STX) {
+ cfpkt_extr_head(pkt, &tmp8, 1);
+ }
+ if (!cfpkt_more(pkt)) {
+ cfpkt_destroy(pkt);
+ layr->incomplete_frm = NULL;
+ cfglu_unlock(layr->sync);
+ return CFGLU_EPROTO;
+ }
+ }
+ }
+
+ pkt_len = cfpkt_getlen(pkt);
+
+ /*
+ * pkt_len is the accumulated length of the packet data
+ * we have received so far.
+ * Exit if frame doesn't hold length.
+ */
+
+ if (pkt_len < 2) {
+ if (layr->usestx)
+ cfpkt_add_head(pkt, &stx, 1);
+ layr->incomplete_frm = pkt;
+ cfglu_unlock(layr->sync);
+ return 0;
+ }
+
+ /*
+ * Find length of frame.
+ * expectlen is the length we need for a full frame.
+ */
+ cfpkt_peek_head(pkt, &tmp, 2);
+ expectlen = cfglu_le16_to_cpu(tmp) + 2;
+ /*
+ * Frame error handling
+ */
+ if (expectlen < CAIF_MINIUM_PACKET_SIZE
+ || expectlen > CAIF_MAX_FRAMESIZE) {
+ if (!layr->usestx) {
+ if (pkt != NULL)
+ cfpkt_destroy(pkt);
+ layr->incomplete_frm = NULL;
+ expectlen = 0;
+ cfglu_unlock(layr->sync);
+ return CFGLU_EPROTO;
+ }
+ continue;
+ }
+
+ if (pkt_len < expectlen) {
+ /* Too little received data */
+ if (layr->usestx)
+ cfpkt_add_head(pkt, &stx, 1);
+ layr->incomplete_frm = pkt;
+ cfglu_unlock(layr->sync);
+ return 0;
+ }
+
+ /*
+ * Enough data for at least one frame.
+ * Split the frame, if too long
+ */
+ if (pkt_len > expectlen)
+ tail_pkt = cfpkt_split(pkt, expectlen);
+ else
+ tail_pkt = NULL;
+
+ /* Send the first part of packet upwards.*/
+ cfglu_unlock(layr->sync);
+ ret = layr->layer.up->receive(layr->layer.up, pkt);
+ cfglu_lock(layr->sync);
+ if (ret == CFGLU_EFCS) {
+ if (layr->usestx) {
+ if (tail_pkt != NULL)
+ pkt = cfpkt_append(pkt, tail_pkt, 0);
+
+ /* Start search for next STX if frame failed */
+ continue;
+ } else {
+ cfpkt_destroy(pkt);
+ pkt = NULL;
+ }
+ }
+
+ pkt = tail_pkt;
+
+ } while (pkt != NULL);
+
+ cfglu_unlock(layr->sync);
+ return 0;
+}
+
+static int cfserl_transmit(struct layer *layer, struct cfpkt *newpkt)
+{
+ struct cfserl *layr = container_obj(layer);
+ int ret;
+ uint8 tmp8 = CFSERL_STX;
+ if (layr->usestx)
+ cfpkt_add_head(newpkt, &tmp8, 1);
+ ret = layer->dn->transmit(layer->dn, newpkt);
+ if (ret < 0)
+ cfpkt_extr_head(newpkt, &tmp8, 1);
+
+ return ret;
+}
+
+static void cfserl_ctrlcmd(struct layer *layr, enum caif_ctrlcmd ctrl,
+ int phyid)
+{
+ layr->up->ctrlcmd(layr->up, ctrl, phyid);
+}
diff --git a/net/caif/generic/cfsrvl.c b/net/caif/generic/cfsrvl.c
new file mode 100644
index 0000000..5770042
--- /dev/null
+++ b/net/caif/generic/cfsrvl.c
@@ -0,0 +1,182 @@
+/*
+ * 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/caif_layer.h>
+#include <net/caif/generic/cfsrvl.h>
+#include <net/caif/generic/cfpkt.h>
+
+#define SRVL_CTRL_PKT_SIZE 1
+#define SRVL_FLOW_OFF 0x81
+#define SRVL_FLOW_ON 0x80
+#define SRVL_SET_PIN 0x82
+#define SRVL_CTRL_PKT_SIZE 1
+
+#define container_obj(layr) cfglu_container_of(layr, struct cfsrvl, layer)
+
+static void cfservl_ctrlcmd(struct layer *layr, enum caif_ctrlcmd ctrl,
+ int phyid)
+{
+ struct cfsrvl *service = container_obj(layr);
+ caif_assert(layr->up != NULL);
+ caif_assert(layr->up->ctrlcmd != NULL);
+ switch (ctrl) {
+ case CAIF_CTRLCMD_INIT_RSP:
+ service->open = true;
+ layr->up->ctrlcmd(layr->up, ctrl, phyid);
+ break;
+ case CAIF_CTRLCMD_DEINIT_RSP:
+ case CAIF_CTRLCMD_INIT_FAIL_RSP:
+ service->open = false;
+ layr->up->ctrlcmd(layr->up, ctrl, phyid);
+ break;
+ case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND:
+ if (phyid != service->dev_info.id)
+ break;
+ if (service->modem_flow_on)
+ layr->up->ctrlcmd(layr->up,
+ CAIF_CTRLCMD_FLOW_OFF_IND, phyid);
+ service->phy_flow_on = false;
+ break;
+ case _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND:
+ if (phyid != service->dev_info.id)
+ return;
+ if (service->modem_flow_on) {
+ layr->up->ctrlcmd(layr->up,
+ CAIF_CTRLCMD_FLOW_ON_IND,
+ phyid);
+ }
+ service->phy_flow_on = true;
+ break;
+ case CAIF_CTRLCMD_FLOW_OFF_IND:
+ if (service->phy_flow_on) {
+ layr->up->ctrlcmd(layr->up,
+ CAIF_CTRLCMD_FLOW_OFF_IND, phyid);
+ }
+ service->modem_flow_on = false;
+ break;
+ case CAIF_CTRLCMD_FLOW_ON_IND:
+ if (service->phy_flow_on) {
+ layr->up->ctrlcmd(layr->up,
+ CAIF_CTRLCMD_FLOW_ON_IND, phyid);
+ }
+ service->modem_flow_on = true;
+ break;
+ case _CAIF_CTRLCMD_PHYIF_DOWN_IND:
+ /* In case interface is down, let's fake a remove shutdown */
+ layr->up->ctrlcmd(layr->up,
+ CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND, phyid);
+ break;
+ case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND:
+ layr->up->ctrlcmd(layr->up, ctrl, phyid);
+ break;
+ default:
+ pr_warning("CAIF: %s(): "
+ "Unexpected ctrl in cfsrvl (%d)\n", __func__, ctrl);
+ /* We have both modem and phy flow on, send flow on */
+ layr->up->ctrlcmd(layr->up, ctrl, phyid);
+ service->phy_flow_on = true;
+ break;
+ }
+}
+
+static int cfservl_modemcmd(struct layer *layr, enum caif_modemcmd ctrl)
+{
+ struct cfsrvl *service = container_obj(layr);
+ caif_assert(layr != NULL);
+ caif_assert(layr->dn != NULL);
+ caif_assert(layr->dn->transmit != NULL);
+ switch (ctrl) {
+ case CAIF_MODEMCMD_FLOW_ON_REQ:
+ {
+ struct cfpkt *pkt;
+ struct payload_info *info;
+ uint8 flow_on = SRVL_FLOW_ON;
+ pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE);
+ if (!pkt) {
+ pr_warning("CAIF: %s(): Out of memory\n",
+ __func__);
+ return CFGLU_ENOMEM;
+ }
+
+ if (cfpkt_add_head(pkt, &flow_on, 1) < 0) {
+ pr_err("CAIF: %s(): Packet is erroneous!\n",
+ __func__);
+ cfpkt_destroy(pkt);
+ return CFGLU_EPROTO;
+ }
+ info = cfpkt_info(pkt);
+ info->channel_id = service->layer.id;
+ info->hdr_len = 1;
+ info->dev_info = &service->dev_info;
+ return layr->dn->transmit(layr->dn, pkt);
+ }
+ case CAIF_MODEMCMD_FLOW_OFF_REQ:
+ {
+ struct cfpkt *pkt;
+ struct payload_info *info;
+ uint8 flow_off = SRVL_FLOW_OFF;
+ pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE);
+ if (cfpkt_add_head(pkt, &flow_off, 1) < 0) {
+ pr_err("CAIF: %s(): Packet is erroneous!\n",
+ __func__);
+ cfpkt_destroy(pkt);
+ return CFGLU_EPROTO;
+ }
+ info = cfpkt_info(pkt);
+ info->channel_id = service->layer.id;
+ info->hdr_len = 1;
+ info->dev_info = &service->dev_info;
+ return layr->dn->transmit(layr->dn, pkt);
+ }
+ default:
+ break;
+ }
+ return CFGLU_EINVAL;
+}
+
+void cfservl_destroy(struct layer *layer)
+{
+ cfglu_free(layer);
+}
+
+void cfsrvl_init(struct cfsrvl *service,
+ uint8 channel_id,
+ struct dev_info *dev_info)
+{
+ caif_assert(offsetof(struct cfsrvl, layer) == 0);
+ service->open = false;
+ service->modem_flow_on = true;
+ service->phy_flow_on = true;
+ service->layer.id = channel_id;
+ service->layer.ctrlcmd = cfservl_ctrlcmd;
+ service->layer.modemcmd = cfservl_modemcmd;
+ service->dev_info = *dev_info;
+}
+
+bool cfsrvl_ready(struct cfsrvl *service, int *err)
+{
+ if (service->open && service->modem_flow_on && service->phy_flow_on)
+ return true;
+ if (!service->open) {
+ *err = CFGLU_ENOTCONN;
+ return false;
+ }
+ caif_assert(!(service->modem_flow_on && service->phy_flow_on));
+ *err = CFGLU_ERETRY;
+ return false;
+}
+uint8 cfsrvl_getphyid(struct layer *layer)
+{
+ struct cfsrvl *servl = container_obj(layer);
+ return servl->dev_info.id;
+}
+
+bool cfsrvl_phyid_match(struct layer *layer, int phyid)
+{
+ struct cfsrvl *servl = container_obj(layer);
+ return servl->dev_info.id == phyid;
+}
diff --git a/net/caif/generic/cfutill.c b/net/caif/generic/cfutill.c
new file mode 100644
index 0000000..5029e0b
--- /dev/null
+++ b/net/caif/generic/cfutill.c
@@ -0,0 +1,112 @@
+/*
+ * 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/caif_layer.h>
+#include <net/caif/generic/cfsrvl.h>
+#include <net/caif/generic/cfpkt.h>
+
+#define container_obj(layr) ((struct cfsrvl *) layr)
+#define UTIL_PAYLOAD 0x00
+#define UTIL_CMD_BIT 0x80
+#define UTIL_REMOTE_SHUTDOWN 0x82
+#define UTIL_FLOW_OFF 0x81
+#define UTIL_FLOW_ON 0x80
+#define UTIL_CTRL_PKT_SIZE 1
+static int cfutill_receive(struct layer *layr, struct cfpkt *pkt);
+static int cfutill_transmit(struct layer *layr, struct cfpkt *pkt);
+
+struct layer *cfutill_create(uint8 channel_id, struct dev_info *dev_info)
+{
+ struct cfsrvl *util = cfglu_alloc(sizeof(struct cfsrvl));
+ if (!util) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return NULL;
+ }
+ caif_assert(offsetof(struct cfsrvl, layer) == 0);
+ memset(util, 0, sizeof(struct cfsrvl));
+ cfsrvl_init(util, channel_id, dev_info);
+ util->layer.receive = cfutill_receive;
+ util->layer.transmit = cfutill_transmit;
+ snprintf(util->layer.name, CAIF_LAYER_NAME_SZ - 1, "util1");
+ return &util->layer;
+}
+
+static int cfutill_receive(struct layer *layr, struct cfpkt *pkt)
+{
+ uint8 cmd = -1;
+ struct cfsrvl *service = container_obj(layr);
+ caif_assert(layr != NULL);
+ caif_assert(layr->up != NULL);
+ caif_assert(layr->up->receive != NULL);
+ caif_assert(layr->up->ctrlcmd != NULL);
+ if (cfpkt_extr_head(pkt, &cmd, 1) < 0) {
+ pr_err("CAIF: %s(): Packet is erroneous!\n", __func__);
+ cfpkt_destroy(pkt);
+ return CFGLU_EPROTO;
+ }
+
+ switch (cmd) {
+ case UTIL_PAYLOAD:
+ return layr->up->receive(layr->up, pkt);
+ case UTIL_FLOW_OFF:
+ layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_OFF_IND, 0);
+ cfpkt_destroy(pkt);
+ return 0;
+ case UTIL_FLOW_ON:
+ layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_ON_IND, 0);
+ cfpkt_destroy(pkt);
+ return 0;
+ case UTIL_REMOTE_SHUTDOWN: /* Remote Shutdown Request */
+ pr_err("CAIF: %s(): REMOTE SHUTDOWN REQUEST RECEIVED\n",
+ __func__);
+ layr->ctrlcmd(layr, CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND, 0);
+ service->open = false;
+ cfpkt_destroy(pkt);
+ return 0;
+ default:
+ cfpkt_destroy(pkt);
+ pr_warning("CAIF: %s(): Unknown service control %d (0x%x)\n",
+ __func__, cmd, cmd);
+ return CFGLU_EPROTO;
+ }
+}
+
+static int cfutill_transmit(struct layer *layr, struct cfpkt *pkt)
+{
+ uint8 zero = 0;
+ struct payload_info *info;
+ int ret;
+ struct cfsrvl *service = container_obj(layr);
+ caif_assert(layr != NULL);
+ caif_assert(layr->dn != NULL);
+ caif_assert(layr->dn->transmit != NULL);
+ if (!cfsrvl_ready(service, &ret))
+ return ret;
+
+ if (cfpkt_getlen(pkt) > CAIF_MAX_PAYLOAD_SIZE) {
+ pr_err("CAIF: %s(): packet too large size=%d\n",
+ __func__, cfpkt_getlen(pkt));
+ return CFGLU_EOVERFLOW;
+ }
+
+ cfpkt_add_head(pkt, &zero, 1);
+ /* Add info for MUX-layer to route the packet out. */
+ info = cfpkt_info(pkt);
+ info->channel_id = service->layer.id;
+ /*
+ * To optimize alignment, we add up the size of CAIF header before
+ * payload.
+ */
+ info->hdr_len = 1;
+ info->dev_info = &service->dev_info;
+ ret = layr->dn->transmit(layr->dn, pkt);
+ if (ret < 0) {
+ uint32 tmp32;
+ cfpkt_extr_head(pkt, &tmp32, 4);
+ }
+ return ret;
+}
diff --git a/net/caif/generic/cfveil.c b/net/caif/generic/cfveil.c
new file mode 100644
index 0000000..c2fccfd
--- /dev/null
+++ b/net/caif/generic/cfveil.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <net/caif/generic/cfglue.h>
+#include <net/caif/generic/caif_layer.h>
+#include <net/caif/generic/cfsrvl.h>
+#include <net/caif/generic/cfpkt.h>
+
+#define VEI_PAYLOAD 0x00
+#define VEI_CMD_BIT 0x80
+#define VEI_FLOW_OFF 0x81
+#define VEI_FLOW_ON 0x80
+#define VEI_SET_PIN 0x82
+#define VEI_CTRL_PKT_SIZE 1
+#define container_obj(layr) cfglu_container_of(layr, struct cfsrvl, layer)
+
+static int cfvei_receive(struct layer *layr, struct cfpkt *pkt);
+static int cfvei_transmit(struct layer *layr, struct cfpkt *pkt);
+
+struct layer *cfvei_create(uint8 channel_id, struct dev_info *dev_info)
+{
+ struct cfsrvl *vei = cfglu_alloc(sizeof(struct cfsrvl));
+ if (!vei) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return NULL;
+ }
+ caif_assert(offsetof(struct cfsrvl, layer) == 0);
+ memset(vei, 0, sizeof(struct cfsrvl));
+ cfsrvl_init(vei, channel_id, dev_info);
+ vei->layer.receive = cfvei_receive;
+ vei->layer.transmit = cfvei_transmit;
+ snprintf(vei->layer.name, CAIF_LAYER_NAME_SZ - 1, "vei%d", channel_id);
+ return &vei->layer;
+}
+
+static int cfvei_receive(struct layer *layr, struct cfpkt *pkt)
+{
+ uint8 cmd;
+ int ret;
+ caif_assert(layr->up != NULL);
+ caif_assert(layr->receive != NULL);
+ caif_assert(layr->ctrlcmd != NULL);
+
+
+ if (cfpkt_extr_head(pkt, &cmd, 1) < 0) {
+ pr_err("CAIF: %s(): Packet is erroneous!\n", __func__);
+ cfpkt_destroy(pkt);
+ return CFGLU_EPROTO;
+ }
+ switch (cmd) {
+ case VEI_PAYLOAD:
+ ret = layr->up->receive(layr->up, pkt);
+ return ret;
+ case VEI_FLOW_OFF:
+ layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_OFF_IND, 0);
+ cfpkt_destroy(pkt);
+ return CFGLU_EOK;
+ case VEI_FLOW_ON:
+ layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_ON_IND, 0);
+ cfpkt_destroy(pkt);
+ return CFGLU_EOK;
+ case VEI_SET_PIN: /* SET RS232 PIN */
+ cfpkt_destroy(pkt);
+ return CFGLU_EOK;
+ default: /* SET RS232 PIN */
+ pr_warning("CAIF: %s():Unknown VEI control packet %d (0x%x)!\n",
+ __func__, cmd, cmd);
+ cfpkt_destroy(pkt);
+ return CFGLU_EPROTO;
+ }
+}
+
+static int cfvei_transmit(struct layer *layr, struct cfpkt *pkt)
+{
+ uint8 tmp = 0;
+ struct payload_info *info;
+ int ret;
+ struct cfsrvl *service = container_obj(layr);
+ if (!cfsrvl_ready(service, &ret))
+ return ret;
+ caif_assert(layr->dn != NULL);
+ caif_assert(layr->dn->transmit != NULL);
+ if (!cfpkt_getlen(pkt) > CAIF_MAX_PAYLOAD_SIZE) {
+ pr_warning("CAIF: %s(): Packet too large - size=%d\n",
+ __func__, cfpkt_getlen(pkt));
+ return CFGLU_EOVERFLOW;
+ }
+
+ if (cfpkt_add_head(pkt, &tmp, 1) < 0) {
+ pr_err("CAIF: %s(): Packet is erroneous!\n", __func__);
+ return CFGLU_EPKT;
+ }
+
+ /* Add info-> for MUX-layer to route the packet out. */
+ info = cfpkt_info(pkt);
+ info->channel_id = service->layer.id;
+ info->hdr_len = 1;
+ info->dev_info = &service->dev_info;
+ ret = layr->dn->transmit(layr->dn, pkt);
+ if (ret < 0)
+ cfpkt_extr_head(pkt, &tmp, 1);
+ return ret;
+}
diff --git a/net/caif/generic/cfvidl.c b/net/caif/generic/cfvidl.c
new file mode 100644
index 0000000..f88450e
--- /dev/null
+++ b/net/caif/generic/cfvidl.c
@@ -0,0 +1,62 @@
+/*
+ * 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/caif_layer.h>
+#include <net/caif/generic/cfglue.h>
+#include <net/caif/generic/cfsrvl.h>
+#include <net/caif/generic/cfpkt.h>
+
+#define container_obj(layr) ((struct cfsrvl *) layr)
+
+static int cfvidl_receive(struct layer *layr, struct cfpkt *pkt);
+static int cfvidl_transmit(struct layer *layr, struct cfpkt *pkt);
+
+struct layer *cfvidl_create(uint8 channel_id, struct dev_info *dev_info)
+{
+ struct cfsrvl *vid = cfglu_alloc(sizeof(struct cfsrvl));
+ if (!vid) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return NULL;
+ }
+ caif_assert(offsetof(struct cfsrvl, layer) == 0);
+
+ memset(vid, 0, sizeof(struct cfsrvl));
+ cfsrvl_init(vid, channel_id, dev_info);
+ vid->layer.receive = cfvidl_receive;
+ vid->layer.transmit = cfvidl_transmit;
+ snprintf(vid->layer.name, CAIF_LAYER_NAME_SZ - 1, "vid1");
+ return &vid->layer;
+}
+
+static int cfvidl_receive(struct layer *layr, struct cfpkt *pkt)
+{
+ uint32 videoheader;
+ if (cfpkt_extr_head(pkt, &videoheader, 4) < 0) {
+ pr_err("CAIF: %s(): Packet is erroneous!\n", __func__);
+ cfpkt_destroy(pkt);
+ return CFGLU_EPROTO;
+ }
+ return layr->up->receive(layr->up, pkt);
+}
+
+static int cfvidl_transmit(struct layer *layr, struct cfpkt *pkt)
+{
+ struct cfsrvl *service = container_obj(layr);
+ struct payload_info *info;
+ uint32 videoheader = 0;
+ int ret;
+ if (!cfsrvl_ready(service, &ret))
+ return ret;
+ cfpkt_add_head(pkt, &videoheader, 4);
+ /* Add info for MUX-layer to route the packet out */
+ info = cfpkt_info(pkt);
+ info->channel_id = service->layer.id;
+ info->dev_info = &service->dev_info;
+ ret = layr->dn->transmit(layr->dn, pkt);
+ if (ret < 0)
+ cfpkt_extr_head(pkt, &videoheader, 4);
+ return ret;
+}
--
1.6.3.3
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PATCH net-next-2.6 06/13] net-caif: add CAIF generic caif support functions
2010-01-20 22:55 [PATCH net-next-2.6 00/13] net-caif: introducing CAIF protocol stack sjur.brandeland
` (4 preceding siblings ...)
2010-01-20 22:55 ` [PATCH net-next-2.6 05/13] net-caif: add CAIF generic protocol stack sjur.brandeland
@ 2010-01-20 22:55 ` sjur.brandeland
2010-01-20 22:55 ` [PATCH net-next-2.6 07/13] net-caif: add CAIF device registration functionality sjur.brandeland
` (7 subsequent siblings)
13 siblings, 0 replies; 47+ messages in thread
From: sjur.brandeland @ 2010-01-20 22:55 UTC (permalink / raw)
To: netdev; +Cc: davem, marcel, stefano.babic, randy.dunlap, Sjur Braendeland
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
Support functions for the generic caif protocol stack:
cflist.c - List implementation for CAIF
cfcnfg.c - CAIF Configuration Module used for
adding and removing drivers and connection
cfpkt_skbuff.c - CAIF Packet layer (SKB helper functions)
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
net/caif/generic/cfcnfg.c | 538 +++++++++++++++++++++++++++++++++++
net/caif/generic/cflist.c | 88 ++++++
net/caif/generic/cfpkt_skbuff.c | 596 +++++++++++++++++++++++++++++++++++++++
3 files changed, 1222 insertions(+), 0 deletions(-)
diff --git a/net/caif/generic/cfcnfg.c b/net/caif/generic/cfcnfg.c
new file mode 100644
index 0000000..0b664ff
--- /dev/null
+++ b/net/caif/generic/cfcnfg.c
@@ -0,0 +1,538 @@
+/*
+ * 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/caif_layer.h>
+#include <net/caif/generic/cfpkt.h>
+#include <net/caif/generic/cflst.h>
+#include <net/caif/generic/cfcnfg.h>
+#include <net/caif/generic/cfctrl.h>
+#include <net/caif/generic/cfmuxl.h>
+#include <net/caif/generic/cffrml.h>
+#include <net/caif/generic/cfserl.h>
+#include <net/caif/generic/cfsrvl.h>
+
+#include <linux/module.h>
+
+#define MAX_PHY_LAYERS 7
+#define PHY_NAME_LEN 20
+
+#define container_obj(layr) cfglu_container_of(layr, struct cfcnfg, layer)
+
+/* Information about CAIF physical interfaces held by Config Module in order
+ * to manage physical interfaces
+ */
+struct cfcnfg_phyinfo {
+ /* Pointer to the layer below the MUX (framing layer) */
+ struct layer *frm_layer;
+ /* Pointer to the lowest actual physical layer */
+ struct layer *phy_layer;
+ /* Unique identifier of the physical interface */
+ unsigned int id;
+ /* Preference of the physical in interface */
+ enum cfcnfg_phy_preference pref;
+
+ /* Reference count, number of channels using the device */
+ int phy_ref_count;
+
+ /* Information about the physical device */
+ struct dev_info dev_info;
+};
+
+struct cfcnfg {
+ struct layer layer;
+ struct layer *ctrl;
+ struct layer *mux;
+ uint8 last_phyid;
+ struct cfcnfg_phyinfo phy_layers[MAX_PHY_LAYERS];
+};
+
+static void cncfg_linkup_rsp(struct layer *layer, uint8 linkid,
+ enum cfctrl_srv serv, uint8 phyid,
+ struct layer *adapt_layer);
+static void cncfg_linkdestroy_rsp(struct layer *layer, uint8 linkid,
+ struct layer *client_layer);
+static void cncfg_reject_rsp(struct layer *layer, uint8 linkid,
+ struct layer *adapt_layer);
+static void cfctrl_resp_func(void);
+static void cfctrl_enum_resp(void);
+
+struct cfcnfg *cfcnfg_create()
+{
+ struct cfcnfg *this;
+ struct cfctrl_rsp *resp;
+ /* Initiate this layer */
+ this = cfglu_alloc(sizeof(struct cfcnfg));
+ if (!this) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return NULL;
+ }
+ memset(this, 0, sizeof(struct cfcnfg));
+ this->mux = cfmuxl_create();
+ if (!this->mux)
+ goto out_of_mem;
+ this->ctrl = cfctrl_create();
+ if (!this->ctrl)
+ goto out_of_mem;
+ /* Initiate response functions */
+ resp = cfctrl_get_respfuncs(this->ctrl);
+ resp->enum_rsp = cfctrl_enum_resp;
+ resp->linkerror_ind = cfctrl_resp_func;
+ resp->linkdestroy_rsp = cncfg_linkdestroy_rsp;
+ resp->sleep_rsp = cfctrl_resp_func;
+ resp->wake_rsp = cfctrl_resp_func;
+ resp->restart_rsp = cfctrl_resp_func;
+ resp->radioset_rsp = cfctrl_resp_func;
+ resp->linksetup_rsp = cncfg_linkup_rsp;
+ resp->reject_rsp = cncfg_reject_rsp;
+
+ this->last_phyid = 1;
+
+ cfmuxl_set_uplayer(this->mux, this->ctrl, 0);
+ layer_set_dn(this->ctrl, this->mux);
+ layer_set_up(this->ctrl, this);
+ return this;
+out_of_mem:
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ cfglu_free(this->mux);
+ cfglu_free(this->ctrl);
+ cfglu_free(this);
+ return NULL;
+}
+EXPORT_SYMBOL(cfcnfg_create);
+
+void cfcnfg_remove(struct cfcnfg *cfg)
+{
+ if (cfg) {
+ cfglu_free(cfg->mux);
+ cfglu_free(cfg->ctrl);
+ cfglu_free(cfg);
+ }
+}
+
+static void cfctrl_resp_func(void)
+{
+}
+
+static void cfctrl_enum_resp(void)
+{
+}
+
+struct dev_info *cfcnfg_get_phyid(struct cfcnfg *cnfg,
+ enum cfcnfg_phy_preference phy_pref)
+{
+ int i;
+
+ /* Try to match with specified preference */
+ for (i = 1; i < MAX_PHY_LAYERS; i++) {
+ if (cnfg->phy_layers[i].id == i &&
+ cnfg->phy_layers[i].pref == phy_pref &&
+ cnfg->phy_layers[i].frm_layer != NULL) {
+ caif_assert(cnfg->phy_layers != NULL);
+ caif_assert(cnfg->phy_layers[i].id == i);
+ return &cnfg->phy_layers[i].dev_info;
+ }
+ }
+ /* Otherwise just return something */
+ for (i = 1; i < MAX_PHY_LAYERS; i++) {
+ if (cnfg->phy_layers[i].id == i) {
+ caif_assert(cnfg->phy_layers != NULL);
+ caif_assert(cnfg->phy_layers[i].id == i);
+ return &cnfg->phy_layers[i].dev_info;
+ }
+ }
+
+ return NULL;
+}
+
+static struct cfcnfg_phyinfo *cfcnfg_get_phyinfo(struct cfcnfg *cnfg,
+ uint8 phyid)
+{
+ int i;
+
+ /* Try to match with specified preference */
+ for (i = 0; i < MAX_PHY_LAYERS; i++)
+ if (cnfg->phy_layers[i].frm_layer != NULL &&
+ cnfg->phy_layers[i].id == phyid)
+ return &cnfg->phy_layers[i];
+ return 0;
+}
+
+int cfcnfg_get_named(struct cfcnfg *cnfg, char *name)
+{
+ int i;
+
+ /* Try to match with specified preference */
+ for (i = 0; i < MAX_PHY_LAYERS; i++) {
+ if (cnfg->phy_layers[i].frm_layer != NULL
+ && strcmp(cnfg->phy_layers[i].frm_layer->name,
+ name) == 0) {
+ return cnfg->phy_layers[i].frm_layer->id;
+ }
+ }
+ return 0;
+}
+
+/*
+ * NOTE: What happens on destroy failure:
+ * 1a) No response - Too early
+ * This will not happen because enumerate has already
+ * completed.
+ * 1b) No response - FATAL
+ * Not handled, but this should be a CAIF PROTOCOL ERROR
+ * Modem error, response is really expected - this
+ * case is not really handled.
+ * 2) O/E-bit indicate error
+ * Ignored - this link is destroyed anyway.
+ * 3) Not able to match on request
+ * Not handled, but this should be a CAIF PROTOCOL ERROR
+ * 4) Link-Error - (no response)
+ * Not handled, but this should be a CAIF PROTOCOL ERROR
+ */
+
+int cfcnfg_del_adapt_layer(struct cfcnfg *cnfg, struct layer *adap_layer)
+{
+ uint8 channel_id = 0;
+ int ret = 0;
+ struct cfcnfg_phyinfo *phyinfo = NULL;
+ uint8 phyid = 0;
+
+ caif_assert(adap_layer != NULL);
+ channel_id = adap_layer->id;
+ if (channel_id == 0) {
+ pr_err("CAIF: %s():adap_layer->id is 0\n", __func__);
+ ret = CFGLU_ENOTCONN;
+ goto end;
+ }
+
+ if (adap_layer->dn == NULL) {
+ pr_err("CAIF: %s():adap_layer->dn is NULL\n", __func__);
+ ret = CFGLU_ENODEV;
+ goto end;
+ }
+
+ if (adap_layer->dn != NULL)
+ phyid = cfsrvl_getphyid(adap_layer->dn);
+
+ phyinfo = cfcnfg_get_phyinfo(cnfg, phyid);
+ if (phyinfo == NULL) {
+ pr_warning("CAIF: %s(): No interface to send disconnect to\n",
+ __func__);
+ ret = CFGLU_ENODEV;
+ goto end;
+ }
+
+ if (phyinfo->id != phyid
+ || phyinfo->phy_layer->id != phyid
+ || phyinfo->frm_layer->id != phyid) {
+
+ pr_err("CAIF: %s(): Inconsistency in phy registration\n",
+ __func__);
+ ret = CFGLU_EINVAL;
+ goto end;
+ }
+
+ ret = cfctrl_linkdown_req(cnfg->ctrl, channel_id, adap_layer);
+
+end:
+ if (phyinfo != NULL && --phyinfo->phy_ref_count == 0 &&
+ phyinfo->phy_layer != NULL &&
+ phyinfo->phy_layer->modemcmd != NULL) {
+ phyinfo->phy_layer->modemcmd(phyinfo->phy_layer,
+ _CAIF_MODEMCMD_PHYIF_USELESS);
+ }
+ return ret;
+
+}
+EXPORT_SYMBOL(cfcnfg_del_adapt_layer);
+
+static void cncfg_linkdestroy_rsp(struct layer *layer, uint8 linkid,
+ struct layer *client_layer)
+{
+ struct cfcnfg *cnfg = container_obj(layer);
+ struct layer *servl;
+
+ /*
+ * 1) Remove service from the MUX layer. The MUX must
+ * guarante that no more payload sent "upwards" (receive)
+ */
+ servl = cfmuxl_remove_uplayer(cnfg->mux, linkid);
+
+ if (servl == NULL) {
+ pr_err("CAIF: %s(): PROTOCOL ERROR "
+ "- Error removing service_layer Linkid(%d)",
+ __func__, linkid);
+ return;
+ }
+ caif_assert(linkid == servl->id);
+
+ if (servl != client_layer && servl->up != client_layer) {
+ pr_err("CAIF: %s(): Error removing service_layer "
+ "Linkid(%d) %p %p",
+ __func__, linkid, (void *) servl,
+ (void *) client_layer);
+ return;
+ }
+
+ /*
+ * 2) DEINIT_RSP must guarantee that no more packets are transmitted
+ * from client (adap_layer) when it returns.
+ */
+
+ if (servl->ctrlcmd == NULL) {
+ pr_err("CAIF: %s(): Error servl->ctrlcmd == NULL", __func__);
+ return;
+ }
+
+ servl->ctrlcmd(servl, CAIF_CTRLCMD_DEINIT_RSP, 0);
+
+ /* 3) It is now safe to destroy the service layer. */
+
+ if (client_layer != servl->up)
+ cfservl_destroy(servl);
+}
+
+/*
+ * NOTE: What happens on linksetup failure:
+ * 1a) No response - Too early
+ * This will not happen because enumerate is secured
+ * before using interface.
+ * 1b) No response - FATAL
+ * Not handled, but this should be a CAIF PROTOCOL ERROR
+ * Modem error, response is really expected - this case is
+ * not really handled.
+ * 2) O/E-bit indicate error
+ * Handled in cnfg_reject_rsp
+ * 3) Not able to match on request
+ * Not handled, but this should be a CAIF PROTOCOL ERROR
+ * 4) Link-Error - (no response)
+ * Not handled, but this should be a CAIF PROTOCOL ERROR
+ */
+
+bool
+cfcnfg_add_adaptation_layer(struct cfcnfg *cnfg,
+ struct cfctrl_link_param *param,
+ struct layer *adap_layer)
+{
+ struct layer *frml;
+ if (adap_layer == NULL) {
+ pr_err("CAIF: %s(): adap_layer is zero", __func__);
+ return CFGLU_EINVAL;
+ }
+ if (adap_layer->receive == NULL) {
+ pr_err("CAIF: %s(): adap_layer->receive is NULL", __func__);
+ return CFGLU_EINVAL;
+ }
+ if (adap_layer->ctrlcmd == NULL) {
+ pr_err("CAIF: %s(): adap_layer->ctrlcmd == NULL", __func__);
+ return CFGLU_EINVAL;
+ }
+ frml = cnfg->phy_layers[param->phyid].frm_layer;
+ if (frml == NULL) {
+ pr_err("CAIF: %s(): Specified PHY type does not exist!",
+ __func__);
+ return CFGLU_ENODEV;
+ }
+ caif_assert(param->phyid == cnfg->phy_layers[param->phyid].id);
+ caif_assert(cnfg->phy_layers[param->phyid].frm_layer->id ==
+ param->phyid);
+ caif_assert(cnfg->phy_layers[param->phyid].phy_layer->id ==
+ param->phyid);
+ /* FIXME: ENUMERATE INITIALLY WHEN ACTIVATING PHYSICAL INTERFACE */
+ cfctrl_enum_req(cnfg->ctrl, param->phyid);
+ cfctrl_linkup_request(cnfg->ctrl, param, adap_layer);
+ return 0;
+}
+EXPORT_SYMBOL(cfcnfg_add_adaptation_layer);
+
+static void cncfg_reject_rsp(struct layer *layer, uint8 linkid,
+ struct layer *adapt_layer)
+{
+ if (adapt_layer != NULL && adapt_layer->ctrlcmd != NULL)
+ adapt_layer->ctrlcmd(adapt_layer,
+ CAIF_CTRLCMD_INIT_FAIL_RSP, 0);
+}
+
+static void
+cncfg_linkup_rsp(struct layer *layer, uint8 linkid, enum cfctrl_srv serv,
+ uint8 phyid, struct layer *adapt_layer)
+{
+ struct cfcnfg *cnfg = container_obj(layer);
+ struct layer *servicel = NULL;
+ struct cfcnfg_phyinfo *phyinfo;
+ if (adapt_layer == NULL) {
+ pr_err("CAIF: %s(): PROTOCOL ERROR "
+ "- LinkUp Request/Response did not match\n", __func__);
+ return;
+ }
+
+ caif_assert(cnfg != NULL);
+ caif_assert(phyid != 0);
+ phyinfo = &cnfg->phy_layers[phyid];
+ caif_assert(phyinfo != NULL);
+ caif_assert(phyinfo->id == phyid);
+ caif_assert(phyinfo->phy_layer != NULL);
+ caif_assert(phyinfo->phy_layer->id == phyid);
+
+ if (phyinfo != NULL &&
+ phyinfo->phy_ref_count++ == 0 &&
+ phyinfo->phy_layer != NULL &&
+ phyinfo->phy_layer->modemcmd != NULL) {
+ caif_assert(phyinfo->phy_layer->id == phyid);
+ phyinfo->phy_layer->modemcmd(phyinfo->phy_layer,
+ _CAIF_MODEMCMD_PHYIF_USEFULL);
+
+ }
+ adapt_layer->id = linkid;
+
+ switch (serv) {
+ case CFCTRL_SRV_VEI:
+ servicel = cfvei_create(linkid, &phyinfo->dev_info);
+ break;
+ case CFCTRL_SRV_DATAGRAM:
+ servicel = cfdgml_create(linkid, &phyinfo->dev_info);
+ break;
+ case CFCTRL_SRV_RFM:
+ servicel = cfrfml_create(linkid, &phyinfo->dev_info);
+ break;
+ case CFCTRL_SRV_UTIL:
+ servicel = cfutill_create(linkid, &phyinfo->dev_info);
+ break;
+ case CFCTRL_SRV_VIDEO:
+ servicel = cfvidl_create(linkid, &phyinfo->dev_info);
+ break;
+ case CFCTRL_SRV_DBG:
+ servicel = adapt_layer;
+ break;
+ default:
+ pr_err("CAIF: %s(): Protocol error. "
+ "Link setup response - unknown channel type\n",
+ __func__);
+ return;
+ }
+ if (!servicel) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return;
+ }
+ layer_set_dn(servicel, cnfg->mux);
+
+
+ cfmuxl_set_uplayer(cnfg->mux, servicel, linkid);
+ if (servicel != adapt_layer) {
+ layer_set_up(servicel, adapt_layer);
+ layer_set_dn(adapt_layer, servicel);
+ }
+ servicel->ctrlcmd(servicel, CAIF_CTRLCMD_INIT_RSP, 0);
+}
+
+void
+cfcnfg_add_phy_layer(struct cfcnfg *cnfg, enum cfcnfg_phy_type phy_type,
+ void *dev, struct layer *phy_layer, uint16 *phyid,
+ enum cfcnfg_phy_preference pref,
+ bool fcs, bool stx)
+{
+ struct layer *frml;
+ struct layer *phy_driver = NULL;
+ int i;
+
+
+ if (cnfg->phy_layers[cnfg->last_phyid].frm_layer == NULL) {
+ *phyid = cnfg->last_phyid;
+
+ /* range: * 1..(MAX_PHY_LAYERS-1) */
+ cnfg->last_phyid =
+ (cnfg->last_phyid % (MAX_PHY_LAYERS - 1)) + 1;
+ } else {
+ *phyid = 0;
+ for (i = 1; i < MAX_PHY_LAYERS; i++) {
+ if (cnfg->phy_layers[i].frm_layer == NULL) {
+ *phyid = i;
+ break;
+ }
+ }
+ }
+ if (*phyid == 0) {
+ pr_err("CAIF: %s(): No Available PHY ID\n", __func__);
+ return;
+ }
+
+ switch (phy_type) {
+ case CFPHYTYPE_FRAG:
+ fcs = true;
+ phy_driver =
+ cfserl_create(CFPHYTYPE_FRAG, *phyid, stx);
+ if (!phy_driver) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return;
+ }
+
+ break;
+ case CFPHYTYPE_CAIF:
+ phy_driver = NULL;
+ break;
+ default:
+ pr_err("CAIF: %s(): %d", __func__, phy_type);
+ return;
+ break;
+ }
+
+ phy_layer->id = *phyid;
+ cnfg->phy_layers[*phyid].pref = pref;
+ cnfg->phy_layers[*phyid].id = *phyid;
+ cnfg->phy_layers[*phyid].dev_info.id = *phyid;
+ cnfg->phy_layers[*phyid].dev_info.dev = dev;
+ cnfg->phy_layers[*phyid].phy_layer = phy_layer;
+ cnfg->phy_layers[*phyid].phy_ref_count = 0;
+ phy_layer->type = phy_type;
+ frml = cffrml_create(*phyid, fcs);
+ if (!frml) {
+ pr_warning("CAIF: %s(): Out of memory\n", __func__);
+ return;
+ }
+ cnfg->phy_layers[*phyid].frm_layer = frml;
+ cfmuxl_set_dnlayer(cnfg->mux, frml, *phyid);
+ layer_set_up(frml, cnfg->mux);
+
+ if (phy_driver != NULL) {
+ phy_driver->id = *phyid;
+ layer_set_dn(frml, phy_driver);
+ layer_set_up(phy_driver, frml);
+ layer_set_dn(phy_driver, phy_layer);
+ layer_set_up(phy_layer, phy_driver);
+ } else {
+ layer_set_dn(frml, phy_layer);
+ layer_set_up(phy_layer, frml);
+ }
+}
+EXPORT_SYMBOL(cfcnfg_add_phy_layer);
+
+int cfcnfg_del_phy_layer(struct cfcnfg *cnfg, struct layer *phy_layer)
+{
+ struct layer *frml, *frml_dn;
+ uint16 phyid;
+ phyid = phy_layer->id;
+ caif_assert(phyid == cnfg->phy_layers[phyid].id);
+ caif_assert(phy_layer == cnfg->phy_layers[phyid].phy_layer);
+ caif_assert(phy_layer->id == phyid);
+ caif_assert(cnfg->phy_layers[phyid].frm_layer->id == phyid);
+
+ memset(&cnfg->phy_layers[phy_layer->id], 0,
+ sizeof(struct cfcnfg_phyinfo));
+ frml = cfmuxl_remove_dnlayer(cnfg->mux, phy_layer->id);
+ frml_dn = frml->dn;
+ cffrml_set_uplayer(frml, NULL);
+ cffrml_set_dnlayer(frml, NULL);
+ cffrml_destroy(frml);
+
+ if (phy_layer != frml_dn) {
+ layer_set_up(frml_dn, NULL);
+ layer_set_dn(frml_dn, NULL);
+ cfglu_free(frml_dn);
+ }
+ layer_set_up(phy_layer, NULL);
+ return CFGLU_EOK;
+}
+EXPORT_SYMBOL(cfcnfg_del_phy_layer);
diff --git a/net/caif/generic/cflist.c b/net/caif/generic/cflist.c
new file mode 100644
index 0000000..05603bc
--- /dev/null
+++ b/net/caif/generic/cflist.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <net/caif/generic/cfglue.h>
+#include <net/caif/generic/cfpkt.h>
+#include <net/caif/generic/cflst.h>
+
+void cflst_init(struct layer **lst)
+{
+ *lst = NULL;
+}
+
+struct layer *cflst_remove(struct layer **lst, struct layer *elem)
+{
+ struct layer *tmp;
+ if (*lst == NULL)
+ return NULL;
+
+ tmp = (*lst);
+ (*lst) = (*lst)->next;
+ return tmp;
+}
+
+/* Adds an element from the queue. */
+void cflst_insert(struct layer **lst, struct layer *node)
+{
+ struct layer *tmp;
+ node->next = NULL;
+ if ((*lst) == NULL) {
+ (*lst) = node;
+ return;
+ }
+ tmp = *lst;
+ while (tmp->next != NULL)
+ tmp = tmp->next;
+ tmp->next = node;
+}
+
+int cflst_put(struct layer **lst, uint8 id, struct layer *node)
+{
+ if (cflst_get(lst, id) != NULL) {
+ pr_err("CAIF: %s(): cflst_put duplicate key\n", __func__);
+ return CFGLU_EINVAL;
+ }
+ node->id = id;
+ cflst_insert(lst, node);
+ return CFGLU_EOK;
+}
+
+struct layer *cflst_get(struct layer * *lst, uint8 id)
+{
+ struct layer *node;
+ for (node = (*lst); node != NULL; node = node->next) {
+ if (id == node->id)
+ return node;
+ }
+ return NULL;
+}
+
+struct layer *cflst_del(struct layer * *lst, uint8 id)
+{
+ struct layer *iter;
+ struct layer *node = NULL;
+
+ if ((*lst) == NULL)
+ return NULL;
+
+ if ((*lst)->id == id) {
+ node = (*lst);
+ (*lst) = (*lst)->next;
+ node->next = NULL;
+
+ return node;
+ }
+
+ for (iter = (*lst); iter->next != NULL; iter = iter->next) {
+ if (id == iter->next->id) {
+ node = iter->next;
+ iter->next = iter->next->next;
+ node->next = NULL;
+ return node;
+ }
+ }
+ return NULL;
+}
diff --git a/net/caif/generic/cfpkt_skbuff.c b/net/caif/generic/cfpkt_skbuff.c
new file mode 100644
index 0000000..1bf0b5e
--- /dev/null
+++ b/net/caif/generic/cfpkt_skbuff.c
@@ -0,0 +1,596 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/string.h>
+#include <linux/skbuff.h>
+#include <linux/hardirq.h>
+#include <net/caif/generic/cfglue.h>
+#include <net/caif/generic/cfpkt.h>
+
+#define PKT_PREFIX CAIF_NEEDED_HEADROOM
+#define PKT_POSTFIX CAIF_NEEDED_TAILROOM
+#define PKT_LEN_WHEN_EXTENDING 128
+#define PKT_ERROR(pkt, errmsg) do { \
+ cfpkt_priv(pkt)->erronous = true; \
+ skb_reset_tail_pointer(&pkt->skb); \
+ pr_warning("CAIF: " errmsg);\
+ } while (0)
+
+struct cfpktq {
+ struct sk_buff_head head;
+ cfglu_atomic_t count;
+ spinlock_t lock;
+};
+
+/*
+ * net/caif/generic/ is generic and does not
+ * understand SKB, so we do this typecast
+ */
+struct cfpkt {
+ struct sk_buff skb;
+};
+
+/* Private data inside SKB */
+struct cfpkt_priv_data {
+ struct dev_info dev_info;
+ bool erronous;
+};
+
+inline struct cfpkt_priv_data *cfpkt_priv(struct cfpkt *pkt)
+{
+ return (struct cfpkt_priv_data *) pkt->skb.cb;
+}
+
+inline bool is_erronous(struct cfpkt *pkt)
+{
+ return cfpkt_priv(pkt)->erronous;
+}
+
+inline struct sk_buff *pkt_to_skb(struct cfpkt *pkt)
+{
+ return &pkt->skb;
+}
+
+inline struct cfpkt *skb_to_pkt(struct sk_buff *skb)
+{
+ return (struct cfpkt *) skb;
+}
+
+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);
+
+void *cfpkt_tonative(struct cfpkt *pkt)
+{
+ return (void *) pkt;
+}
+EXPORT_SYMBOL(cfpkt_tonative);
+
+struct cfpkt *cfpkt_create_pfx(uint16 len, uint16 pfx)
+{
+ struct sk_buff *skb;
+
+ if (likely(in_interrupt()))
+ skb = alloc_skb(len + pfx, GFP_ATOMIC);
+ else
+ skb = alloc_skb(len + pfx, GFP_KERNEL);
+
+ if (unlikely(skb == NULL))
+ return NULL;
+
+ skb_reserve(skb, pfx);
+ cfglu_atomic_inc(cfpkt_packet_count);
+ return skb_to_pkt(skb);
+}
+
+inline struct cfpkt *cfpkt_create(uint16 len)
+{
+ return cfpkt_create_pfx(len + PKT_POSTFIX, PKT_PREFIX);
+}
+EXPORT_SYMBOL(cfpkt_create);
+
+void cfpkt_destroy(struct cfpkt *pkt)
+{
+ struct sk_buff *skb = pkt_to_skb(pkt);
+ cfglu_atomic_dec(cfpkt_packet_count);
+ caif_assert(cfglu_atomic_read(cfpkt_packet_count) >= 0);
+ kfree_skb(skb);
+}
+EXPORT_SYMBOL(cfpkt_destroy);
+
+inline bool cfpkt_more(struct cfpkt *pkt)
+{
+ struct sk_buff *skb = pkt_to_skb(pkt);
+ return skb->len > 0;
+}
+EXPORT_SYMBOL(cfpkt_more);
+
+int cfpkt_peek_head(struct cfpkt *pkt, void *data, uint16 len)
+{
+ struct sk_buff *skb = pkt_to_skb(pkt);
+ if (skb->tail - skb->data >= len) {
+ memcpy(data, skb->data, len);
+ return CFGLU_EOK;
+ }
+ 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)
+{
+ struct sk_buff *skb = pkt_to_skb(pkt);
+ uint8 *from;
+ if (unlikely(is_erronous(pkt)))
+ return CFGLU_EPKT;
+
+ if (unlikely(len > skb->len)) {
+ PKT_ERROR(pkt, "cfpkt_extr_head read beyond end of packet\n");
+ return CFGLU_EPKT;
+ }
+
+ if (unlikely(len > skb_headlen(skb))) {
+ if (unlikely(skb_linearize(skb) != 0)) {
+ PKT_ERROR(pkt, "cfpkt_extr_head linearize failed\n");
+ return CFGLU_EPKT;
+ }
+ }
+ from = skb_pull(skb, len);
+ from -= len;
+ memcpy(data, from, len);
+ return CFGLU_EOK;
+}
+EXPORT_SYMBOL(cfpkt_extr_head);
+
+int cfpkt_extr_trail(struct cfpkt *pkt, void *dta, uint16 len)
+{
+ struct sk_buff *skb = pkt_to_skb(pkt);
+ uint8 *data = dta;
+ uint8 *from;
+ if (unlikely(is_erronous(pkt)))
+ return CFGLU_EPKT;
+
+ if (unlikely(skb_linearize(skb) != 0)) {
+ PKT_ERROR(pkt, "cfpkt_extr_trail linearize failed\n");
+ return CFGLU_EPKT;
+ }
+ if (unlikely(skb->data + len > skb_tail_pointer(skb))) {
+ PKT_ERROR(pkt, "cfpkt_extr_trail read beyond end of packet\n");
+ return CFGLU_EPKT;
+ }
+ from = skb_tail_pointer(skb) - len;
+ skb_trim(skb, skb->len - len);
+ memcpy(data, from, len);
+ return CFGLU_EOK;
+}
+EXPORT_SYMBOL(cfpkt_extr_trail);
+
+int cfpkt_pad_trail(struct cfpkt *pkt, uint16 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)
+{
+ struct sk_buff *skb = pkt_to_skb(pkt);
+ struct sk_buff *lastskb;
+ uint8 *to;
+ uint16 addlen = 0;
+
+
+ if (unlikely(is_erronous(pkt)))
+ return CFGLU_EPKT;
+
+ lastskb = skb;
+
+ /* Check whether we need to add space at the tail */
+ if (unlikely(skb_tailroom(skb) < len)) {
+ if (likely(len < PKT_LEN_WHEN_EXTENDING))
+ addlen = PKT_LEN_WHEN_EXTENDING;
+ else
+ addlen = len;
+ }
+
+ /* Check whether we need to change the SKB before writing to the tail */
+ if (unlikely((addlen > 0) || skb_cloned(skb) || skb_shared(skb))) {
+
+ /* Make sure data is writable */
+ if (unlikely(skb_cow_data(skb, addlen, &lastskb) < 0)) {
+ PKT_ERROR(pkt, "cfpkt_add_body: cow failed\n");
+ return CFGLU_EPKT;
+ }
+ /*
+ * Is the SKB non-linear after skb_cow_data()? If so, we are
+ * going to add data to the last SKB, so we need to adjust
+ * lengths of the top SKB.
+ */
+ if (lastskb != skb) {
+ pr_warning("CAIF: %s(): Packet is non-linear\n",
+ __func__);
+ skb->len += len;
+ skb->data_len += len;
+ }
+ }
+
+ /* All set to put the last SKB and optionally write data there. */
+ to = skb_put(lastskb, len);
+ if (likely(data))
+ memcpy(to, data, len);
+ return CFGLU_EOK;
+}
+EXPORT_SYMBOL(cfpkt_add_body);
+
+inline int cfpkt_addbdy(struct cfpkt *pkt, uint8 data)
+{
+ return cfpkt_add_body(pkt, &data, 1);
+}
+EXPORT_SYMBOL(cfpkt_addbdy);
+
+int cfpkt_add_head(struct cfpkt *pkt, const void *data2, uint16 len)
+{
+ struct sk_buff *skb = pkt_to_skb(pkt);
+ struct sk_buff *lastskb;
+ uint8 *to;
+ const uint8 *data = data2;
+ if (unlikely(is_erronous(pkt)))
+ return CFGLU_EPKT;
+ if (unlikely(skb_headroom(skb) < len)) {
+ PKT_ERROR(pkt, "cfpkt_add_head: no headroom\n");
+ return CFGLU_EPKT;
+ }
+
+ /* 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;
+ }
+
+ to = skb_push(skb, len);
+ memcpy(to, data, len);
+ return CFGLU_EOK;
+}
+EXPORT_SYMBOL(cfpkt_add_head);
+
+inline int cfpkt_add_trail(struct cfpkt *pkt, const void *data, uint16 len)
+{
+ return cfpkt_add_body(pkt, data, len);
+}
+EXPORT_SYMBOL(cfpkt_add_trail);
+
+inline uint16 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)
+{
+ /*
+ * 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;
+ if (unlikely(skb_linearize(&pkt->skb) != 0)) {
+ PKT_ERROR(pkt, "cfpkt_iterate: linearize failed\n");
+ return CFGLU_EPKT;
+ }
+ return iter_func(data, pkt->skb.data, cfpkt_getlen(pkt));
+}
+EXPORT_SYMBOL(cfpkt_iterate);
+
+int cfpkt_setlen(struct cfpkt *pkt, uint16 len)
+{
+ struct sk_buff *skb = pkt_to_skb(pkt);
+
+
+ if (unlikely(is_erronous(pkt)))
+ return CFGLU_EPKT;
+
+ if (likely(len <= skb->len)) {
+ if (unlikely(skb->data_len))
+ ___pskb_trim(skb, len);
+ else
+ skb_trim(skb, len);
+
+ return cfpkt_getlen(pkt);
+ }
+
+ /* Need to expand SKB */
+ if (unlikely(!cfpkt_pad_trail(pkt, len - skb->len)))
+ PKT_ERROR(pkt, "cfpkt_setlen: skb_pad_trail failed\n");
+
+ return cfpkt_getlen(pkt);
+}
+EXPORT_SYMBOL(cfpkt_setlen);
+
+struct cfpkt *cfpkt_create_uplink(const unsigned char *data, unsigned int len)
+{
+ struct cfpkt *pkt = cfpkt_create_pfx(len + PKT_POSTFIX, PKT_PREFIX);
+ if (unlikely(data != NULL))
+ cfpkt_add_body(pkt, data, len);
+ return pkt;
+}
+EXPORT_SYMBOL(cfpkt_create_uplink);
+
+
+struct cfpkt *cfpkt_append(struct cfpkt *dstpkt,
+ struct cfpkt *addpkt,
+ uint16 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;
+ struct sk_buff *tmp;
+ uint16 dstlen;
+ uint16 createlen;
+ if (unlikely(is_erronous(dstpkt) || is_erronous(addpkt))) {
+ cfpkt_destroy(addpkt);
+ return dstpkt;
+ }
+ if (expectlen > addlen)
+ neededtailspace = expectlen;
+ else
+ neededtailspace = addlen;
+
+ if (dst->tail + neededtailspace > dst->end) {
+ /* Create a dumplicate of 'dst' with more tail space */
+ dstlen = dst->tail - dst->data;
+ createlen = dstlen + neededtailspace;
+ tmp = pkt_to_skb(
+ cfpkt_create(createlen + PKT_PREFIX + PKT_POSTFIX));
+ if (!tmp)
+ return NULL;
+ tmp->tail = tmp->data + dstlen;
+ tmp->len = dstlen;
+ memcpy(tmp->data, dst->data, dstlen);
+ cfpkt_destroy(dstpkt);
+ dst = tmp;
+ }
+ memcpy(dst->tail, add->data, add->tail - add->data);
+ cfpkt_destroy(addpkt);
+ dst->tail += addlen;
+ dst->len += addlen;
+ return skb_to_pkt(dst);
+}
+EXPORT_SYMBOL(cfpkt_append);
+
+struct cfpkt *cfpkt_split(struct cfpkt *pkt, uint16 pos)
+{
+ struct sk_buff *skb2;
+ struct sk_buff *skb = pkt_to_skb(pkt);
+ uint8 *split = skb->data + pos;
+ uint16 len2nd = skb->tail - split;
+
+ if (unlikely(is_erronous(pkt)))
+ return NULL;
+
+ if (skb->data + pos > skb->tail) {
+ PKT_ERROR(pkt,
+ "cfpkt_split: trying to split beyond end of packet");
+ return NULL;
+ }
+
+ /* Create a new packet for the second part of the data */
+ skb2 = pkt_to_skb(
+ cfpkt_create_pfx(len2nd + PKT_PREFIX + PKT_POSTFIX,
+ PKT_PREFIX));
+
+ if (skb2 == NULL)
+ return NULL;
+
+ /* Reduce the length of the original packet */
+ skb->tail = split;
+ skb->len = pos;
+
+ memcpy(skb2->data, split, len2nd);
+ skb2->tail += len2nd;
+ skb2->len += len2nd;
+ return skb_to_pkt(skb2);
+}
+EXPORT_SYMBOL(cfpkt_split);
+
+
+char *cfpkt_log_pkt(struct cfpkt *pkt, char *buf, int buflen)
+{
+ struct sk_buff *skb = pkt_to_skb(pkt);
+ char *p = buf;
+ int i;
+
+ /*
+ * Sanity check buffer length, it needs to be at least as large as
+ * the header info: ~=50+ bytes
+ */
+ if (buflen < 50)
+ return NULL;
+
+ snprintf(buf, buflen, "%s: pkt:%p len:%d(%d+%d) {%d,%d} data: [",
+ is_erronous(pkt) ? "ERRONOUS-SKB" :
+ (skb->data_len != 0 ? "COMPLEX-SKB" : "SKB"),
+ skb,
+ skb->len,
+ skb->tail - skb->data,
+ skb->data_len,
+ skb->data - skb->head, skb->tail - skb->head);
+ p = buf + strlen(buf);
+
+ for (i = 0; i < skb->tail - skb->data && i < 300; i++) {
+ if (p > buf + buflen - 10) {
+ sprintf(p, "...");
+ p = buf + strlen(buf);
+ break;
+ }
+ sprintf(p, "%02x,", skb->data[i]);
+ p = buf + strlen(buf);
+ }
+ sprintf(p, "]\n");
+ return buf;
+}
+EXPORT_SYMBOL(cfpkt_log_pkt);
+
+int cfpkt_raw_append(struct cfpkt *pkt, void **buf, unsigned int buflen)
+{
+ struct sk_buff *skb = pkt_to_skb(pkt);
+ struct sk_buff *lastskb;
+
+ caif_assert(buf != NULL);
+ if (unlikely(is_erronous(pkt)))
+ return CFGLU_EPKT;
+ /* 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;
+ }
+
+ if (unlikely(skb_linearize(skb) != 0)) {
+ PKT_ERROR(pkt, "cfpkt_raw_append: linearize failed\n");
+ return CFGLU_EPKT;
+ }
+
+ if (unlikely(skb_tailroom(skb) < buflen)) {
+ PKT_ERROR(pkt, "cfpkt_raw_append: buffer too short - failed\n");
+ return CFGLU_EPKT;
+ }
+
+ *buf = skb_put(skb, buflen);
+ return 1;
+}
+EXPORT_SYMBOL(cfpkt_raw_append);
+
+int cfpkt_raw_extract(struct cfpkt *pkt, void **buf, unsigned int buflen)
+{
+ struct sk_buff *skb = pkt_to_skb(pkt);
+
+ caif_assert(buf != NULL);
+ if (unlikely(is_erronous(pkt)))
+ return CFGLU_EPKT;
+
+ if (unlikely(buflen > skb->len)) {
+ PKT_ERROR(pkt, "cfpkt_raw_extract: buflen too large "
+ "- failed\n");
+ return CFGLU_EPKT;
+ }
+
+ if (unlikely(buflen > skb_headlen(skb))) {
+ if (unlikely(skb_linearize(skb) != 0)) {
+ PKT_ERROR(pkt, "cfpkt_raw_extract: linearize failed\n");
+ return CFGLU_EPKT;
+ }
+ }
+
+ *buf = skb->data;
+ skb_pull(skb, buflen);
+
+ return 1;
+}
+EXPORT_SYMBOL(cfpkt_raw_extract);
+
+inline bool cfpkt_erroneous(struct cfpkt *pkt)
+{
+ return cfpkt_priv(pkt)->erronous;
+}
+EXPORT_SYMBOL(cfpkt_erroneous);
+
+struct cfpkt *cfpkt_create_pkt(enum caif_direction dir,
+ const unsigned char *data, unsigned int len)
+{
+ struct cfpkt *pkt;
+ if (dir == CAIF_DIR_OUT)
+ pkt = cfpkt_create_pfx(len + PKT_POSTFIX, PKT_PREFIX);
+ else
+ pkt = cfpkt_create_pfx(len, 0);
+ if (unlikely(!pkt))
+ return NULL;
+ if (unlikely(data))
+ cfpkt_add_body(pkt, data, len);
+ cfpkt_priv(pkt)->erronous = false;
+ return pkt;
+}
+EXPORT_SYMBOL(cfpkt_create_pkt);
+
+struct cfpktq *cfpktq_create()
+{
+ struct cfpktq *q = cfglu_alloc(sizeof(struct cfpktq));
+ if (!q)
+ return NULL;
+ skb_queue_head_init(&q->head);
+ cfglu_atomic_set(q->count, 0);
+ spin_lock_init(&q->lock);
+ return q;
+}
+EXPORT_SYMBOL(cfpktq_create);
+
+void cfpkt_queue(struct cfpktq *pktq, struct cfpkt *pkt, unsigned short prio)
+{
+ cfglu_atomic_inc(pktq->count);
+ spin_lock(&pktq->lock);
+ skb_queue_tail(&pktq->head, pkt_to_skb(pkt));
+ spin_unlock(&pktq->lock);
+
+}
+EXPORT_SYMBOL(cfpkt_queue);
+
+struct cfpkt *cfpkt_qpeek(struct cfpktq *pktq)
+{
+ struct cfpkt *tmp;
+ spin_lock(&pktq->lock);
+ tmp = skb_to_pkt(skb_peek(&pktq->head));
+ spin_unlock(&pktq->lock);
+ return tmp;
+}
+EXPORT_SYMBOL(cfpkt_qpeek);
+
+struct cfpkt *cfpkt_dequeue(struct cfpktq *pktq)
+{
+ struct cfpkt *pkt;
+ spin_lock(&pktq->lock);
+ pkt = skb_to_pkt(skb_dequeue(&pktq->head));
+ if (pkt) {
+ cfglu_atomic_dec(pktq->count);
+ caif_assert(cfglu_atomic_read(pktq->count) >= 0);
+ }
+ spin_unlock(&pktq->lock);
+ return pkt;
+}
+EXPORT_SYMBOL(cfpkt_dequeue);
+
+int cfpkt_qcount(struct cfpktq *pktq)
+{
+ return cfglu_atomic_read(pktq->count);
+}
+EXPORT_SYMBOL(cfpkt_qcount);
+
+struct cfpkt *cfpkt_clone_release(struct cfpkt *pkt)
+{
+ struct cfpkt *clone;
+ clone = skb_to_pkt(skb_clone(pkt_to_skb(pkt), GFP_ATOMIC));
+ /* Free original packet. */
+ cfpkt_destroy(pkt);
+ if (!clone)
+ return NULL;
+ cfglu_atomic_inc(cfpkt_packet_count);
+ return clone;
+}
+EXPORT_SYMBOL(cfpkt_clone_release);
+
+struct payload_info *cfpkt_info(struct cfpkt *pkt)
+{
+ return (struct payload_info *)&pkt_to_skb(pkt)->cb;
+}
+EXPORT_SYMBOL(cfpkt_info);
--
1.6.3.3
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PATCH net-next-2.6 07/13] net-caif: add CAIF device registration functionality
2010-01-20 22:55 [PATCH net-next-2.6 00/13] net-caif: introducing CAIF protocol stack sjur.brandeland
` (5 preceding siblings ...)
2010-01-20 22:55 ` [PATCH net-next-2.6 06/13] net-caif: add CAIF generic caif support functions sjur.brandeland
@ 2010-01-20 22:55 ` sjur.brandeland
2010-01-20 22:55 ` [PATCH net-next-2.6 08/13] net-caif: add CAIF socket implementation sjur.brandeland
` (6 subsequent siblings)
13 siblings, 0 replies; 47+ messages in thread
From: sjur.brandeland @ 2010-01-20 22:55 UTC (permalink / raw)
To: netdev; +Cc: davem, marcel, stefano.babic, randy.dunlap, Sjur Braendeland
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
Registration and deregistration of CAIF Link Layer.
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
net/caif/caif_config_util.c | 121 +++++++++++++
net/caif/caif_dev.c | 417 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 538 insertions(+), 0 deletions(-)
diff --git a/net/caif/caif_config_util.c b/net/caif/caif_config_util.c
new file mode 100644
index 0000000..b9ae530
--- /dev/null
+++ b/net/caif/caif_config_util.c
@@ -0,0 +1,121 @@
+/*
+ * 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>
+
+int
+channel_config_2_link_param(struct cfcnfg *cnfg,
+ struct caif_channel_config *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->phy_name[0] != '\0') {
+ l->phyid = cfcnfg_get_named(cnfg, s->phy_name);
+ } 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;
+ default:
+ return -CFGLU_ENODEV;
+ }
+ dev_info = cfcnfg_get_phyid(cnfg, pref);
+ if (dev_info == NULL)
+ return -CFGLU_ENODEV;
+ l->phyid = dev_info->id;
+ }
+ switch (s->type) {
+ case CAIF_CHTY_AT:
+ l->linktype = CFCTRL_SRV_VEI;
+ l->chtype = 0x02;
+ l->endpoint = 0x00;
+ break;
+ case CAIF_CHTY_AT_CTRL:
+ l->linktype = CFCTRL_SRV_VEI;
+ l->chtype = 0x00;
+ l->endpoint = 0x00;
+ break;
+ case CAIF_CHTY_AT_PAIRED:
+ l->linktype = CFCTRL_SRV_VEI;
+ l->chtype = 0x03;
+ l->endpoint = 0x00;
+ break;
+ case CAIF_CHTY_VIDEO:
+ 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:
+ l->linktype = CFCTRL_SRV_DATAGRAM;
+ l->chtype = 0x00;
+ l->u.datagram.connid = s->u.dgm.connection_id;
+ break;
+ case CAIF_CHTY_DATAGRAM_LOOP:
+ 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:
+ 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:
+ l->linktype = CFCTRL_SRV_DBG;
+ l->endpoint = 0x01; /* ACC SIDE */
+ l->chtype = 0x02; /* Interactive debug only */
+ break;
+ case CAIF_CHTY_DEBUG_TRACE:
+ l->linktype = CFCTRL_SRV_DBG;
+ l->endpoint = 0x01; /* ACC SIDE */
+ l->chtype = 0x01; /* Debug print-out only */
+ break;
+ case CAIF_CHTY_RFM:
+ 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)-1);
+ l->u.rfm.volume[sizeof(l->u.rfm.volume)-1] = 0;
+ break;
+ case CAIF_CHTY_UTILITY:
+ 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)-1);
+ l->u.utility.name[sizeof(l->u.utility.name)-1] = 0;
+ 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;
+ default:
+ return -CFGLU_EINVAL;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(channel_config_2_link_param);
diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c
new file mode 100644
index 0000000..a268ddd
--- /dev/null
+++ b/net/caif/caif_dev.c
@@ -0,0 +1,417 @@
+/*
+ * CAIF Interface registration.
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ * Borrowed heavily from file: pn_dev.c. Thanks to
+ * Remi Denis-Courmont <remi.denis-courmont@nokia.com>
+ * and Sakari Ailus <sakari.ailus@nokia.com>
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/if_arp.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <net/netns/generic.h>
+#include <net/net_namespace.h>
+#include <net/pkt_sched.h>
+#include <net/caif/caif_device.h>
+#include <net/caif/caif_dev.h>
+#include <net/caif/generic/caif_layer.h>
+#include <net/caif/generic/cfpkt.h>
+#include <net/caif/generic/cfcnfg.h>
+
+MODULE_LICENSE("GPL");
+#define TIMEOUT (HZ*1000)
+
+/* Used for local tracking of the CAIF net devices */
+struct caif_device_entry {
+ struct layer layer;
+ struct list_head list;
+ atomic_t in_use;
+ atomic_t state;
+ uint16 phyid;
+ struct net_device *netdev;
+ wait_queue_head_t event;
+};
+
+struct caif_device_entry_list {
+ struct list_head list;
+ spinlock_t lock;
+};
+
+struct caif_net {
+ struct caif_device_entry_list caifdevs;
+};
+
+int caif_net_id;
+struct cfcnfg *cfg;
+
+struct caif_device_entry_list *caif_device_list(struct net *net)
+{
+ struct caif_net *caifn;
+ BUG_ON(!net);
+ caifn = net_generic(net, caif_net_id);
+ BUG_ON(!caifn);
+ return &caifn->caifdevs;
+}
+
+/* Allocate new CAIF device. */
+static struct caif_device_entry *caif_device_alloc(struct net_device *dev)
+{
+ struct caif_device_entry_list *caifdevs;
+ struct caif_device_entry *caifd;
+ caifdevs = caif_device_list(dev_net(dev));
+ BUG_ON(!caifdevs);
+ caifd = kzalloc(sizeof(*caifd), GFP_ATOMIC);
+ if (!caifd)
+ return NULL;
+ caifd->netdev = dev;
+ list_add(&caifd->list, &caifdevs->list);
+ init_waitqueue_head(&caifd->event);
+ return caifd;
+}
+
+static struct caif_device_entry *caif_get(struct net_device *dev)
+{
+ struct caif_device_entry_list *caifdevs =
+ caif_device_list(dev_net(dev));
+ struct caif_device_entry *caifd;
+ BUG_ON(!caifdevs);
+ list_for_each_entry(caifd, &caifdevs->list, list) {
+ if (caifd->netdev == dev)
+ return caifd;
+ }
+ return NULL;
+}
+
+static void caif_device_destroy(struct net_device *dev)
+{
+ struct caif_device_entry_list *caifdevs =
+ caif_device_list(dev_net(dev));
+ struct caif_device_entry *caifd;
+ ASSERT_RTNL();
+ if (dev->type != ARPHRD_CAIF)
+ return;
+
+ spin_lock_bh(&caifdevs->lock);
+ caifd = caif_get(dev);
+ if (caifd == NULL) {
+ spin_unlock_bh(&caifdevs->lock);
+ return;
+ }
+
+ list_del(&caifd->list);
+ spin_unlock_bh(&caifdevs->lock);
+
+ kfree(caifd);
+ return;
+}
+
+static int transmit(struct layer *layer, struct cfpkt *pkt)
+{
+ struct caif_device_entry *caifd =
+ container_of(layer, struct caif_device_entry, layer);
+ struct sk_buff *skb, *skb2;
+ int ret = -EINVAL;
+ skb = cfpkt_tonative(pkt);
+ skb->dev = caifd->netdev;
+ /*
+ * Don't allow SKB to be destroyed upon error, but signal resend
+ * notification to clients. We can't rely on the return value as
+ * congestion (NET_XMIT_CN) sometimes drops the packet, sometimes don't.
+ */
+ if (netif_queue_stopped(caifd->netdev))
+ return -EAGAIN;
+ skb2 = skb_get(skb);
+
+ ret = dev_queue_xmit(skb2);
+
+ if (!ret)
+ kfree_skb(skb);
+ else
+ return -EAGAIN;
+
+ return 0;
+}
+
+static int modemcmd(struct layer *layr, enum caif_modemcmd ctrl)
+{
+ struct caif_device_entry *caifd;
+ struct caif_dev_common *caifdev;
+ caifd = container_of(layr, struct caif_device_entry, layer);
+ caifdev = netdev_priv(caifd->netdev);
+ if (ctrl == _CAIF_MODEMCMD_PHYIF_USEFULL) {
+ atomic_set(&caifd->in_use, 1);
+ wake_up_interruptible(&caifd->event);
+
+ } else if (ctrl == _CAIF_MODEMCMD_PHYIF_USELESS) {
+ atomic_set(&caifd->in_use, 0);
+ wake_up_interruptible(&caifd->event);
+ }
+ return 0;
+}
+
+/*
+ * Stuff received packets to associated sockets.
+ * On error, returns non-zero and releases the skb.
+ */
+static int receive(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *pkttype, struct net_device *orig_dev)
+{
+ struct net *net;
+ struct cfpkt *pkt;
+ struct caif_device_entry *caifd;
+ net = dev_net(dev);
+ pkt = cfpkt_fromnative(CAIF_DIR_IN, skb);
+ caifd = caif_get(dev);
+ if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd)
+ return NET_RX_DROP;
+
+ if (caifd->layer.up->receive(caifd->layer.up, pkt))
+ return NET_RX_DROP;
+
+ return 0;
+}
+
+static struct packet_type caif_packet_type __read_mostly = {
+ .type = cpu_to_be16(ETH_P_CAIF),
+ .func = receive,
+};
+
+static void dev_flowctrl(struct net_device *dev, int on)
+{
+ struct caif_device_entry *caifd = caif_get(dev);
+ if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd)
+ return;
+
+ caifd->layer.up->ctrlcmd(caifd->layer.up,
+ on ?
+ _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND :
+ _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND,
+ caifd->layer.id);
+}
+
+/* notify Caif of device events */
+static int caif_device_notify(struct notifier_block *me, unsigned long what,
+ void *arg)
+{
+ struct net_device *dev = arg;
+ struct caif_device_entry *caifd = NULL;
+ struct caif_dev_common *caifdev;
+ int res = -EINVAL;
+ enum caif_phy_preference phy_pref;
+ enum cfcnfg_phy_type phy_type;
+
+ if (dev->type != ARPHRD_CAIF)
+ return 0;
+
+ switch (what) {
+ case NETDEV_REGISTER:
+ pr_info("CAIF: %s():register %s\n", __func__, dev->name);
+ caifd = caif_device_alloc(dev);
+ if (caifd == NULL)
+ break;
+ caifdev = netdev_priv(dev);
+ caifdev->flowctrl = dev_flowctrl;
+ atomic_set(&caifd->state, what);
+ res = 0;
+ break;
+
+ case NETDEV_UP:
+ pr_info("CAIF: %s(): up %s\n", __func__, dev->name);
+ caifd = caif_get(dev);
+ if (caifd == NULL)
+ break;
+ caifdev = netdev_priv(dev);
+ if (atomic_read(&caifd->state) == NETDEV_UP) {
+ pr_info("CAIF: %s():%s already up\n",
+ __func__, dev->name);
+ break;
+ }
+ atomic_set(&caifd->state, what);
+ caifd->layer.transmit = transmit;
+ caifd->layer.modemcmd = modemcmd;
+ if (caifdev->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;
+
+ cfcnfg_add_phy_layer(get_caif_conf(),
+ phy_type,
+ dev,
+ &caifd->layer,
+ &caifd->phyid,
+ phy_pref,
+ caifdev->use_fcs,
+ caifdev->use_stx);
+ strncpy(caifd->layer.name, dev->name,
+ sizeof(caifd->layer.name) - 1);
+ caifd->layer.name[sizeof(caifd->layer.name) - 1] = 0;
+ break;
+
+ case NETDEV_GOING_DOWN:
+ caifd = caif_get(dev);
+ if (caifd == NULL)
+ break;
+ pr_info("CAIF: %s():going down %s\n", __func__, dev->name);
+ atomic_set(&caifd->state, what);
+ if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd)
+ return -EINVAL;
+ caifd->layer.up->ctrlcmd(caifd->layer.up,
+ _CAIF_CTRLCMD_PHYIF_DOWN_IND,
+ caifd->layer.id);
+ res = wait_event_interruptible_timeout(caifd->event,
+ atomic_read(&caifd->in_use) == 0,
+ TIMEOUT);
+ break;
+
+ case NETDEV_DOWN:
+ caifd = caif_get(dev);
+ if (caifd == NULL)
+ break;
+ pr_info("CAIF: %s(): down %s\n", __func__, dev->name);
+ if (atomic_read(&caifd->in_use))
+ pr_warning("CAIF: %s(): "
+ "Unregistering an active CAIF device: %s\n",
+ __func__, dev->name);
+ cfcnfg_del_phy_layer(get_caif_conf(), &caifd->layer);
+ atomic_set(&caifd->state, what);
+ break;
+
+ case NETDEV_UNREGISTER:
+ caifd = caif_get(dev);
+ pr_info("CAIF: %s(): unregister %s\n", __func__, dev->name);
+ atomic_set(&caifd->state, what);
+ caif_device_destroy(dev);
+ break;
+ }
+ return 0;
+}
+
+static struct notifier_block caif_device_notifier = {
+ .notifier_call = caif_device_notify,
+ .priority = 0,
+};
+
+
+struct cfcnfg *get_caif_conf(void)
+{
+ return cfg;
+}
+EXPORT_SYMBOL(get_caif_conf);
+
+static int (*caif_ioctl_func)(unsigned int cmd, unsigned long arg, bool);
+
+void caif_register_ioctl(
+ int (*ioctl)(unsigned int cmd,
+ unsigned long arg,
+ bool))
+{
+ caif_ioctl_func = ioctl;
+}
+EXPORT_SYMBOL(caif_register_ioctl);
+
+int caif_ioctl(unsigned int cmd, unsigned long arg, bool from_user_land)
+{
+ if (caif_ioctl_func == NULL)
+ return -EINVAL;
+ return caif_ioctl_func(cmd, arg, from_user_land);
+}
+EXPORT_SYMBOL(caif_ioctl);
+
+int caifdev_adapt_register(struct caif_channel_config *config,
+ struct layer *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(caifdev_adapt_register);
+
+int caifdev_adapt_unregister(struct layer *adap_layer)
+{
+ return cfcnfg_del_adapt_layer(get_caif_conf(), adap_layer);
+}
+EXPORT_SYMBOL(caifdev_adapt_unregister);
+
+/* Per-namespace Caif devices handling */
+static int caif_init_net(struct net *net)
+{
+ struct caif_net *caifn = net_generic(net, caif_net_id);
+ INIT_LIST_HEAD(&caifn->caifdevs.list);
+ spin_lock_init(&caifn->caifdevs.lock);
+ return 0;
+}
+
+static void caif_exit_net(struct net *net)
+{
+ struct net_device *dev;
+ int res;
+ rtnl_lock();
+ for_each_netdev(net, dev) {
+ if (dev->type != ARPHRD_CAIF)
+ continue;
+ res = dev_close(dev);
+ caif_device_destroy(dev);
+ }
+ rtnl_unlock();
+}
+
+static struct pernet_operations caif_net_ops = {
+ .init = caif_init_net,
+ .exit = caif_exit_net,
+ .id = &caif_net_id,
+ .size = sizeof(struct caif_net),
+};
+
+/* Initialize Caif devices list */
+int __init caif_device_init(void)
+{
+ int result;
+ cfg = cfcnfg_create();
+ if (!cfg) {
+ pr_warning("CAIF: %s(): can't create cfcnfg.\n", __func__);
+ goto err_cfcnfg_create_failed;
+ }
+ result = register_pernet_device(&caif_net_ops);
+
+ if (result) {
+ kfree(cfg);
+ cfg = NULL;
+ return result;
+ }
+ dev_add_pack(&caif_packet_type);
+ register_netdevice_notifier(&caif_device_notifier);
+
+ return result;
+err_cfcnfg_create_failed:
+ return -ENODEV;
+}
+
+void __exit caif_device_exit(void)
+{
+ dev_remove_pack(&caif_packet_type);
+ unregister_pernet_device(&caif_net_ops);
+ unregister_netdevice_notifier(&caif_device_notifier);
+ cfcnfg_remove(cfg);
+}
+
+module_init(caif_device_init);
+module_exit(caif_device_exit);
--
1.6.3.3
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PATCH net-next-2.6 08/13] net-caif: add CAIF socket implementation
2010-01-20 22:55 [PATCH net-next-2.6 00/13] net-caif: introducing CAIF protocol stack sjur.brandeland
` (6 preceding siblings ...)
2010-01-20 22:55 ` [PATCH net-next-2.6 07/13] net-caif: add CAIF device registration functionality sjur.brandeland
@ 2010-01-20 22:55 ` sjur.brandeland
2010-01-20 22:55 ` [PATCH net-next-2.6 09/13] net-caif: add CAIF netdevice sjur.brandeland
` (5 subsequent siblings)
13 siblings, 0 replies; 47+ messages in thread
From: sjur.brandeland @ 2010-01-20 22:55 UTC (permalink / raw)
To: netdev; +Cc: davem, marcel, stefano.babic, randy.dunlap, Sjur Braendeland
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
Implementation of CAIF sockets for protocol and address family
PF_CAIF and AF_CAIF.
CAIF socket is connection oriented implementing SOCK_SEQPACKET
interface with supporting blocking and non-blocking mode.
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
net/caif/caif_socket.c | 1427 ++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1427 insertions(+), 0 deletions(-)
diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c
new file mode 100644
index 0000000..1e98bf6
--- /dev/null
+++ b/net/caif/caif_socket.c
@@ -0,0 +1,1427 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland sjur.brandeland@stericsson.com
+ * Per Sigmond per.sigmond@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/poll.h>
+#include <linux/tcp.h>
+#include <linux/uaccess.h>
+#include <asm/atomic.h>
+
+#include <linux/caif/caif_socket.h>
+#include <linux/caif/caif_config.h>
+#include <net/caif/generic/caif_layer.h>
+#include <net/caif/caif_dev.h>
+#include <net/caif/generic/cfpkt.h>
+
+MODULE_LICENSE("GPL");
+
+#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 num_remote_shutdown_ind;
+ atomic_t num_tx_flow_off_ind;
+ atomic_t num_tx_flow_on_ind;
+ atomic_t num_rx_flow_off;
+ atomic_t num_rx_flow_on;
+};
+struct debug_fs_counter cnt;
+#define dbfs_atomic_inc(v) atomic_inc(v)
+#else
+#define dbfs_atomic_inc(v)
+#endif
+
+/* The AF_CAIF socket */
+struct caifsock {
+ /* NOTE: sk has to be the first member */
+ struct sock sk;
+ struct layer layer;
+ char name[CAIF_LAYER_NAME_SZ];
+ u32 conn_state;
+ u32 flow_state;
+ struct cfpktq *pktq;
+ int file_mode;
+ struct caif_channel_config config;
+ int read_queue_len;
+ spinlock_t read_queue_len_lock;
+ struct dentry *debugfs_socket_dir;
+};
+
+static void drain_queue(struct caifsock *cf_sk);
+
+/* Packet Receive Callback function called from CAIF Stack */
+static int caif_sktrecv_cb(struct layer *layr, struct cfpkt *pkt)
+{
+ struct caifsock *cf_sk;
+ int read_queue_high;
+ cf_sk = container_of(layr, struct caifsock, layer);
+
+ if (!STATE_IS_OPEN(cf_sk)) {
+ /*FIXME: This should be allowed finally!*/
+ pr_debug("CAIF: %s(): called after close request\n", __func__);
+ cfpkt_destroy(pkt);
+ return 0;
+ }
+ /* NOTE: This function may be called in Tasklet context! */
+
+ /* The queue has its own lock */
+ cfpkt_queue(cf_sk->pktq, pkt, 0);
+
+ spin_lock(&cf_sk->read_queue_len_lock);
+ cf_sk->read_queue_len++;
+
+ read_queue_high = (cf_sk->read_queue_len > CHNL_SKT_READ_QUEUE_HIGH);
+ spin_unlock(&cf_sk->read_queue_len_lock);
+
+ if (RX_FLOW_IS_ON(cf_sk) && read_queue_high) {
+ dbfs_atomic_inc(&cnt.num_rx_flow_off);
+ SET_RX_FLOW_OFF(cf_sk);
+
+ /* Send flow off (NOTE: must not sleep) */
+ pr_debug("CAIF: %s():"
+ " sending flow OFF (queue len = %d)\n",
+ __func__,
+ cf_sk->read_queue_len);
+ caif_assert(cf_sk->layer.dn);
+ caif_assert(cf_sk->layer.dn->ctrlcmd);
+
+ (void) cf_sk->layer.dn->modemcmd(cf_sk->layer.dn,
+ CAIF_MODEMCMD_FLOW_OFF_REQ);
+ }
+
+ /* Signal reader that data is available. */
+
+ wake_up_interruptible(cf_sk->sk.sk_sleep);
+
+ return 0;
+}
+
+/* Packet Flow Control Callback function called from CAIF */
+static void caif_sktflowctrl_cb(struct layer *layr,
+ enum caif_ctrlcmd flow,
+ int phyid)
+{
+ struct caifsock *cf_sk;
+
+ /* NOTE: This function may be called in Tasklet context! */
+ 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);
+ switch (flow) {
+ case CAIF_CTRLCMD_FLOW_ON_IND:
+ dbfs_atomic_inc(&cnt.num_tx_flow_on_ind);
+ /* Signal reader that data is available. */
+ SET_TX_FLOW_ON(cf_sk);
+ wake_up_interruptible(cf_sk->sk.sk_sleep);
+ break;
+
+ case CAIF_CTRLCMD_FLOW_OFF_IND:
+ dbfs_atomic_inc(&cnt.num_tx_flow_off_ind);
+ SET_TX_FLOW_OFF(cf_sk);
+ break;
+
+ case CAIF_CTRLCMD_INIT_RSP:
+ dbfs_atomic_inc(&cnt.num_init_resp);
+ /* Signal reader that data is available. */
+ caif_assert(STATE_IS_OPEN(cf_sk));
+ SET_PENDING_OFF(cf_sk);
+ SET_TX_FLOW_ON(cf_sk);
+ wake_up_interruptible(cf_sk->sk.sk_sleep);
+ break;
+
+ case CAIF_CTRLCMD_DEINIT_RSP:
+ dbfs_atomic_inc(&cnt.num_deinit_resp);
+ caif_assert(!STATE_IS_OPEN(cf_sk));
+ SET_PENDING_OFF(cf_sk);
+ if (!STATE_IS_PENDING_DESTROY(cf_sk)) {
+ if (cf_sk->sk.sk_sleep != NULL)
+ wake_up_interruptible(cf_sk->sk.sk_sleep);
+ }
+ sock_put(&cf_sk->sk);
+ break;
+
+ case CAIF_CTRLCMD_INIT_FAIL_RSP:
+ dbfs_atomic_inc(&cnt.num_init_fail_resp);
+ caif_assert(STATE_IS_OPEN(cf_sk));
+ SET_STATE_CLOSED(cf_sk);
+ SET_PENDING_OFF(cf_sk);
+ SET_TX_FLOW_OFF(cf_sk);
+ wake_up_interruptible(cf_sk->sk.sk_sleep);
+ break;
+
+ case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND:
+ dbfs_atomic_inc(&cnt.num_remote_shutdown_ind);
+ 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);
+ break;
+
+ default:
+ pr_debug("CAIF: %s(): Unexpected flow command %d\n",
+ __func__, flow);
+ }
+}
+
+
+
+static struct sk_buff *caif_alloc_send_skb(struct sock *sk,
+ unsigned long data_len,
+ int *err)
+{
+ 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);
+
+ sk->sk_allocation = sk_allocation;
+
+ return skb;
+}
+
+static int caif_recvmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *m, size_t buf_len, int flags)
+
+{
+ struct sock *sk = sock->sk;
+ struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
+ struct cfpkt *pkt = NULL;
+ size_t len;
+ int result;
+ struct sk_buff *skb;
+ ssize_t ret = -EIO;
+ int read_queue_low;
+
+ if (cf_sk == NULL) {
+ pr_debug("CAIF: %s(): private_data not set!\n",
+ __func__);
+ ret = -EBADFD;
+ goto read_error;
+ }
+
+ /* Don't do multiple iovec entries yet */
+ if (m->msg_iovlen != 1)
+ return -EOPNOTSUPP;
+
+ if (unlikely(!buf_len))
+ return -EINVAL;
+
+ lock_sock(&(cf_sk->sk));
+
+ caif_assert(cf_sk->pktq);
+
+ if (!STATE_IS_OPEN(cf_sk)) {
+ /* Socket is closed or closing. */
+ if (!STATE_IS_PENDING(cf_sk)) {
+ pr_debug("CAIF: %s(): socket is closed (by remote)\n",
+ __func__);
+ ret = -EPIPE;
+ } else {
+ pr_debug("CAIF: %s(): socket is closing..\n", __func__);
+ ret = -EBADF;
+ }
+ goto read_error;
+ }
+
+ /* Socket is open or opening. */
+ if (STATE_IS_PENDING(cf_sk)) {
+ pr_debug("CAIF: %s(): socket is opening...\n", __func__);
+
+ if (flags & MSG_DONTWAIT) {
+ /* We can't block. */
+ pr_debug("CAIF: %s():state pending and MSG_DONTWAIT\n",
+ __func__);
+ ret = -EAGAIN;
+ goto read_error;
+ }
+
+ /*
+ * Blocking mode; state is pending and we need to wait
+ * for its conclusion.
+ */
+ release_sock(&cf_sk->sk);
+
+ result =
+ wait_event_interruptible(*cf_sk->sk.sk_sleep,
+ !STATE_IS_PENDING(cf_sk));
+
+ lock_sock(&(cf_sk->sk));
+
+ if (result == -ERESTARTSYS) {
+ pr_debug("CAIF: %s(): wait_event_interruptible"
+ " woken by a signal (1)", __func__);
+ ret = -ERESTARTSYS;
+ goto read_error;
+ }
+ }
+
+ if (STATE_IS_REMOTE_SHUTDOWN(cf_sk)) {
+ pr_debug("CAIF: %s(): received remote_shutdown indication\n",
+ __func__);
+ ret = -ESHUTDOWN;
+ goto read_error;
+ }
+
+ /*
+ * Block if we don't have any received buffers.
+ * The queue has its own lock.
+ */
+ while ((pkt = cfpkt_qpeek(cf_sk->pktq)) == NULL) {
+
+ if (flags & MSG_DONTWAIT) {
+ pr_debug("CAIF: %s(): MSG_DONTWAIT\n", __func__);
+ ret = -EAGAIN;
+ goto read_error;
+ }
+ 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;
+ goto read_error;
+ }
+
+ /*
+ * Get packet from queue.
+ * The queue has its own lock.
+ */
+ pkt = cfpkt_dequeue(cf_sk->pktq);
+
+ spin_lock(&cf_sk->read_queue_len_lock);
+ cf_sk->read_queue_len--;
+ read_queue_low = (cf_sk->read_queue_len < CHNL_SKT_READ_QUEUE_LOW);
+ spin_unlock(&cf_sk->read_queue_len_lock);
+
+ if (!RX_FLOW_IS_ON(cf_sk) && read_queue_low) {
+ dbfs_atomic_inc(&cnt.num_rx_flow_on);
+ SET_RX_FLOW_ON(cf_sk);
+
+ /* Send flow on. */
+ pr_debug("CAIF: %s(): sending flow ON (queue len = %d)\n",
+ __func__, cf_sk->read_queue_len);
+ caif_assert(cf_sk->layer.dn);
+ caif_assert(cf_sk->layer.dn->ctrlcmd);
+ (void) cf_sk->layer.dn->modemcmd(cf_sk->layer.dn,
+ CAIF_MODEMCMD_FLOW_ON_REQ);
+
+ caif_assert(cf_sk->read_queue_len >= 0);
+ }
+ skb = cfpkt_tonative(pkt);
+ result = skb_copy_datagram_iovec(skb, 0, m->msg_iov, len);
+ if (result) {
+ pr_debug("CAIF: %s(): cfpkt_raw_extract failed\n", __func__);
+ cfpkt_destroy(pkt);
+ ret = -EFAULT;
+ goto read_error;
+ }
+ if (unlikely(buf_len < len)) {
+ len = buf_len;
+ m->msg_flags |= MSG_TRUNC;
+ }
+
+ /* Free packet. */
+ skb_free_datagram(sk, skb);
+
+ /* Let the others in. */
+ release_sock(&cf_sk->sk);
+ return len;
+
+read_error:
+ release_sock(&cf_sk->sk);
+read_error_no_unlock:
+ return ret;
+}
+
+/* Send a signal as a consequence of sendmsg, sendto or caif_sendmsg. */
+static int caif_sendmsg(struct kiocb *kiocb, struct socket *sock,
+ struct msghdr *msg, size_t len)
+{
+
+ struct sock *sk = sock->sk;
+ struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
+ void *payload;
+ size_t payload_size = msg->msg_iov->iov_len;
+ struct cfpkt *pkt = NULL;
+ struct payload_info info;
+ unsigned char *txbuf;
+ int err = 0;
+ ssize_t ret = -EIO;
+ int result;
+ struct sk_buff *skb;
+ caif_assert(msg->msg_iovlen == 1);
+
+ if (cf_sk == NULL) {
+ pr_debug("CAIF: %s(): private_data not set!\n",
+ __func__);
+ ret = -EBADFD;
+ goto write_error_no_unlock;
+ }
+
+ if (unlikely(msg->msg_iov->iov_base == NULL)) {
+ pr_warning("CAIF: %s(): Buffer is NULL.\n", __func__);
+ ret = -EINVAL;
+ goto write_error_no_unlock;
+ }
+
+ payload = msg->msg_iov->iov_base;
+ if (payload_size > CAIF_MAX_PAYLOAD_SIZE) {
+ pr_debug("CAIF: %s(): buffer too long\n", __func__);
+ ret = -EINVAL;
+ goto write_error_no_unlock;
+ }
+ /* I want to be alone on cf_sk (except status and queue) */
+ lock_sock(&(cf_sk->sk));
+
+ caif_assert(cf_sk->pktq);
+
+ if (!STATE_IS_OPEN(cf_sk)) {
+ /* Socket is closed or closing */
+ if (!STATE_IS_PENDING(cf_sk)) {
+ pr_debug("CAIF: %s(): socket is closed (by remote)\n",
+ __func__);
+ ret = -EPIPE;
+ } else {
+ pr_debug("CAIF: %s(): socket is closing...\n",
+ __func__);
+ ret = -EBADF;
+ }
+ goto write_error;
+ }
+
+ /* Socket is open or opening */
+ if (STATE_IS_PENDING(cf_sk)) {
+ pr_debug("CAIF: %s(): socket is opening...\n", __func__);
+
+ if (msg->msg_flags & MSG_DONTWAIT) {
+ /* We can't block */
+ trace_printk("CAIF: %s():state pending:"
+ "state=MSG_DONTWAIT\n", __func__);
+ ret = -EAGAIN;
+ goto write_error;
+ }
+
+ /* Let readers in */
+ release_sock(&cf_sk->sk);
+
+ /*
+ * Blocking mode; state is pending and we need to wait
+ * for its conclusion.
+ */
+ result =
+ wait_event_interruptible(*cf_sk->sk.sk_sleep,
+ !STATE_IS_PENDING(cf_sk));
+
+ /* I want to be alone on cf_sk (except status and queue) */
+ lock_sock(&(cf_sk->sk));
+
+ if (result == -ERESTARTSYS) {
+ pr_debug("CAIF: %s(): wait_event_interruptible"
+ " woken by a signal (1)", __func__);
+ ret = -ERESTARTSYS;
+ goto write_error;
+ }
+ }
+
+ if (STATE_IS_REMOTE_SHUTDOWN(cf_sk)) {
+ pr_debug("CAIF: %s(): received remote_shutdown indication\n",
+ __func__);
+ ret = -ESHUTDOWN;
+ goto write_error;
+ }
+
+ if (!TX_FLOW_IS_ON(cf_sk)) {
+
+ /* Flow is off. Check non-block flag */
+ if (msg->msg_flags & MSG_DONTWAIT) {
+ trace_printk("CAIF: %s(): MSG_DONTWAIT and tx flow off",
+ __func__);
+ ret = -EAGAIN;
+ goto write_error;
+ }
+
+ /* release lock before waiting */
+ release_sock(&cf_sk->sk);
+
+ /* Wait until flow is on or socket is closed */
+ if (wait_event_interruptible(*cf_sk->sk.sk_sleep,
+ TX_FLOW_IS_ON(cf_sk)
+ || !STATE_IS_OPEN(cf_sk)
+ || STATE_IS_REMOTE_SHUTDOWN(cf_sk)
+ ) == -ERESTARTSYS) {
+ pr_debug("CAIF: %s():"
+ " wait_event_interruptible woken by a signal",
+ __func__);
+ ret = -ERESTARTSYS;
+ goto write_error_no_unlock;
+ }
+
+ /* I want to be alone on cf_sk (except status and queue) */
+ lock_sock(&(cf_sk->sk));
+
+ if (!STATE_IS_OPEN(cf_sk)) {
+ /* someone closed the link, report error */
+ pr_debug("CAIF: %s(): remote end shutdown!\n",
+ __func__);
+ ret = -EPIPE;
+ goto write_error;
+ }
+
+ if (STATE_IS_REMOTE_SHUTDOWN(cf_sk)) {
+ pr_debug("CAIF: %s(): "
+ "received remote_shutdown indication\n",
+ __func__);
+ ret = -ESHUTDOWN;
+ goto write_error;
+ }
+ }
+
+ /* 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;
+ }
+
+ skb_reserve(skb, CAIF_NEEDED_HEADROOM);
+ pkt = cfpkt_fromnative(CAIF_DIR_OUT, skb);
+ caif_assert((void *)pkt == (void *)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;
+ }
+
+ /* Copy data into buffer. */
+ if (copy_from_user(txbuf, payload, payload_size)) {
+ pr_debug("CAIF: %s(): copy_from_user returned non zero.\n",
+ __func__);
+ cfpkt_destroy(pkt);
+ ret = -EINVAL;
+ goto write_error;
+ }
+ memset(&info, 0, sizeof(info));
+
+ /* Send the packet down the stack. */
+ caif_assert(cf_sk->layer.dn);
+ caif_assert(cf_sk->layer.dn->transmit);
+
+ do {
+ ret = cf_sk->layer.dn->transmit(cf_sk->layer.dn, pkt);
+
+ if (likely((ret >= 0) || (ret != -EAGAIN)))
+ break;
+
+ /* EAGAIN - retry */
+ if (msg->msg_flags & MSG_DONTWAIT) {
+ pr_debug("CAIF: %s(): NONBLOCK and transmit failed,"
+ " error = %d\n", __func__, ret);
+ ret = -EAGAIN;
+ goto write_error;
+ }
+
+ /* Let readers in */
+ release_sock(&cf_sk->sk);
+
+ /* Wait until flow is on or socket is closed */
+ if (wait_event_interruptible(*cf_sk->sk.sk_sleep,
+ TX_FLOW_IS_ON(cf_sk)
+ || !STATE_IS_OPEN(cf_sk)
+ || STATE_IS_REMOTE_SHUTDOWN(cf_sk)
+ ) == -ERESTARTSYS) {
+ pr_debug("CAIF: %s(): wait_event_interruptible"
+ " woken by a signal", __func__);
+ ret = -ERESTARTSYS;
+ goto write_error_no_unlock;
+ }
+
+ /* I want to be alone on cf_sk (except status and queue) */
+ lock_sock(&(cf_sk->sk));
+
+ } while (ret == -EAGAIN);
+
+ if (ret < 0) {
+ cfpkt_destroy(pkt);
+ pr_debug("CAIF: %s(): transmit failed, error = %d\n",
+ __func__, ret);
+
+ goto write_error;
+ }
+
+ release_sock(&cf_sk->sk);
+ return payload_size;
+
+write_error:
+ release_sock(&cf_sk->sk);
+write_error_no_unlock:
+ return ret;
+}
+
+static unsigned int caif_poll(struct file *file, struct socket *sock,
+ poll_table *wait)
+{
+ struct sock *sk = sock->sk;
+ struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
+ u32 mask = 0;
+
+ poll_wait(file, sk->sk_sleep, wait);
+ lock_sock(&(cf_sk->sk));
+ if (cfpkt_qpeek(cf_sk->pktq) != NULL)
+ mask |= (POLLIN | POLLRDNORM);
+ if (!STATE_IS_OPEN(cf_sk))
+ mask |= POLLHUP;
+ else if (TX_FLOW_IS_ON(cf_sk))
+ mask |= (POLLOUT | POLLWRNORM);
+ release_sock(&cf_sk->sk);
+ trace_printk("CAIF: %s(): poll mask=0x%04x...\n",
+ __func__, mask);
+ return mask;
+}
+
+static void drain_queue(struct caifsock *cf_sk)
+{
+ struct cfpkt *pkt = NULL;
+
+ /* Empty the queue */
+ do {
+ /* The queue has its own lock */
+ pkt = cfpkt_dequeue(cf_sk->pktq);
+ if (!pkt)
+ break;
+ pr_debug("CAIF: %s(): freeing packet from read queue\n",
+ __func__);
+ cfpkt_destroy(pkt);
+
+ } while (1);
+
+ spin_lock(&cf_sk->read_queue_len_lock);
+ cf_sk->read_queue_len = 0;
+ spin_unlock(&cf_sk->read_queue_len_lock);
+}
+
+
+static int setsockopt(struct socket *sock,
+ int lvl, int opt, char __user *ov, unsigned int ol)
+{
+ struct sock *sk = sock->sk;
+ struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
+ struct caif_channel_opt confopt;
+ int res;
+
+ if (lvl != SOL_CAIF) {
+ pr_debug("CAIF: %s(): setsockopt bad level\n", __func__);
+ 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__);
+ 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);
+ 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;
+
+
+ if (len != sizeof(struct sockaddr_caif)) {
+ pr_debug("CAIF: %s(): Bad address len (%d,%d)\n",
+ __func__, len, sizeof(struct sockaddr_caif));
+ 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;
+ }
+
+ 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;
+ }
+ 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;
+}
+
+
+int caif_connect(struct socket *sock, struct sockaddr *uservaddr,
+ int sockaddr_len, int flags)
+{
+ struct caifsock *cf_sk = NULL;
+ int result = -1;
+ int mode = 0;
+ int ret = -EIO;
+ struct sock *sk = sock->sk;
+
+ BUG_ON(sk == NULL);
+
+ cf_sk = container_of(sk, struct caifsock, sk);
+
+ trace_printk("CAIF: %s(): cf_sk=%p OPEN=%d, TX_FLOW=%d, RX_FLOW=%d\n",
+ __func__, cf_sk,
+ STATE_IS_OPEN(cf_sk),
+ TX_FLOW_IS_ON(cf_sk), RX_FLOW_IS_ON(cf_sk));
+
+ sk->sk_state = TCP_CLOSE;
+ sock->state = SS_UNCONNECTED;
+
+ if (sock->type == SOCK_SEQPACKET) {
+ sock->state = SS_CONNECTED;
+ sk->sk_state = TCP_ESTABLISHED;
+ } else
+ goto out;
+
+ /* I want to be alone on cf_sk (except status and queue) */
+ lock_sock(&(cf_sk->sk));
+
+ 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);
+ }
+
+ /* If opened non-blocking, report "success".
+ */
+ if (flags & MSG_DONTWAIT) {
+ pr_debug("CAIF: %s(): MSG_DONTWAIT success\n",
+ __func__);
+ ret = 0;
+ goto open_success;
+ }
+
+ trace_printk("CAIF: %s(): Wait for connect response\n",
+ __func__);
+
+ /* release lock before waiting */
+ release_sock(&cf_sk->sk);
+
+ result =
+ wait_event_interruptible(*cf_sk->sk.sk_sleep,
+ !STATE_IS_PENDING(cf_sk));
+
+ lock_sock(&(cf_sk->sk));
+
+ if (result == -ERESTARTSYS) {
+ pr_debug("CAIF: %s(): wait_event_interruptible"
+ "woken by a signal (2)", __func__);
+ ret = -ERESTARTSYS;
+ goto open_error;
+ }
+
+ if (!STATE_IS_OPEN(cf_sk)) {
+ /* Lower layers said "no" */
+ pr_debug("CAIF: %s(): Closed received\n", __func__);
+ ret = -EPIPE;
+ goto open_error;
+ }
+
+ trace_printk("CAIF: %s(): Connect received\n", __func__);
+ }
+open_success:
+ /* Open is ok */
+ cf_sk->file_mode |= mode;
+
+ trace_printk("CAIF: %s(): Connected - file mode = %x\n",
+ __func__, cf_sk->file_mode);
+
+ release_sock(&cf_sk->sk);
+ return 0;
+open_error:
+ release_sock(&cf_sk->sk);
+out:
+ return ret;
+}
+
+static int caif_shutdown(struct socket *sock, int how)
+{
+ struct caifsock *cf_sk = NULL;
+ int result;
+ int tx_flow_state_was_on;
+ struct sock *sk = sock->sk;
+ int res = 0;
+
+ if (how != SHUT_RDWR)
+ return -EOPNOTSUPP; /* FIXME: ENOTSUP in userland for POSIX */
+
+ cf_sk = container_of(sk, struct caifsock, sk);
+ if (cf_sk == NULL) {
+ pr_debug("CAIF: %s(): COULD NOT FIND SOCKET\n", __func__);
+ return -EBADF;
+ }
+
+ /* I want to be alone on cf_sk (except status queue) */
+ lock_sock(&(cf_sk->sk));
+ dbfs_atomic_inc(&cnt.num_close);
+
+ /* 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;
+ }
+
+ /* 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.
+ */
+
+ 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);
+ }
+
+ dbfs_atomic_inc(&cnt.num_deinit);
+
+ /*
+ * 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;
+
+ release_sock(&cf_sk->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)
+{
+ struct sock *sk = sock->sk;
+ struct caifsock *cf_sk = NULL;
+
+ caif_assert(sk != NULL);
+ cf_sk = container_of(sk, struct caifsock, sk);
+
+ caif_shutdown(sock, SHUT_RDWR);
+ lock_sock(&(cf_sk->sk));
+
+ sock->sk = NULL;
+
+ /* Detach the socket from its process context by making it orphan. */
+ sock_orphan(sk);
+
+ /*
+ * Setting SHUTDOWN_MASK means that both send and receive are shutdown
+ * for the socket.
+ */
+ sk->sk_shutdown = SHUTDOWN_MASK;
+
+ /*
+ * Set the socket state to closed, the TCP_CLOSE macro is used when
+ * closing any socket.
+ */
+ sk->sk_state = TCP_CLOSE;
+
+ /* Flush out this sockets receive queue. */
+ drain_queue(cf_sk);
+
+ /* Finally release the socket. */
+ STATE_IS_PENDING_DESTROY(cf_sk);
+ release_sock(&cf_sk->sk);
+
+ /*
+ * The rest of the cleanup will be handled from the
+ * caif_sock_destructor
+ */
+ sock_put(sk);
+ return 0;
+}
+
+static int caif_sock_ioctl(struct socket *sock,
+ unsigned int cmd,
+ unsigned long arg)
+
+{
+ return caif_ioctl(cmd, arg, true);
+}
+
+static struct proto_ops caif_ops = {
+ .family = PF_CAIF,
+ .owner = THIS_MODULE,
+ .release = caif_release,
+ .bind = sock_no_bind,
+ .connect = caif_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = sock_no_getname,
+ .poll = caif_poll,
+ .ioctl = caif_sock_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = caif_shutdown,
+ .setsockopt = setsockopt,
+ .getsockopt = getsockopt,
+ .sendmsg = caif_sendmsg,
+ .recvmsg = caif_recvmsg,
+ .mmap = sock_no_mmap,
+ .sendpage = caif_sock_no_sendpage,
+};
+
+/* This function is called when a socket is finally destroyed. */
+static void caif_sock_destructor(struct sock *sk)
+{
+ struct caifsock *cf_sk = NULL;
+ cf_sk = container_of(sk, struct caifsock, sk);
+
+ /* Error checks. */
+ caif_assert(!atomic_read(&sk->sk_wmem_alloc));
+ caif_assert(sk_unhashed(sk));
+ caif_assert(!sk->sk_socket);
+
+ if (!sock_flag(sk, SOCK_DEAD)) {
+ pr_debug("CAIF: %s(): 0x%p", __func__, sk);
+ return;
+ }
+
+ lock_sock(&(cf_sk->sk));
+
+ if (STATE_IS_OPEN(cf_sk)) {
+ pr_debug("CAIF: %s(): socket is opened (cf_sk=%p)"
+ " file_mode = 0x%x\n", __func__,
+ cf_sk, cf_sk->file_mode);
+ release_sock(&cf_sk->sk);
+ return;
+ }
+ drain_queue(cf_sk);
+ 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);
+}
+
+static int caif_create(struct net *net, struct socket *sock, int protocol,
+ int kern)
+{
+ struct sock *sk = NULL;
+ struct caifsock *cf_sk = NULL;
+ int result = 0;
+
+ static struct proto prot = {.name = "PF_CAIF",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct caifsock),
+ };
+ prot.slab = caif_sk_cachep;
+
+ if (net != &init_net)
+ return -EAFNOSUPPORT;
+
+ if (protocol < 0 || protocol >= CAIFPROTO_MAX)
+ return -EPROTONOSUPPORT;
+
+ /*
+ * Set the socket state to unconnected. The socket state is really
+ * not used at all in the net/core or socket.c but the
+ * initialization makes sure that sock->state is not uninitialized.
+ */
+ sock->state = SS_UNCONNECTED;
+
+ sk = sk_alloc(net, PF_CAIF, GFP_KERNEL, &prot);
+ if (!sk)
+ return -ENOMEM;
+
+ cf_sk = container_of(sk, struct caifsock, sk);
+
+ /* Store the protocol */
+ sk->sk_protocol = (unsigned char) protocol;
+
+ spin_lock_init(&cf_sk->read_queue_len_lock);
+
+ /* Fill in some information concerning the misc socket. */
+ snprintf(cf_sk->name, sizeof(cf_sk->name), "cf_sk%d",
+ atomic_read(&caif_nr_socks));
+ 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;
+ }
+
+ /*
+ * Lock in order to try to stop someone from opening the socket
+ * too early.
+ */
+ lock_sock(&(cf_sk->sk));
+
+ /* Initialize the nozero default sock structure data. */
+ sock_init_data(sock, sk);
+
+ sk->sk_destruct = caif_sock_destructor;
+ sk->sk_sndbuf = caif_sockbuf_size;
+ sk->sk_rcvbuf = caif_sockbuf_size;
+
+ cf_sk->pktq = cfpktq_create();
+
+ if (!cf_sk->pktq) {
+ pr_err("CAIF: %s(): queue create failed.\n", __func__);
+ result = -ENOMEM;
+ release_sock(&cf_sk->sk);
+ goto err_failed;
+ }
+
+ cf_sk->layer.ctrlcmd = caif_sktflowctrl_cb;
+ SET_STATE_CLOSED(cf_sk);
+ SET_PENDING_OFF(cf_sk);
+ SET_TX_FLOW_OFF(cf_sk);
+ SET_RX_FLOW_ON(cf_sk);
+
+ /* Increase the number of sockets created. */
+ atomic_inc(&caif_nr_socks);
+ if (!IS_ERR(debugfsdir)) {
+ cf_sk->debugfs_socket_dir =
+ debugfs_create_dir(cf_sk->name, debugfsdir);
+ debugfs_create_u32("conn_state", S_IRUSR | S_IWUSR,
+ cf_sk->debugfs_socket_dir, &cf_sk->conn_state);
+ debugfs_create_u32("flow_state", S_IRUSR | S_IWUSR,
+ cf_sk->debugfs_socket_dir, &cf_sk->flow_state);
+ debugfs_create_u32("read_queue_len", S_IRUSR | S_IWUSR,
+ cf_sk->debugfs_socket_dir,
+ (u32 *) &cf_sk->read_queue_len);
+ debugfs_create_u32("identity", S_IRUSR | S_IWUSR,
+ cf_sk->debugfs_socket_dir,
+ (u32 *) &cf_sk->layer.id);
+ }
+ release_sock(&cf_sk->sk);
+ return 0;
+err_failed:
+ sk_free(sk);
+ return result;
+}
+
+
+static struct net_proto_family caif_family_ops = {
+ .family = PF_CAIF,
+ .create = caif_create,
+ .owner = THIS_MODULE,
+};
+
+int af_caif_init(void)
+{
+ int err;
+ err = sock_register(&caif_family_ops);
+
+ if (!err)
+ return err;
+
+ return 0;
+}
+
+static int __init caif_sktinit_module(void)
+{
+ int stat;
+#ifdef CONFIG_DEBUG_FS
+ debugfsdir = debugfs_create_dir("chnl_skt", NULL);
+ if (!IS_ERR(debugfsdir)) {
+ 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,
+ debugfsdir,
+ (u32 *) &cnt.num_open);
+ debugfs_create_u32("num_close", S_IRUSR | S_IWUSR,
+ debugfsdir,
+ (u32 *) &cnt.num_close);
+ debugfs_create_u32("num_init", S_IRUSR | S_IWUSR,
+ debugfsdir,
+ (u32 *) &cnt.num_init);
+ debugfs_create_u32("num_init_resp", S_IRUSR | S_IWUSR,
+ debugfsdir,
+ (u32 *) &cnt.num_init_resp);
+ debugfs_create_u32("num_init_fail_resp", S_IRUSR | S_IWUSR,
+ debugfsdir,
+ (u32 *) &cnt.num_init_fail_resp);
+ debugfs_create_u32("num_deinit", S_IRUSR | S_IWUSR,
+ debugfsdir,
+ (u32 *) &cnt.num_deinit);
+ debugfs_create_u32("num_deinit_resp", S_IRUSR | S_IWUSR,
+ debugfsdir,
+ (u32 *) &cnt.num_deinit_resp);
+ debugfs_create_u32("num_remote_shutdown_ind",
+ S_IRUSR | S_IWUSR, debugfsdir,
+ (u32 *) &cnt.num_remote_shutdown_ind);
+ debugfs_create_u32("num_tx_flow_off_ind", S_IRUSR | S_IWUSR,
+ debugfsdir,
+ (u32 *) &cnt.num_tx_flow_off_ind);
+ debugfs_create_u32("num_tx_flow_on_ind", S_IRUSR | S_IWUSR,
+ debugfsdir,
+ (u32 *) &cnt.num_tx_flow_on_ind);
+ debugfs_create_u32("num_rx_flow_off", S_IRUSR | S_IWUSR,
+ debugfsdir,
+ (u32 *) &cnt.num_rx_flow_off);
+ debugfs_create_u32("num_rx_flow_on", S_IRUSR | S_IWUSR,
+ debugfsdir,
+ (u32 *) &cnt.num_rx_flow_on);
+ }
+#endif
+ stat = af_caif_init();
+ if (stat) {
+ pr_err("CAIF: %s(): Failed to initialize CAIF socket layer.",
+ __func__);
+ return stat;
+ }
+ return 0;
+}
+
+static void __exit caif_sktexit_module(void)
+{
+ sock_unregister(PF_CAIF);
+ if (debugfsdir != NULL)
+ debugfs_remove_recursive(debugfsdir);
+}
+
+module_init(caif_sktinit_module);
+module_exit(caif_sktexit_module);
+
--
1.6.3.3
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PATCH net-next-2.6 09/13] net-caif: add CAIF netdevice
2010-01-20 22:55 [PATCH net-next-2.6 00/13] net-caif: introducing CAIF protocol stack sjur.brandeland
` (7 preceding siblings ...)
2010-01-20 22:55 ` [PATCH net-next-2.6 08/13] net-caif: add CAIF socket implementation sjur.brandeland
@ 2010-01-20 22:55 ` sjur.brandeland
2010-01-21 8:03 ` Patrick McHardy
2010-01-20 22:55 ` [PATCH net-next-2.6 10/13] net-caif: add kernel-client API for CAIF sjur.brandeland
` (4 subsequent siblings)
13 siblings, 1 reply; 47+ messages in thread
From: sjur.brandeland @ 2010-01-20 22:55 UTC (permalink / raw)
To: netdev; +Cc: davem, marcel, stefano.babic, randy.dunlap, Sjur Braendeland
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
Adding GPRS Net Device for PDP Contexts.
The device can be managed by IOCTLs defined in if_caif.h and
RTNL.
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
net/caif/chnl_net.c | 566 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 566 insertions(+), 0 deletions(-)
diff --git a/net/caif/chnl_net.c b/net/caif/chnl_net.c
new file mode 100644
index 0000000..a9e18a7
--- /dev/null
+++ b/net/caif/chnl_net.c
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Daniel Martensson / Daniel.Martensson@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/version.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/if_ether.h>
+#include <linux/moduleparam.h>
+#include <linux/ip.h>
+#include <linux/sched.h>
+#include <linux/sockios.h>
+#include <linux/caif/if_caif.h>
+#include <net/rtnetlink.h>
+#include <net/caif/generic/caif_layer.h>
+#include <net/caif/generic/cfcnfg.h>
+#include <net/caif/generic/cfpkt.h>
+#include <net/caif/caif_dev.h>
+
+#define CAIF_CONNECT_TIMEOUT 30
+#define SIZE_MTU 1500
+#define SIZE_MTU_MAX 4080
+#define SIZE_MTU_MIN 68
+#define CAIF_NET_DEFAULT_QUEUE_LEN 500
+
+/*This list isi protected by the rtnl lock. */
+static LIST_HEAD(chnl_net_list);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_RTNL_LINK("caif");
+
+struct chnl_net {
+ struct layer chnl;
+ struct net_device_stats stats;
+ struct caif_channel_config config;
+ struct list_head list_field;
+ struct net_device *netdev;
+ char name[256];
+ wait_queue_head_t netmgmt_wq;
+ /* Flow status to remember and control the transmission. */
+ bool flowenabled;
+};
+
+
+static struct chnl_net *find_device(char *name)
+{
+ struct list_head *list_node;
+ struct list_head *n;
+ struct chnl_net *dev = NULL;
+ struct chnl_net *tmp;
+ ASSERT_RTNL();
+ list_for_each_safe(list_node, n, &chnl_net_list) {
+ tmp = list_entry(list_node, struct chnl_net, list_field);
+ /* Find from name. */
+ if (name) {
+ if (!strncmp(tmp->name, name, sizeof(tmp->name)))
+ dev = tmp;
+ else if (!strncmp(tmp->netdev->name,
+ name,
+ sizeof(tmp->netdev->name)))
+ dev = tmp;
+ } else
+ /* Get the first element if name is not specified. */
+ dev = tmp;
+ if (dev)
+ break;
+ }
+ return dev;
+}
+
+static void robust_list_del(struct list_head *delete_node)
+{
+ struct list_head *list_node;
+ struct list_head *n;
+ ASSERT_RTNL();
+ list_for_each_safe(list_node, n, &chnl_net_list) {
+ if (list_node == delete_node) {
+ list_del(list_node);
+ break;
+ }
+ }
+}
+
+static int chnl_recv_cb(struct layer *layr, struct cfpkt *pkt)
+{
+ struct sk_buff *skb;
+ struct chnl_net *priv = NULL;
+ int pktlen;
+ int err = 0;
+
+ priv = container_of(layr, struct chnl_net, chnl);
+
+ if (!priv)
+ return -EINVAL;
+
+ /* Get length of CAIF packet. */
+ pktlen = cfpkt_getlen(pkt);
+
+ skb = (struct sk_buff *) cfpkt_tonative(pkt);
+ /* Pass some minimum information and
+ * send the packet to the net stack.
+ */
+ skb->dev = priv->netdev;
+ skb->protocol = htons(ETH_P_IP);
+
+ if (priv->config.type == CAIF_CHTY_DATAGRAM_LOOP) {
+ /* We change the header, so the checksum is corrupted. */
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ } else {
+ skb->ip_summed = CHECKSUM_COMPLETE;
+ }
+
+ /* FIXME: Drivers should call this in tasklet context. */
+ if (in_interrupt())
+ netif_rx(skb);
+ else
+ netif_rx_ni(skb);
+
+ /* Update statistics. */
+ priv->netdev->stats.rx_packets++;
+ priv->netdev->stats.rx_bytes += pktlen;
+
+ return err;
+}
+
+static void chnl_flowctrl_cb(struct layer *layr, enum caif_ctrlcmd flow,
+ int phyid)
+{
+ struct chnl_net *priv = NULL;
+ pr_debug("CAIF: %s(): NET flowctrl func called flow: %s.\n",
+ __func__,
+ flow == CAIF_CTRLCMD_FLOW_ON_IND ? "ON" :
+ flow == CAIF_CTRLCMD_INIT_RSP ? "INIT" :
+ flow == CAIF_CTRLCMD_FLOW_OFF_IND ? "OFF" :
+ flow == CAIF_CTRLCMD_DEINIT_RSP ? "CLOSE/DEINIT" :
+ flow == CAIF_CTRLCMD_INIT_FAIL_RSP ? "OPEN_FAIL" :
+ flow == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND ?
+ "REMOTE_SHUTDOWN" : "UKNOWN CTRL COMMAND");
+
+ priv = container_of(layr, struct chnl_net, chnl);
+
+ switch (flow) {
+ case CAIF_CTRLCMD_FLOW_OFF_IND:
+ case CAIF_CTRLCMD_DEINIT_RSP:
+ case CAIF_CTRLCMD_INIT_FAIL_RSP:
+ case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND:
+ priv->flowenabled = false;
+ netif_tx_disable(priv->netdev);
+ wake_up_interruptible(&priv->netmgmt_wq);
+ break;
+ case CAIF_CTRLCMD_FLOW_ON_IND:
+ case CAIF_CTRLCMD_INIT_RSP:
+ priv->flowenabled = true;
+ netif_wake_queue(priv->netdev);
+ wake_up_interruptible(&priv->netmgmt_wq);
+ break;
+ default:
+ break;
+ }
+}
+
+static int chnl_net_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct chnl_net *priv;
+ struct cfpkt *pkt = NULL;
+ int len;
+ int result = -1;
+
+ /* Get our private data. */
+ priv = (struct chnl_net *)netdev_priv(dev);
+ if (!priv)
+ return -ENOSPC;
+
+
+ if (skb->len > priv->netdev->mtu) {
+ pr_warning("CAIF: %s(): Size of skb exceeded MTU\n", __func__);
+ return -ENOSPC;
+ }
+
+ if (!priv->flowenabled) {
+ pr_debug("CAIF: %s(): dropping packets flow off\n", __func__);
+ return NETDEV_TX_BUSY;
+ }
+
+ if (priv->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;
+ }
+ /* Store original SKB length. */
+ len = skb->len;
+
+ pkt = cfpkt_fromnative(CAIF_DIR_OUT, (void *) skb);
+
+ /* Send the packet down the stack. */
+ result = priv->chnl.dn->transmit(priv->chnl.dn, pkt);
+ if (result) {
+ if (result == CFGLU_ERETRY)
+ result = NETDEV_TX_BUSY;
+ return result;
+ }
+
+ /* Update statistics. */
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += len;
+
+ return NETDEV_TX_OK;
+}
+
+
+static int chnl_net_open(struct net_device *dev)
+{
+ struct chnl_net *priv = NULL;
+ int result = -1;
+ ASSERT_RTNL();
+
+ priv = (struct chnl_net *)netdev_priv(dev);
+ pr_debug("CAIF: %s(): dev name: %s\n", __func__, priv->name);
+
+ if (!priv) {
+ pr_debug("CAIF: %s(): chnl_net_open: no priv\n", __func__);
+ return -ENODEV;
+ }
+ result = 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;
+ }
+ result = wait_event_interruptible(priv->netmgmt_wq, priv->flowenabled);
+
+ if (result == -ERESTARTSYS) {
+ pr_debug("CAIF: %s(): wait_event_interruptible"
+ " woken by a signal\n", __func__);
+ return -ERESTARTSYS;
+ } else
+ pr_debug("CAIF: %s(): Flow on recieved\n", __func__);
+
+ return 0;
+}
+
+static int chnl_net_stop(struct net_device *dev)
+{
+ struct chnl_net *priv;
+ int result = -1;
+ ASSERT_RTNL();
+ priv = (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__);
+
+ }
+
+ return 0;
+}
+
+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));
+ strncpy(priv->name, dev->name, sizeof(priv->name));
+ return 0;
+}
+
+void chnl_net_uninit(struct net_device *dev)
+{
+ struct chnl_net *priv;
+ ASSERT_RTNL();
+ priv = (struct chnl_net *)netdev_priv(dev);
+ /* If someone already have the lock it's already protected */
+ robust_list_del(&priv->list_field);
+ dev_put(dev);
+}
+
+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,
+};
+
+static void ipcaif_net_init(struct net_device *dev)
+{
+ struct chnl_net *priv;
+ dev->netdev_ops = &netdev_ops;
+ dev->destructor = free_netdev;
+ dev->flags |= IFF_NOARP;
+ dev->flags |= IFF_POINTOPOINT;
+ dev->needed_headroom = CAIF_NEEDED_HEADROOM;
+ dev->needed_tailroom = CAIF_NEEDED_TAILROOM;
+ dev->mtu = SIZE_MTU;
+ dev->tx_queue_len = CAIF_NET_DEFAULT_QUEUE_LEN;
+
+ priv = (struct chnl_net *)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->flowenabled = false;
+
+ ASSERT_RTNL();
+ init_waitqueue_head(&priv->netmgmt_wq);
+ list_add(&priv->list_field, &chnl_net_list);
+}
+
+static int delete_device(struct chnl_net *dev)
+{
+ ASSERT_RTNL();
+ if (dev->netdev)
+ unregister_netdevice(dev->netdev);
+ return 0;
+}
+
+static int ipcaif_fill_info(struct sk_buff *skb, const struct net_device *dev)
+{
+ struct chnl_net *priv;
+ u8 loop;
+ priv = (struct chnl_net *)netdev_priv(dev);
+ NLA_PUT_U32(skb, IFLA_CAIF_IPV4_CONNID,
+ priv->config.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;
+ NLA_PUT_U8(skb, IFLA_CAIF_LOOPBACK, loop);
+
+
+ return 0;
+nla_put_failure:
+ return -EMSGSIZE;
+
+}
+
+static void caif_netlink_parms(struct nlattr *data[],
+ struct caif_channel_config *parms)
+{
+ if (!data) {
+ pr_warning("CAIF: %s: no params data found\n", __func__);
+ return;
+ }
+ if (data[IFLA_CAIF_IPV4_CONNID])
+ parms->u.dgm.connection_id =
+ nla_get_u32(data[IFLA_CAIF_IPV4_CONNID]);
+ if (data[IFLA_CAIF_IPV6_CONNID])
+ parms->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;
+ else
+ parms->type = CAIF_CHTY_DATAGRAM;
+ }
+}
+
+static int ipcaif_newlink(struct net *src_net, struct net_device *dev,
+ struct nlattr *tb[], struct nlattr *data[])
+{
+ int err;
+ struct chnl_net *caifdev;
+ ASSERT_RTNL();
+ caifdev = netdev_priv(dev);
+ caif_netlink_parms(data, &caifdev->config);
+ err = register_netdevice(dev);
+ if (err) {
+ pr_warning("CAIF: %s(): device rtml registration failed\n",
+ __func__);
+ goto out;
+ }
+ dev_hold(dev);
+out:
+ return err;
+}
+
+static int ipcaif_changelink(struct net_device *dev, struct nlattr *tb[],
+ struct nlattr *data[])
+{
+ struct chnl_net *caifdev;
+ ASSERT_RTNL();
+ caifdev = netdev_priv(dev);
+ caif_netlink_parms(data, &caifdev->config);
+ netdev_state_change(dev);
+ return 0;
+}
+
+static size_t ipcaif_get_size(const struct net_device *dev)
+{
+ return
+ /* IFLA_CAIF_IPV4_CONNID */
+ nla_total_size(4) +
+ /* IFLA_CAIF_IPV6_CONNID */
+ nla_total_size(4) +
+ /* IFLA_CAIF_LOOPBACK */
+ nla_total_size(2) +
+ 0;
+}
+
+static const struct nla_policy ipcaif_policy[IFLA_CAIF_MAX + 1] = {
+ [IFLA_CAIF_IPV4_CONNID] = { .type = NLA_U32 },
+ [IFLA_CAIF_IPV6_CONNID] = { .type = NLA_U32 },
+ [IFLA_CAIF_LOOPBACK] = { .type = NLA_U8 }
+};
+
+
+static struct rtnl_link_ops ipcaif_link_ops __read_mostly = {
+ .kind = "caif",
+ .priv_size = (size_t)sizeof(struct chnl_net),
+ .setup = ipcaif_net_init,
+ .maxtype = IFLA_CAIF_MAX,
+ .policy = ipcaif_policy,
+ .newlink = ipcaif_newlink,
+ .changelink = ipcaif_changelink,
+ .get_size = ipcaif_get_size,
+ .fill_info = ipcaif_fill_info,
+
+};
+
+int chnl_net_ioctl(unsigned int cmd, unsigned long arg, bool from_user_land)
+{
+ struct chnl_net *priv;
+ int result = -1;
+ struct chnl_net *dev;
+ struct net_device *netdevptr;
+ int ret;
+ struct ifreq ifreq;
+ struct ifcaif_param param;
+ rtnl_lock();
+ if (from_user_land) {
+ if (copy_from_user(&ifreq, (const void *)arg, sizeof(ifreq)))
+ return -EFAULT;
+ } else
+ memcpy(&ifreq, (void *)arg, sizeof(ifreq));
+
+ if (cmd == SIOCCAIFNETREMOVE) {
+ pr_debug("CAIF: %s(): %s\n", __func__, ifreq.ifr_name);
+ dev = find_device(ifreq.ifr_name);
+ if (!dev)
+ ret = -ENODEV;
+ else
+ ret = delete_device(dev);
+ rtnl_unlock();
+ return ret;
+ }
+
+ if (cmd != SIOCCAIFNETNEW) {
+ rtnl_unlock();
+ return -ENOIOCTLCMD;
+ }
+ if (ifreq.ifr_ifru.ifru_data != NULL) {
+ if (from_user_land) {
+ ret = copy_from_user(¶m,
+ ifreq.ifr_ifru.ifru_data,
+ sizeof(param));
+ if (ret) {
+ rtnl_unlock();
+ return -EFAULT;
+ }
+ } else
+ memcpy(¶m,
+ ifreq.ifr_ifru.ifru_data,
+ sizeof(param));
+ ifreq.ifr_ifru.ifru_data = ¶m;
+ }
+
+ netdevptr = alloc_netdev(sizeof(struct chnl_net),
+ ifreq.ifr_name, ipcaif_net_init);
+ 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;
+
+ if (param.loop)
+ priv->config.type = CAIF_CHTY_DATAGRAM_LOOP;
+ else
+ priv->config.type = CAIF_CHTY_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;
+};
+
+struct net_device *chnl_net_create(char *name,
+ struct caif_channel_config *config)
+{
+ struct net_device *dev;
+ ASSERT_RTNL();
+ dev = alloc_netdev(sizeof(struct chnl_net), name, ipcaif_net_init);
+ if (!dev)
+ return NULL;
+ ((struct chnl_net *)netdev_priv(dev))->config = *config;
+ dev_hold(dev);
+ return dev;
+}
+EXPORT_SYMBOL(chnl_net_create);
+
+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;
+}
+
+static void __exit chnl_exit_module(void)
+{
+ struct chnl_net *dev = NULL;
+ struct list_head *list_node;
+ struct list_head *_tmp;
+ rtnl_lock();
+ list_for_each_safe(list_node, _tmp, &chnl_net_list) {
+ dev = list_entry(list_node, struct chnl_net, list_field);
+ delete_device(dev);
+ }
+ rtnl_unlock();
+ rtnl_link_unregister(&ipcaif_link_ops);
+ caif_register_ioctl(NULL);
+}
+
+module_init(chnl_init_module);
+module_exit(chnl_exit_module);
--
1.6.3.3
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PATCH net-next-2.6 10/13] net-caif: add kernel-client API for CAIF
2010-01-20 22:55 [PATCH net-next-2.6 00/13] net-caif: introducing CAIF protocol stack sjur.brandeland
` (8 preceding siblings ...)
2010-01-20 22:55 ` [PATCH net-next-2.6 09/13] net-caif: add CAIF netdevice sjur.brandeland
@ 2010-01-20 22:55 ` sjur.brandeland
2010-01-26 17:50 ` Randy Dunlap
2010-01-20 22:55 ` [PATCH net-next-2.6 11/13] net-caif: add CAIF Kconfig and Makefiles sjur.brandeland
` (3 subsequent siblings)
13 siblings, 1 reply; 47+ messages in thread
From: sjur.brandeland @ 2010-01-20 22:55 UTC (permalink / raw)
To: netdev; +Cc: davem, marcel, stefano.babic, randy.dunlap, Sjur Braendeland
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
This patch provides a CAIF API for managing CAIF devices from
kernel modules.
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
include/net/caif/caif_kernel.h | 309 ++++++++++++++++++++++++++++++++++++++++
net/caif/caif_chnlif.c | 177 +++++++++++++++++++++++
2 files changed, 486 insertions(+), 0 deletions(-)
diff --git a/include/net/caif/caif_kernel.h b/include/net/caif/caif_kernel.h
new file mode 100644
index 0000000..6617a8f
--- /dev/null
+++ b/include/net/caif/caif_kernel.h
@@ -0,0 +1,309 @@
+/*
+ * CAIF Kernel Internal interface for configuring and accessing
+ * CAIF Channels.
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland/ sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef CAIF_KERNEL_H_
+#define CAIF_KERNEL_H_
+#include <linux/caif/caif_config.h>
+struct sk_buff;
+
+/*!\page caif_kernel.h
+ * This is the specification of the CAIF kernel internal interface to
+ * CAIF Channels.
+ * This interface follows the pattern used in Linux device drivers with a
+ * struct \ref caif_device
+ * holding control data handling each device instance.
+ *
+ * The functional interface consists of a few basic functions:
+ * - \ref caif_add_device Configure and connect the CAIF
+ * channel to the remote end. Configuration is described in
+ * \ref caif_channel_config.
+ * - \ref caif_remove_device Disconnect and remove the channel.
+ * - \ref caif_transmit Sends a CAIF message on the link.
+ * - \ref caif_device.receive_cb Receive callback function for
+ * receiving packets.
+ * - \ref caif_device.control_cb Control information from the CAIF stack.
+ * - \ref caif_flow_control Send flow control message to remote end.
+ *
+ *
+ * Details:
+ * \see { caif_kernel }
+ *
+ * \code
+ *
+#include <net/caif/caif_kernel.h>"
+
+ static void my_receive(struct caif_device *dev, struct sk_buff *skb)
+ {
+ ...
+ }
+
+ static void my_control(struct caif_device *dev, enum caif_control ctrl)
+ {
+ ....
+ }
+
+ int kernel_caif_usage_example()
+ {
+ struct sk_buff *skb;
+ char *message = "hello";
+
+ // Connect the channel
+ struct caif_device caif_dev = {
+ .caif_config = {
+ .name = "MYDEV",
+ .priority = CAIF_PRIO_NORMAL,
+ .type = CAIF_CHTY_UTILITY,
+ .phy_pref = CAIF_PHYPREF_LOW_LAT,
+ .u.utility.name = "CAIF_PSOCK_TEST",
+ .u.utility.params = {0x01},
+ .u.utility.paramlen = 1,
+ },
+
+ .receive_cb = my_receive,
+ .control_cb = my_control,
+
+ };
+ ret = caif_add_device(&caif_dev);
+ if (ret)
+ goto error;
+
+ // Send a packet
+ skb = caif_create_skb(message, strlen(message));
+ ret = caif_transmit(&caif_dev, skb);
+ if (ret)
+ goto error;
+
+ // Remove device
+ ret = caif_remove_device(&caif_dev);
+ if (ret)
+ goto error;
+
+}
+
+* \endcode
+*
+* \section Linux Socket Buffer (SKB)
+ * When sending out packets on a connection (\ref caif_transmit)
+ * the CAIF stack will add CAIF protocol headers.
+ * This requires space in the SKB.
+ * CAIF has defined \ref CAIF_SKB_HEAD_RESERVE for minimum
+ * required reserved head-space in the packet and
+ * \ref CAIF_SKB_TAIL_RESERVE for minimum reserved tail-space.
+ *
+ * \b NOTE The Linux kernel SKB operations panic if not
+ * enough space is available!
+ *
+ */
+
+ /*! \addtogroup caif_kernel
+ * @{
+ */
+
+struct caif_device;
+
+ /** Minimum required CAIF socket buffer head-space */
+#define CAIF_SKB_HEAD_RESERVE 32
+
+ /** Minimum required CAIF socket buffer tail-space */
+#define CAIF_SKB_TAIL_RESERVE 32
+
+ /** CAIF control information (used in \ref caif_device.control_cb)
+ * used for receiving control information from the modem.
+ */
+enum caif_control {
+ /** Modem has sent Flow-ON, Clients can start transmitting
+ * data using \ref caif_transmit.
+ */
+ CAIF_CONTROL_FLOW_ON = 0,
+ /** Modem has sent Flow-OFF, Clients must stop transmitting
+ * data using \ref caif_transmit.
+ */
+ CAIF_CONTROL_FLOW_OFF = 1,
+
+ /** Channel creation is complete. This is an acknowledgement to
+ * \ref caif_add_device from the modem.
+ * The channel is ready for transmit (Flow-state is ON).
+ */
+ CAIF_CONTROL_DEV_INIT = 3,
+
+ /** Spontaneous close request from the modem, only applicable
+ * for utility link. The client should respond by calling
+ * \ref caif_remove_device.
+ */
+ CAIF_CONTROL_REMOTE_SHUTDOWN = 4,
+
+ /** Channel disconnect is complete. This is an acknowledgement to
+ * \ref caif_remove_device from the modem.
+ * \ref caif_transmit or \ref caif_flow_control must not be
+ * called after this.
+ */
+ CAIF_CONTROL_DEV_DEINIT = 5,
+
+ /** Channel creation has failed. This is a negative acknowledgement
+ * to \ref caif_add_device from the modem.
+ */
+ CAIF_CONTROL_DEV_INIT_FAILED = 6
+};
+
+/** Flow control information (used in \ref caif_device.control_cb) used
+ * for controlling outgoing flow.
+ */
+enum caif_flowctrl {
+ /** Flow Control is ON, transmit function can start sending data */
+ CAIF_FLOWCTRL_ON = 0,
+ /** Flow Control is OFF, transmit function should stop sending data */
+ CAIF_FLOWCTRL_OFF = 1,
+};
+
+/** Transmits CAIF packets on channel.
+ * This function is non-blocking and safe to use in tasklet context.
+ * The CAIF stack takes ownership of the socket buffer (SKB) after calling
+ * \ref caif_transmit.
+ * This means that the user cannot access the SKB afterwards; this applies
+ * even in error situations.
+ *
+ * @return 0 on success, < 0 upon error.
+ *
+ * @param[in] skb Socket buffer holding data to be written.
+ * @param[in] dev Structure used when creating the channel
+ *
+ *
+ * Error codes:
+ * - \b ENOTCONN, The channel is not connected.
+ * - \b EPROTO, Protocol error (or SKB is faulty)
+ * - \b EIO IO error (unspecified error)
+ */
+int caif_transmit(struct caif_device *dev, struct sk_buff *skb);
+
+/** Function for sending flow ON / OFF to remote end.
+ * This function is non-blocking and safe to use in tasklet context.
+ *
+ * @param[in] dev Reference to device data.
+ * @param[in] flow Flow control information.
+
+ * @return 0 on success, < 0 upon error.
+ * Error codes:
+ * - \b ENOTCONN, The channel is not connected.
+ * - \b EPROTO, Protocol error.
+ * - \b EIO IO error (unspecified error).
+ */
+int caif_flow_control(struct caif_device *dev, enum caif_flowctrl flow);
+
+/** Handle for kernel internal CAIF channels.
+ * All fields in this structure must be filled in by client before calling
+ * \ref caif_add_device (except _caif_handle).
+ */
+struct caif_device {
+
+ /** Channel configuration parameter. Contains information about type
+ * and configuration of the channel.
+ * This must be set before calling \ref caif_add_device.
+ */
+ struct caif_channel_config caif_config;
+
+
+ /** Callback function for receiving CAIF Packets from channel.
+ * This callback is called from softirq context (tasklet).
+ * The receiver <b> must </b> free the SKB.
+ * <b> DO NOT BLOCK IN THIS FUNCTION! </b>
+ *
+ * If the client has to do blocking operations then
+ * it must start its own work queue (or kernel thread).
+ *
+ * @param[in] dev Reference to device data.
+ * @param[in] skb Socket buffer with received data.
+ */
+ void (*receive_cb) (struct caif_device *dev, struct sk_buff *skb);
+
+
+ /** Callback function for notifying flow control from remote end - see
+ * \ref caif_control.
+ * This callback is called from from softirq context (tasklet).
+ *
+ * <b> DO NOT BLOCK IN THIS FUNCTION! </b>
+ *
+ * Client must not call \ref caif_transmit from this function.
+ *
+ * If the client has queued packets to send then
+ * it must start its own thread to do \ref caif_transmit.
+ *
+ * @param[in] dev Reference to device data.
+ * @param[in] ctrl CAIF control info \ref caif_control.
+ * e.g. Flow control
+ * \ref CAIF_CONTROL_FLOW_ON or
+ * \ref CAIF_CONTROL_FLOW_OFF
+ */
+ void (*control_cb) (struct caif_device *dev, enum caif_control ctrl);
+
+ /** This is a CAIF private attribute, holding CAIF internal reference
+ * to the CAIF stack. Do not update this field.
+ */
+ void *_caif_handle;
+
+ /** This field may be filled in by client for their own usage. */
+ void *user_data;
+};
+
+/** Add (connect) a CAIF Channel.
+ * This function is non-blocking. The channel connect is reported in
+ * \ref caif_device.control_cb.
+ * The channel is not open until \ref caif_device.control_cb is called with
+ * \ref CAIF_CONTROL_DEV_INIT.
+ * If setting up the channel fails then \ref caif_device.control_cb is called
+ * with \ref CAIF_CONTROL_DEV_INIT_FAILED.
+ *
+ * \ref caif_transmit, \ref caif_flow_control or \ref caif_remove_device must
+ * not be called before receiveing CAIF_CONTROL_DEV_INIT.
+ * @return 0 on success, < 0 on failure.
+ *
+ * Error codes:
+ * - \b -EINVAL Invalid arguments
+ * - \b -ENODEV No PHY device exists.
+ * - \b -EIO IO error (unspecified error)
+ */
+int caif_add_device(struct caif_device *dev);
+
+/** Disconnect a CAIF Channel
+ * This function is non-blocking.
+ * The channel has not been disconnected until \ref caif_device : control_cb is
+ * called with \ref CAIF_CONTROL_DEV_DEINIT.
+ * \ref caif_transmit or \ref caif_flow_control \b must not be called after
+ * receiving \ref CAIF_CONTROL_DEV_DEINIT.
+ * The client is responsible for freeing the \ref caif_device structure after
+ * receiving \ref CAIF_CONTROL_DEV_DEINIT (if applicable).
+ * @return 0 on success.
+ *
+ * - \b EIO IO error (unspecified error)
+ */
+int caif_remove_device(struct caif_device *caif_dev);
+
+/** Convenience function for allocating a socket buffer for usage with CAIF
+ * and copy user data into the socket buffer.
+ * @param[in] data User data to send with CAIF.
+ * @param[in] data_length Length of data to send.
+ * @return New socket buffer containing user data.
+ */
+struct sk_buff *caif_create_skb(unsigned char *data, unsigned int data_length);
+
+/** Convenience function for extracting data from a socket buffer (SKB) and
+ * then destroying the SKB.
+ * Copies data from the SKB and then frees the SKB.
+ * @param[in] skb SKB to extract data from. SKB will be freed after
+ * extracting data.
+ *
+ * @param[in] data User data buffer to extract packet data into.
+ * @param[in] max_length User data buffer length,
+ * @return number of bytes extracted; < 0 upon error.
+ *
+ */
+int caif_extract_and_destroy_skb(struct sk_buff *skb, unsigned char *data,
+ unsigned int max_length);
+
+/*! @} */
+
+#endif /* CAIF_KERNEL_H_ */
diff --git a/net/caif/caif_chnlif.c b/net/caif/caif_chnlif.c
new file mode 100644
index 0000000..5b70f4b
--- /dev/null
+++ b/net/caif/caif_chnlif.c
@@ -0,0 +1,177 @@
+/*
+ * 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_dev.h>
+struct caif_kernelif {
+ struct layer layer;
+ struct caif_device *dev;
+ struct cfctrl_link_param param;
+};
+
+
+/*
+ * func caif_create_skb - Creates a CAIF SKB buffer
+ * @data: data to add to buffer
+ * @data_length: length 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);
+ if (skb == NULL)
+ return NULL;
+ skb_reserve(skb, CAIF_SKB_HEAD_RESERVE);
+
+ 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;
+ /*
+ * Note: skb_linearize only fails on an out of memory condition
+ * if we fail here we are NOT freeing the skb.
+ */
+ if (!skb_linearize(skb) || 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 client's responcibility.
+ */
+int caif_transmit(struct caif_device *dev, struct sk_buff *skb)
+{
+ struct caif_kernelif *chnlif =
+ (struct caif_kernelif *) dev->_caif_handle;
+ struct cfpkt *pkt;
+ pkt = cfpkt_fromnative(CAIF_DIR_OUT, (void *) skb);
+ return chnlif->layer.dn->transmit(chnlif->layer.dn, pkt);
+}
+EXPORT_SYMBOL(caif_transmit);
+
+int caif_flow_control(struct caif_device *dev, enum caif_flowctrl flow)
+{
+ enum caif_modemcmd 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(struct layer *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;
+}
+
+static void chnlif_flowctrl(struct layer *layr, enum caif_ctrlcmd ctrl,
+ int phyid)
+{
+ struct caif_kernelif *chnl = (struct caif_kernelif *) layr;
+ enum caif_control ctl;
+
+ 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));
+ if (!chnl)
+ return -ENOMEM;
+ chnl->dev = dev;
+ chnl->layer.ctrlcmd = chnlif_flowctrl;
+ chnl->layer.receive = chnlif_receive;
+ ret =
+ channel_config_2_link_param(get_caif_conf(), &dev->caif_config,
+ &chnl->param);
+ if (ret < 0) {
+ ret = CFGLU_EBADPARAM;
+ goto error;
+ }
+ if (cfcnfg_add_adaptation_layer(get_caif_conf(), &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);
+ return cfcnfg_del_adapt_layer(get_caif_conf(), &chnl->layer);
+}
+EXPORT_SYMBOL(caif_remove_device);
--
1.6.3.3
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PATCH net-next-2.6 11/13] net-caif: add CAIF Kconfig and Makefiles
2010-01-20 22:55 [PATCH net-next-2.6 00/13] net-caif: introducing CAIF protocol stack sjur.brandeland
` (9 preceding siblings ...)
2010-01-20 22:55 ` [PATCH net-next-2.6 10/13] net-caif: add kernel-client API for CAIF sjur.brandeland
@ 2010-01-20 22:55 ` sjur.brandeland
2010-01-22 9:40 ` Marcel Holtmann
2010-01-20 22:55 ` [PATCH net-next-2.6 13/13] net-caif-driver: add CAIF serial driver (ldisc) sjur.brandeland
` (2 subsequent siblings)
13 siblings, 1 reply; 47+ messages in thread
From: sjur.brandeland @ 2010-01-20 22:55 UTC (permalink / raw)
To: netdev; +Cc: davem, marcel, stefano.babic, randy.dunlap, Sjur Braendeland
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
Kconfig and Makefiles with options for:
CAIF: Including caif
CAIF_DEBUG: CAIF Debug
CAIF_SOCK: CAIF Socket Implementation
CAIF_NETDEV: CAIF Network Device for GPRS Contexts
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
net/Kconfig | 2 +
net/Makefile | 1 +
net/caif/Kconfig | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++
net/caif/Makefile | 29 +++++++++++++++++++++++++++
4 files changed, 89 insertions(+), 0 deletions(-)
diff --git a/net/Kconfig b/net/Kconfig
index 041c35e..9342f7b 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -275,5 +275,7 @@ source "net/wimax/Kconfig"
source "net/rfkill/Kconfig"
source "net/9p/Kconfig"
+source "net/caif/Kconfig"
+
endif # if NET
diff --git a/net/Makefile b/net/Makefile
index 1542e72..a5eae27 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -56,6 +56,7 @@ obj-$(CONFIG_NETLABEL) += netlabel/
obj-$(CONFIG_IUCV) += iucv/
obj-$(CONFIG_RFKILL) += rfkill/
obj-$(CONFIG_NET_9P) += 9p/
+obj-$(CONFIG_CAIF) += caif/
ifneq ($(CONFIG_DCB),)
obj-y += dcb/
endif
diff --git a/net/caif/Kconfig b/net/caif/Kconfig
new file mode 100644
index 0000000..7d32059
--- /dev/null
+++ b/net/caif/Kconfig
@@ -0,0 +1,57 @@
+#
+# CAIF net configurations
+#
+
+#menu "CAIF Support"
+comment "CAIF Support"
+menuconfig CAIF
+ tristate "Enable CAIF support"
+ select CRC_CCITT
+ default n
+ ---help---
+ The "Communication CPU to Application CPU Interface" (CAIF) is a packet
+ based connection-oriented MUX protocol developed by ST-Ericsson for use
+ with its modems.
+
+ Say Y (or M) here if you build for a phone product (e.g. Android) that
+ uses CAIF as transport, if unsure say N.
+
+ If you select to build it as module then CAIF_SOCK and CAIF_NETDEV also
+ needs to be built as modules. You will also need to say yes to any CAIF
+ physical devices that your platform requires.
+
+ See Documentation/networking/caif for a further explanation on how to
+ use and configure CAIF.
+
+if CAIF
+
+config CAIF_DEBUG
+ bool "Enable Debug"
+ default n
+ --- help ---
+ Enable the inclusion of debug code in the CAIF stack.
+ Be aware that doing this will impact performance.
+ If unsure say N.
+
+config CAIF_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"
+ default CAIF
+ ---help---
+ Say Y if you will be using a CAIF based GPRS network device.
+ This can be either built-in or a loadable module,
+ If you select to build it as a built-in then the main CAIF device must
+ also be a built-in.
+ If unsure say Y.
+
+endif
+#endmenu
diff --git a/net/caif/Makefile b/net/caif/Makefile
new file mode 100644
index 0000000..c49438b
--- /dev/null
+++ b/net/caif/Makefile
@@ -0,0 +1,29 @@
+ifeq ($(CONFIG_CAIF_DEBUG),1)
+CAIF_DBG_FLAGS := -DDEBUG
+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
+
+clean-dirs:= .tmp_versions
+
+clean-files:= \
+ Module.symvers \
+ modules.order \
+ *.cmd \
+ *~ \
+ generic/*.o \
+ generic/*~
+
+obj-$(CONFIG_CAIF) += caif.o
+obj-$(CONFIG_CAIF_NETDEV) += chnl_net.o
+obj-$(CONFIG_CAIF_SOCK) += caif_socket.o
+
+export-objs := caif.o
+
--
1.6.3.3
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PATCH net-next-2.6 13/13] net-caif-driver: add CAIF serial driver (ldisc)
2010-01-20 22:55 [PATCH net-next-2.6 00/13] net-caif: introducing CAIF protocol stack sjur.brandeland
` (10 preceding siblings ...)
2010-01-20 22:55 ` [PATCH net-next-2.6 11/13] net-caif: add CAIF Kconfig and Makefiles sjur.brandeland
@ 2010-01-20 22:55 ` sjur.brandeland
2010-01-20 23:36 ` Randy Dunlap
2010-01-22 9:21 ` Marcel Holtmann
2010-01-22 9:43 ` [PATCH net-next-2.6 00/13] net-caif: introducing CAIF protocol stack Marcel Holtmann
[not found] ` <1264028130-14364-13-git-send-email-sjur.brandeland@stericsson.com>
13 siblings, 2 replies; 47+ messages in thread
From: sjur.brandeland @ 2010-01-20 22:55 UTC (permalink / raw)
To: netdev; +Cc: davem, marcel, stefano.babic, randy.dunlap, Sjur Braendeland
From: Sjur Braendeland <sjur.brandeland@stericsson.com>
Add CAIF Serial driver. This driver is implemented as a line discipline.
The TTY is opened from inside the kernel module.
caif_serial uses the following module parameters:
ser_ttyname - specifies the tty name.
ser_use_stx - specifies if STart of frame eXtension is in use.
ser_loop - sets the interface in loopback mode.
Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
drivers/net/Kconfig | 2 +
drivers/net/Makefile | 1 +
drivers/net/caif/Kconfig | 15 ++
drivers/net/caif/Makefile | 14 ++
drivers/net/caif/caif_serial.c | 420 ++++++++++++++++++++++++++++++++++++++++
include/linux/tty.h | 4 +-
6 files changed, 454 insertions(+), 2 deletions(-)
create mode 100644 drivers/net/caif/Kconfig
create mode 100644 drivers/net/caif/Makefile
create mode 100644 drivers/net/caif/caif_serial.c
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index dd9a09c..c2e670c 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2789,6 +2789,8 @@ source "drivers/ieee802154/Kconfig"
source "drivers/s390/net/Kconfig"
+source "drivers/net/caif/Kconfig"
+
config XEN_NETDEV_FRONTEND
tristate "Xen network device frontend driver"
depends on XEN
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index ad1346d..b7ffa35 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -285,5 +285,6 @@ obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
obj-$(CONFIG_SFC) += sfc/
obj-$(CONFIG_WIMAX) += wimax/
+obj-$(CONFIG_CAIF) += caif/
obj-$(CONFIG_OCTEON_MGMT_ETHERNET) += octeon/
diff --git a/drivers/net/caif/Kconfig b/drivers/net/caif/Kconfig
new file mode 100644
index 0000000..8a1a273
--- /dev/null
+++ b/drivers/net/caif/Kconfig
@@ -0,0 +1,15 @@
+#
+# CAIF physical drivers
+#
+
+if CAIF
+
+comment "CAIF transport drivers"
+
+config CAIF_TTY
+ tristate "CAIF TTY transport driver"
+ default n
+ ---help---
+ The CAIF TTY transport driver.
+
+endif # CAIF
diff --git a/drivers/net/caif/Makefile b/drivers/net/caif/Makefile
new file mode 100644
index 0000000..01784a0
--- /dev/null
+++ b/drivers/net/caif/Makefile
@@ -0,0 +1,14 @@
+ifeq ($(CONFIG_CAIF_DEBUG),1)
+CAIF_DBG_FLAGS := -DDEBUG
+endif
+
+KBUILD_EXTRA_SYMBOLS=net/caif/Module.symvers
+
+ccflags-y := $(CAIF_FLAGS) $(CAIF_DBG_FLAGS)
+clean-dirs:= .tmp_versions
+clean-files:= Module.symvers modules.order *.cmd *~ \
+
+# Serial interface
+obj-$(CONFIG_CAIF_TTY) += caif_serial.o
+
+
diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c
new file mode 100644
index 0000000..7d6636b
--- /dev/null
+++ b/drivers/net/caif/caif_serial.c
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland / sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/tty.h>
+#include <linux/file.h>
+#include <linux/if_arp.h>
+#include <net/caif/caif_device.h>
+#include <net/caif/generic/caif_layer.h>
+#include <net/caif/generic/cfcnfg.h>
+#include <linux/err.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sjur Brendeland<sjur.brandeland@stericsson.com>");
+MODULE_DESCRIPTION("CAIF serial device TTY line discipline");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_LDISC(N_CAIF);
+
+#define CAIF_SENDING 1
+#define CAIF_UART_TX_COMPLETED 2
+#define CAIF_FLOW_OFF_SENT 4
+#define MAX_WRITE_CHUNK 4096
+#define ON 1
+#define OFF 0
+#define CAIF_MAX_MTU 4096
+
+struct net_device *device;
+char *ser_ttyname = "/dev/ttyS0";
+module_param(ser_ttyname, charp, S_IRUGO);
+MODULE_PARM_DESC(ser_ttyname, "TTY to open.");
+
+int ser_loop;
+module_param(ser_loop, bool, S_IRUGO);
+MODULE_PARM_DESC(ser_loop, "Run in simulated loopback mode.");
+
+int ser_use_stx;
+module_param(ser_use_stx, bool, S_IRUGO);
+MODULE_PARM_DESC(ser_use_stx, "STX enabled or not.");
+
+int ser_write_chunk = MAX_WRITE_CHUNK;
+module_param(ser_write_chunk, int, S_IRUGO);
+
+MODULE_PARM_DESC(ser_write_chunk, "Maximum size of data written to UART.");
+
+
+static int caif_net_open(struct net_device *dev);
+static int caif_net_close(struct net_device *dev);
+
+struct ser_device {
+ struct caif_dev_common common;
+ struct net_device *dev;
+ struct sk_buff_head head;
+ int xoff;
+ struct tty_struct *tty;
+ bool tx_started;
+ unsigned long state;
+ struct file *file;
+ char *tty_name;
+};
+
+static int ser_phy_tx(struct ser_device *ser, struct sk_buff *skb);
+static void caifdev_setup(struct net_device *dev);
+static void ser_tx_wakeup(struct tty_struct *tty);
+
+static void ser_receive(struct tty_struct *tty, const u8 *data,
+ char *flags, int count)
+{
+ struct sk_buff *skb = NULL;
+ struct ser_device *ser;
+ int ret;
+ u8 *p;
+ ser = tty->disc_data;
+
+ /*
+ * Workaround for garbage at start of transmission,
+ * only enable if STX handling is not enables
+ */
+ if (!ser->common.use_stx && !ser->tx_started) {
+ dev_info(&ser->dev->dev,
+ "Bytes received before initial transmission -"
+ "bytes discarded.\n");
+ return;
+ }
+
+ BUG_ON(ser->dev == NULL);
+
+ /* Get a suitable caif packet and copy in data. */
+ skb = netdev_alloc_skb(ser->dev, count+1);
+ BUG_ON(skb == NULL);
+ p = skb_put(skb, count);
+ memcpy(p, data, count);
+
+ skb->protocol = htons(ETH_P_CAIF);
+ skb_reset_mac_header(skb);
+ skb->dev = ser->dev;
+
+ /* Push received packet up the stack. */
+ ret = netif_rx(skb);
+ if (!ret) {
+ ser->dev->stats.rx_packets++;
+ ser->dev->stats.rx_bytes += count;
+ } else
+ ++ser->dev->stats.rx_dropped;
+}
+
+static int handle_tx(struct ser_device *ser)
+{
+ struct tty_struct *tty;
+ struct sk_buff *skb;
+ char *buf;
+ int tty_wr, len, room, pktlen;
+ tty = ser->tty;
+
+ /*
+ * NOTE: This workaround is not really needed when STX is enabled.
+ * Remove?
+ */
+ if (ser->tx_started == false)
+ ser->tx_started = true;
+
+ if (test_and_set_bit(CAIF_SENDING, &ser->state)) {
+ set_bit(CAIF_UART_TX_COMPLETED, &ser->state);
+ return 0;
+ }
+
+ do {
+ skb = skb_peek(&ser->head);
+ if (skb != NULL && skb->len == 0) {
+ struct sk_buff *tmp;
+ tmp = skb_dequeue(&ser->head);
+ BUG_ON(tmp != skb);
+ kfree_skb(skb);
+ skb = skb_peek(&ser->head);
+ }
+
+ if (skb == NULL) {
+ if (test_and_clear_bit(
+ CAIF_FLOW_OFF_SENT,
+ &ser->state)) {
+ if (ser->common.flowctrl != NULL)
+ ser->common.flowctrl(ser->dev, ON);
+ }
+ break;
+ }
+
+
+ buf = skb->data;
+ pktlen = len = skb->len;
+
+ clear_bit(CAIF_UART_TX_COMPLETED, &ser->state);
+ room = tty_write_room(tty);
+ if (room > ser_write_chunk)
+ room = ser_write_chunk;
+
+ if (len > room)
+ len = room;
+
+ if (!ser_loop) {
+ tty_wr = tty->ops->write(tty, buf, len);
+ } else {
+ tty_wr = len;
+ ser_receive(tty, buf, 0, len);
+ }
+ ser->dev->stats.tx_packets++;
+ ser->dev->stats.tx_bytes += tty_wr;
+ if (tty_wr > 0)
+ skb_pull(skb, tty_wr);
+
+ if (ser_loop)
+ ser_tx_wakeup(tty);
+
+ } while (test_bit(CAIF_UART_TX_COMPLETED, &(ser->state)));
+
+ clear_bit(CAIF_SENDING, &ser->state);
+ return 0;
+}
+
+static int ser_phy_tx(struct ser_device *ser, struct sk_buff *skb)
+{
+ if (skb_peek(&ser->head) != NULL) {
+ if (!test_and_set_bit(CAIF_FLOW_OFF_SENT, &ser->state)
+ && ser->common.flowctrl != NULL)
+ ser->common.flowctrl(ser->dev, OFF);
+ }
+ skb_queue_tail(&ser->head, skb);
+ if (!test_bit(CAIF_SENDING, &ser->state))
+ handle_tx(ser);
+ return 0;
+}
+
+static int caif_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ser_device *ser;
+ if (!dev)
+ return -EINVAL;
+ ser = netdev_priv(dev);
+ return ser_phy_tx(ser, skb);
+}
+
+
+static void ser_tx_wakeup(struct tty_struct *tty)
+{
+ struct ser_device *ser;
+ ser = tty->disc_data;
+ if (ser == NULL)
+ return;
+ set_bit(CAIF_UART_TX_COMPLETED, &ser->state);
+ if (ser->tty != tty)
+ return;
+ handle_tx(ser);
+}
+
+static void remove_caif_phy_dev(struct net_device *dev)
+{
+ /* Remove may be called inside or outside of rtnl_lock */
+ int islocked = rtnl_is_locked();
+ if (!islocked)
+ rtnl_lock();
+ dev_close(dev);
+ /* device is freed automagically by net-sysfs */
+ unregister_netdevice(dev);
+ if (!islocked)
+ rtnl_unlock();
+}
+
+static int ser_open(struct tty_struct *tty)
+{
+ struct ser_device *ser;
+ /* Use global device to map tty with ser */
+ ser = netdev_priv(device);
+ if (ser->file == NULL ||
+ tty != (struct tty_struct *)ser->file->private_data) {
+ dev_err(&ser->dev->dev,
+ "Cannot install ldisc %s from userspace!",
+ tty->name);
+ return -EINVAL;
+ }
+ tty->receive_room = 4096;
+ ser->tty = tty;
+ tty->disc_data = ser;
+ set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+ return 0;
+}
+
+static void ser_close(struct tty_struct *tty)
+{
+ struct ser_device *ser;
+ ser = tty->disc_data;
+}
+
+static int start_ldisc(struct ser_device *ser)
+{
+ struct file *f;
+ mm_segment_t oldfs;
+ struct termios tio;
+ int ldiscnr = N_CAIF;
+ int ret;
+ f = filp_open(ser->tty_name, 0, 0);
+ if (IS_ERR(f)) {
+ dev_err(&ser->dev->dev, "CAIF cannot open:%s\n", ser->tty_name);
+ ret = -EINVAL;
+ goto error;
+ }
+ if (f == NULL || f->f_op == NULL || f->f_op->unlocked_ioctl == NULL) {
+ dev_err(&ser->dev->dev, "TTY cannot do IOCTL:%s\n",
+ ser->tty_name);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ ser->file = f;
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+
+ f->f_op->unlocked_ioctl(f, TCFLSH, 0x2);
+ memset(&tio, 0, sizeof(tio));
+ tio.c_cflag = B115200 | CRTSCTS | CS8 | CLOCAL | CREAD;
+ f->f_op->unlocked_ioctl(f, TCSETS, (long unsigned int)&tio);
+ f->f_op->unlocked_ioctl(f, TIOCSETD, (long unsigned int)&ldiscnr);
+ set_fs(oldfs);
+ return 0;
+error:
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ return ret;
+}
+
+/* The line discipline structure. */
+static struct tty_ldisc_ops caif_ldisc = {
+ .owner = THIS_MODULE,
+ .magic = TTY_LDISC_MAGIC,
+ .name = "n_caif",
+ .open = ser_open,
+ .close = ser_close,
+ .receive_buf = ser_receive,
+ .write_wakeup = ser_tx_wakeup
+};
+
+
+static int register_ldisc(struct ser_device *ser)
+{
+ int result;
+ result = tty_register_ldisc(N_CAIF, &caif_ldisc);
+
+ if (result < 0) {
+ dev_err(&ser->dev->dev,
+ "cannot register CAIF ldisc=%d err=%d\n",
+ N_CAIF,
+ result);
+ return result;
+ }
+ return result;
+}
+
+static const struct net_device_ops netdev_ops = {
+ .ndo_open = caif_net_open,
+ .ndo_stop = caif_net_close,
+ .ndo_start_xmit = caif_xmit
+};
+static void caifdev_setup(struct net_device *dev)
+{
+ struct ser_device *serdev = netdev_priv(dev);
+ dev->features = 0;
+
+ dev->netdev_ops = &netdev_ops;
+
+ dev->type = ARPHRD_CAIF;
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_POINTOPOINT;
+ dev->mtu = CAIF_MAX_MTU;
+ dev->hard_header_len = CAIF_NEEDED_HEADROOM;
+ dev->tx_queue_len = 0;
+ dev->destructor = free_netdev;
+ skb_queue_head_init(&serdev->head);
+ serdev->common.link_select = CAIF_LINK_LOW_LATENCY;
+ serdev->common.use_frag = true;
+ serdev->common.use_stx = ser_use_stx;
+ serdev->common.use_fcs = true;
+ serdev->xoff = 0;
+ serdev->dev = dev;
+}
+
+static int caif_net_open(struct net_device *dev)
+{
+ struct ser_device *ser;
+ int ret;
+ ser = netdev_priv(dev);
+ ret = register_ldisc(ser);
+ if (ret)
+ return ret;
+ ret = start_ldisc(ser);
+ if (ret) {
+ dev_err(&ser->dev->dev, "CAIF: %s() - open failed:%d\n",
+ __func__, ret);
+ tty_unregister_ldisc(N_CAIF);
+ return ret;
+ }
+ netif_wake_queue(dev);
+ ser->xoff = 0;
+ return 0;
+}
+
+static int caif_net_close(struct net_device *dev)
+{
+ struct ser_device *ser = netdev_priv(dev);
+ netif_stop_queue(dev);
+ /* Close the file handle */
+ if (ser->file)
+ fput(ser->file);
+ return tty_unregister_ldisc(N_CAIF);
+}
+
+static int register_setenv(char *ttyname,
+ struct net_device **device)
+{
+ struct net_device *dev;
+ struct ser_device *ser;
+ int result;
+ dev = alloc_netdev(sizeof(*ser), "caifser%d", caifdev_setup);
+ if (!dev)
+ return -ENODEV;
+ ser = netdev_priv(dev);
+ pr_info("CAIF: %s(): "
+ "Starting CAIF Physical LDisc on tty:%s\n",
+ __func__, ttyname);
+ ser->tty_name = ttyname;
+ netif_stop_queue(dev);
+ ser->dev = dev;
+
+ *device = dev;
+ result = register_netdev(dev);
+ if (result) {
+ free_netdev(dev);
+ *device = 0;
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int __init caif_ser_init(void)
+{
+ return register_setenv(ser_ttyname, &device);
+}
+
+static void __exit caif_ser_exit(void)
+{
+ remove_caif_phy_dev(device);
+}
+
+module_init(caif_ser_init);
+module_exit(caif_ser_exit);
diff --git a/include/linux/tty.h b/include/linux/tty.h
index ef3a294..5dd674b 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -23,7 +23,7 @@
*/
#define NR_UNIX98_PTY_DEFAULT 4096 /* Default maximum for Unix98 ptys */
#define NR_UNIX98_PTY_MAX (1 << MINORBITS) /* Absolute limit */
-#define NR_LDISCS 20
+#define NR_LDISCS 21
/* line disciplines */
#define N_TTY 0
@@ -46,8 +46,8 @@
#define N_GIGASET_M101 16 /* Siemens Gigaset M101 serial DECT adapter */
#define N_SLCAN 17 /* Serial / USB serial CAN Adaptors */
#define N_PPS 18 /* Pulse per Second */
-
#define N_V253 19 /* Codec control over voice modem */
+#define N_CAIF 20 /* CAIF protocol for talking to modems */
/*
* This character is the same as _POSIX_VDISABLE: it cannot be used as
--
1.6.3.3
^ permalink raw reply related [flat|nested] 47+ messages in thread
* Re: [PATCH net-next-2.6 02/13] net-caif: add CAIF header files
2010-01-20 22:55 ` [PATCH net-next-2.6 02/13] net-caif: add CAIF header files sjur.brandeland
@ 2010-01-20 23:27 ` Randy Dunlap
2010-01-22 11:05 ` Sjur Brændeland
2010-01-21 7:44 ` Patrick McHardy
2010-01-22 7:51 ` Marcel Holtmann
2 siblings, 1 reply; 47+ messages in thread
From: Randy Dunlap @ 2010-01-20 23:27 UTC (permalink / raw)
To: sjur.brandeland; +Cc: netdev, davem, marcel, stefano.babic
On Wed, 20 Jan 2010 23:55:19 +0100 sjur.brandeland@stericsson.com wrote:
Hi,
Just a couple of nits here (below):
> From: Sjur Braendeland <sjur.brandeland@stericsson.com>
>
> Add CAIF types for Socket Address, Socket Options,
> and configuration parameters for the GPRS IP network interface.
>
> Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
> ---
> include/linux/caif/caif_config.h | 155 ++++++++++++++++++++++++++++++++++++
> include/linux/caif/caif_socket.h | 161 ++++++++++++++++++++++++++++++++++++++
> include/linux/caif/if_caif.h | 71 +++++++++++++++++
> 3 files changed, 387 insertions(+), 0 deletions(-)
>
> diff --git a/include/linux/caif/caif_config.h b/include/linux/caif/caif_config.h
> new file mode 100644
> index 0000000..5de8bd2
> --- /dev/null
> +++ b/include/linux/caif/caif_config.h
> @@ -0,0 +1,155 @@
> +/*
> + * CAIF Channel Configuration definitions.
> + * Copyright (C) ST-Ericsson AB 2010
> + * Author: Sjur Brendeland/ sjur.brandeland@stericsson.com
> + * License terms: GNU General Public License (GPL) version 2
> + */
> +
> +#ifndef CAIF_CONFIG_H_
> +#define CAIF_CONFIG_H_
> +
...
> +struct caif_channel_config {
> + char name[16];
> + enum caif_channel_type type;
> + unsigned priority;
> + enum caif_phy_preference phy_pref;
> + char phy_name[16];
> +
> + /** Union of channel type-specific configuration parameters.
Don't use /** here.
> + * 'switched' by attribute type.
> + */
> + union {
> + /* CAIF_CHTYPE_DATAGRAM */
> + struct {
> + unsigned connection_id;
> + } dgm;
> + /* CAIF_CHTYPE_VIDEO */
> + struct {
> + unsigned connection_id;
> + } video;
> + /* CAIF_CHTYPE_RFM */
> + struct {
> + unsigned connection_id;
> + char volume[20];
> + } rfm;
> + /* CAIF_CHTYPE_UTILITY */
> + struct {
> + unsigned fifosize_kb;
> + unsigned fifosize_bufs;
> + char name[16];
> + unsigned char params[256];
> + int paramlen;
> + } utility;
> +
> + } u;
> +};
> +
> +#endif /* CAIF_CONFIG_H_ */
> diff --git a/include/linux/caif/if_caif.h b/include/linux/caif/if_caif.h
> new file mode 100644
> index 0000000..e1991b1
> --- /dev/null
> +++ b/include/linux/caif/if_caif.h
> @@ -0,0 +1,71 @@
> +/*
> + * Copyright (C) ST-Ericsson AB 2010
> + * Author: Sjur Brendeland/ sjur.brandeland@stericsson.com
> + * License terms: GNU General Public License (GPL) version 2
> + */
> +
> +#ifndef IF_CAIF_H_
> +#define IF_CAIF_H_
> +#include <linux/sockios.h>
> +#include <linux/types.h>
> +#include <linux/socket.h>
> +
> +/**
> + * enum sioc_caif - SOCKIO for creating new CAIF Net Devices.
> + * @SIOCCAIFNETNEW: Used to create a new instance of the CAIF IP Interface.
> + * struct ifreq containing struct ifcaif_param are used
> + * as parameters. ifr_name must be filled in.
> + * @SIOCCAIFNETCHANGE: As above, but changes a disconnected CAIF IP Inteface.
> + * @SIOCCAIFNETREMOVE: Removes a CAIF IP Interface.
> + *
> + * CAIF IP Interface can be created, changed and deleted,
> + * by this enum. In addition standard Socket IO Controls (SIGIOC*)
> + * can be used to manage standard IP Interface parameters.
> + * The struct ifreq are used to carry parameters.
> + */
> +enum sioc_caif {
> + SIOCCAIFNETNEW = SIOCPROTOPRIVATE,
> + SIOCCAIFNETCHANGE,
> + SIOCCAIFNETREMOVE
> +};
> +
> +
> +/**
> + * struct ifcaif_param - Parameters for creating CAIF Network Interface.
> + *
> + * When using SIOCCAIFNETNEW to create a CAIF IP interface, this structure
> + * is used for configuration data.
> + * The attribute ifr_ifru.ifru_data in struct struct ifreq must be set
> + * point at an instance of struct ifcaif_param.
> + *
> + * @ipv4_connid: Connection ID for IPv4 PDP Context.
> + * @ipv6_connid: Connection ID for IPv6 PDP Context.
> + * @loop: If different from zero, device is doing loopback
> + */
> +struct ifcaif_param {
> + __u32 ipv4_connid;
> + __u32 ipv6_connid;
> + __u8 loop;
> +};
> +
> +/**
> + * enum ifla_caif
Above line needs " - <short description>" on it.
> + * When using RT Netlink to create, destroy or configure a CAIF IP interface,
> + * enum ifla_caif is used to specify the configuration attributes.
> + *
> + * @IFLA_CAIF_IPV4_CONNID: Connection ID for IPv4 PDP Context.
> + * The type of attribute is NLA_U32.
> + * @IFLA_CAIF_IPV6_CONNID: Connection ID for IPv6 PDP Context.
> + * The type of attribute is NLA_U32.
> + * @IFLA_CAIF_LOOPBACK: If different from zero, device is doing loopback
> + * The type of attribute is NLA_U8.
> + */
> +enum ifla_caif {
> + IFLA_CAIF_IPV4_CONNID,
> + IFLA_CAIF_IPV6_CONNID,
> + IFLA_CAIF_LOOPBACK,
> + __IFLA_CAIF_MAX
> +};
> +#define IFLA_CAIF_MAX (__IFLA_CAIF_MAX-1)
> +
> +#endif /*IF_CAIF_H_*/
> --
---
~Randy
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH net-next-2.6 13/13] net-caif-driver: add CAIF serial driver (ldisc)
2010-01-20 22:55 ` [PATCH net-next-2.6 13/13] net-caif-driver: add CAIF serial driver (ldisc) sjur.brandeland
@ 2010-01-20 23:36 ` Randy Dunlap
2010-01-22 11:07 ` Sjur Brændeland
2010-01-22 9:21 ` Marcel Holtmann
1 sibling, 1 reply; 47+ messages in thread
From: Randy Dunlap @ 2010-01-20 23:36 UTC (permalink / raw)
To: sjur.brandeland; +Cc: netdev, davem, marcel, stefano.babic
On Wed, 20 Jan 2010 23:55:30 +0100 sjur.brandeland@stericsson.com wrote:
> From: Sjur Braendeland <sjur.brandeland@stericsson.com>
>
> Add CAIF Serial driver. This driver is implemented as a line discipline.
> The TTY is opened from inside the kernel module.
>
> caif_serial uses the following module parameters:
> ser_ttyname - specifies the tty name.
> ser_use_stx - specifies if STart of frame eXtension is in use.
> ser_loop - sets the interface in loopback mode.
>
> Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
> ---
> drivers/net/Kconfig | 2 +
> drivers/net/Makefile | 1 +
> drivers/net/caif/Kconfig | 15 ++
> drivers/net/caif/Makefile | 14 ++
> drivers/net/caif/caif_serial.c | 420 ++++++++++++++++++++++++++++++++++++++++
> include/linux/tty.h | 4 +-
> 6 files changed, 454 insertions(+), 2 deletions(-)
> create mode 100644 drivers/net/caif/Kconfig
> create mode 100644 drivers/net/caif/Makefile
> create mode 100644 drivers/net/caif/caif_serial.c
> diff --git a/drivers/net/caif/Makefile b/drivers/net/caif/Makefile
> new file mode 100644
> index 0000000..01784a0
> --- /dev/null
> +++ b/drivers/net/caif/Makefile
> @@ -0,0 +1,14 @@
> +ifeq ($(CONFIG_CAIF_DEBUG),1)
> +CAIF_DBG_FLAGS := -DDEBUG
> +endif
> +
> +KBUILD_EXTRA_SYMBOLS=net/caif/Module.symvers
> +
> +ccflags-y := $(CAIF_FLAGS) $(CAIF_DBG_FLAGS)
> +clean-dirs:= .tmp_versions
> +clean-files:= Module.symvers modules.order *.cmd *~ \
> +
> +# Serial interface
> +obj-$(CONFIG_CAIF_TTY) += caif_serial.o
> +
> +
> diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c
> new file mode 100644
> index 0000000..7d6636b
> --- /dev/null
> +++ b/drivers/net/caif/caif_serial.c
> @@ -0,0 +1,420 @@
> +/*
> + * Copyright (C) ST-Ericsson AB 2010
> + * Author: Sjur Brendeland / sjur.brandeland@stericsson.com
> + * License terms: GNU General Public License (GPL) version 2
> + */
> +
> +#include <linux/init.h>
> +#include <linux/version.h>
> +#include <linux/module.h>
> +#include <linux/device.h>
> +#include <linux/types.h>
> +#include <linux/skbuff.h>
> +#include <linux/netdevice.h>
> +#include <linux/rtnetlink.h>
> +#include <linux/tty.h>
> +#include <linux/file.h>
> +#include <linux/if_arp.h>
> +#include <net/caif/caif_device.h>
> +#include <net/caif/generic/caif_layer.h>
> +#include <net/caif/generic/cfcnfg.h>
> +#include <linux/err.h>
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Sjur Brendeland<sjur.brandeland@stericsson.com>");
> +MODULE_DESCRIPTION("CAIF serial device TTY line discipline");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS_LDISC(N_CAIF);
> +
> +#define CAIF_SENDING 1
> +#define CAIF_UART_TX_COMPLETED 2
> +#define CAIF_FLOW_OFF_SENT 4
> +#define MAX_WRITE_CHUNK 4096
> +#define ON 1
> +#define OFF 0
> +#define CAIF_MAX_MTU 4096
> +
> +struct net_device *device;
> +char *ser_ttyname = "/dev/ttyS0";
> +module_param(ser_ttyname, charp, S_IRUGO);
> +MODULE_PARM_DESC(ser_ttyname, "TTY to open.");
> +
Can't a lot of these data items be static? (above & below)
> +int ser_loop;
> +module_param(ser_loop, bool, S_IRUGO);
> +MODULE_PARM_DESC(ser_loop, "Run in simulated loopback mode.");
> +
> +int ser_use_stx;
> +module_param(ser_use_stx, bool, S_IRUGO);
> +MODULE_PARM_DESC(ser_use_stx, "STX enabled or not.");
> +
> +int ser_write_chunk = MAX_WRITE_CHUNK;
> +module_param(ser_write_chunk, int, S_IRUGO);
> +
> +MODULE_PARM_DESC(ser_write_chunk, "Maximum size of data written to UART.");
> +
> +
> +static int caif_net_open(struct net_device *dev);
> +static int caif_net_close(struct net_device *dev);
> +
> +struct ser_device {
> + struct caif_dev_common common;
> + struct net_device *dev;
> + struct sk_buff_head head;
> + int xoff;
> + struct tty_struct *tty;
> + bool tx_started;
> + unsigned long state;
> + struct file *file;
> + char *tty_name;
> +};
> +
> +static int ser_phy_tx(struct ser_device *ser, struct sk_buff *skb);
> +static void caifdev_setup(struct net_device *dev);
> +static void ser_tx_wakeup(struct tty_struct *tty);
> +
...
> +
> +static int start_ldisc(struct ser_device *ser)
> +{
> + struct file *f;
> + mm_segment_t oldfs;
> + struct termios tio;
> + int ldiscnr = N_CAIF;
> + int ret;
> + f = filp_open(ser->tty_name, 0, 0);
> + if (IS_ERR(f)) {
> + dev_err(&ser->dev->dev, "CAIF cannot open:%s\n", ser->tty_name);
> + ret = -EINVAL;
> + goto error;
> + }
> + if (f == NULL || f->f_op == NULL || f->f_op->unlocked_ioctl == NULL) {
> + dev_err(&ser->dev->dev, "TTY cannot do IOCTL:%s\n",
> + ser->tty_name);
> + ret = -EINVAL;
I'm thinking that the errcode should be (negative) ENOTTY...
> + goto error;
> + }
> +
> + ser->file = f;
> + oldfs = get_fs();
> + set_fs(KERNEL_DS);
> +
> + f->f_op->unlocked_ioctl(f, TCFLSH, 0x2);
> + memset(&tio, 0, sizeof(tio));
> + tio.c_cflag = B115200 | CRTSCTS | CS8 | CLOCAL | CREAD;
> + f->f_op->unlocked_ioctl(f, TCSETS, (long unsigned int)&tio);
> + f->f_op->unlocked_ioctl(f, TIOCSETD, (long unsigned int)&ldiscnr);
> + set_fs(oldfs);
> + return 0;
> +error:
> + oldfs = get_fs();
> + set_fs(KERNEL_DS);
> + return ret;
> +}
---
~Randy
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH net-next-2.6 02/13] net-caif: add CAIF header files
2010-01-20 22:55 ` [PATCH net-next-2.6 02/13] net-caif: add CAIF header files sjur.brandeland
2010-01-20 23:27 ` Randy Dunlap
@ 2010-01-21 7:44 ` Patrick McHardy
2010-01-22 10:53 ` Sjur Brændeland
2010-01-22 7:51 ` Marcel Holtmann
2 siblings, 1 reply; 47+ messages in thread
From: Patrick McHardy @ 2010-01-21 7:44 UTC (permalink / raw)
To: sjur.brandeland; +Cc: netdev, davem, marcel, stefano.babic, randy.dunlap
sjur.brandeland@stericsson.com wrote:
> +/**
> + * enum ifla_caif
> + * When using RT Netlink to create, destroy or configure a CAIF IP interface,
> + * enum ifla_caif is used to specify the configuration attributes.
> + *
> + * @IFLA_CAIF_IPV4_CONNID: Connection ID for IPv4 PDP Context.
> + * The type of attribute is NLA_U32.
> + * @IFLA_CAIF_IPV6_CONNID: Connection ID for IPv6 PDP Context.
> + * The type of attribute is NLA_U32.
> + * @IFLA_CAIF_LOOPBACK: If different from zero, device is doing loopback
> + * The type of attribute is NLA_U8.
> + */
> +enum ifla_caif {
> + IFLA_CAIF_IPV4_CONNID,
> + IFLA_CAIF_IPV6_CONNID,
> + IFLA_CAIF_LOOPBACK,
> + __IFLA_CAIF_MAX
> +};
> +#define IFLA_CAIF_MAX (__IFLA_CAIF_MAX-1)
> +
> +#endif /*IF_CAIF_H_*/
You should add an IFLA_CAIF_UNSPEC at the beginning, some of the
nlattr helpers skip attributes with a value of zero.
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH net-next-2.6 09/13] net-caif: add CAIF netdevice
2010-01-20 22:55 ` [PATCH net-next-2.6 09/13] net-caif: add CAIF netdevice sjur.brandeland
@ 2010-01-21 8:03 ` Patrick McHardy
2010-02-02 12:37 ` Sjur Brændeland
0 siblings, 1 reply; 47+ messages in thread
From: Patrick McHardy @ 2010-01-21 8:03 UTC (permalink / raw)
To: sjur.brandeland; +Cc: netdev, davem, marcel, stefano.babic, randy.dunlap
sjur.brandeland@stericsson.com wrote:
> +static void ipcaif_net_init(struct net_device *dev)
> +{
> + struct chnl_net *priv;
> + dev->netdev_ops = &netdev_ops;
> + dev->destructor = free_netdev;
These (especially ->destructor) should be set in the setup function.
> + dev->flags |= IFF_NOARP;
> + dev->flags |= IFF_POINTOPOINT;
> + dev->needed_headroom = CAIF_NEEDED_HEADROOM;
> + dev->needed_tailroom = CAIF_NEEDED_TAILROOM;
> + dev->mtu = SIZE_MTU;
> + dev->tx_queue_len = CAIF_NET_DEFAULT_QUEUE_LEN;
These too I guess, its uncommon to reinitialize mtu and tx_queue_len
when setting a device down and up again.
> +
> + priv = (struct chnl_net *)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->flowenabled = false;
> +
> + ASSERT_RTNL();
> + init_waitqueue_head(&priv->netmgmt_wq);
> + list_add(&priv->list_field, &chnl_net_list);
> +}
> +static int chnl_net_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
static netdev_tx_t
> +{
> + struct chnl_net *priv;
> + struct cfpkt *pkt = NULL;
> + int len;
> + int result = -1;
> +
> + /* Get our private data. */
> + priv = (struct chnl_net *)netdev_priv(dev);
> + if (!priv)
> + return -ENOSPC;
This is an impossible condition, netdev_priv() will never return NULL.
The cast is also unnecessary.
> +
> +
> + if (skb->len > priv->netdev->mtu) {
> + pr_warning("CAIF: %s(): Size of skb exceeded MTU\n", __func__);
> + return -ENOSPC;
> + }
> +
> + if (!priv->flowenabled) {
> + pr_debug("CAIF: %s(): dropping packets flow off\n", __func__);
> + return NETDEV_TX_BUSY;
> + }
> +
> + if (priv->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;
swap()?
> + }
> + /* Store original SKB length. */
> + len = skb->len;
> +
> + pkt = cfpkt_fromnative(CAIF_DIR_OUT, (void *) skb);
> +
> + /* Send the packet down the stack. */
> + result = priv->chnl.dn->transmit(priv->chnl.dn, pkt);
> + if (result) {
> + if (result == CFGLU_ERETRY)
> + result = NETDEV_TX_BUSY;
> + return result;
> + }
> +
> + /* Update statistics. */
> + dev->stats.tx_packets++;
> + dev->stats.tx_bytes += len;
> +
> + return NETDEV_TX_OK;
> +}
> +
> +static int ipcaif_newlink(struct net *src_net, struct net_device *dev,
> + struct nlattr *tb[], struct nlattr *data[])
> +{
> + int err;
> + struct chnl_net *caifdev;
> + ASSERT_RTNL();
> + caifdev = netdev_priv(dev);
> + caif_netlink_parms(data, &caifdev->config);
> + err = register_netdevice(dev);
> + if (err) {
> + pr_warning("CAIF: %s(): device rtml registration failed\n",
> + __func__);
> + goto out;
> + }
> + dev_hold(dev);
What is this reference used for? You don't have a dellink function, so
this looks like a leak.
> +out:
> + return err;
> +}
> +
> +static int ipcaif_changelink(struct net_device *dev, struct nlattr *tb[],
> + struct nlattr *data[])
> +{
> + struct chnl_net *caifdev;
> + ASSERT_RTNL();
> + caifdev = netdev_priv(dev);
> + caif_netlink_parms(data, &caifdev->config);
> + netdev_state_change(dev);
> + return 0;
> +}
> +
> +static size_t ipcaif_get_size(const struct net_device *dev)
> +{
> + return
> + /* IFLA_CAIF_IPV4_CONNID */
> + nla_total_size(4) +
> + /* IFLA_CAIF_IPV6_CONNID */
> + nla_total_size(4) +
> + /* IFLA_CAIF_LOOPBACK */
> + nla_total_size(2) +
> + 0;
> +}
> +
> +static const struct nla_policy ipcaif_policy[IFLA_CAIF_MAX + 1] = {
> + [IFLA_CAIF_IPV4_CONNID] = { .type = NLA_U32 },
> + [IFLA_CAIF_IPV6_CONNID] = { .type = NLA_U32 },
> + [IFLA_CAIF_LOOPBACK] = { .type = NLA_U8 }
> +};
> +
> +
> +static struct rtnl_link_ops ipcaif_link_ops __read_mostly = {
> + .kind = "caif",
> + .priv_size = (size_t)sizeof(struct chnl_net),
Unnecessary cast.
> + .setup = ipcaif_net_init,
> + .maxtype = IFLA_CAIF_MAX,
> + .policy = ipcaif_policy,
> + .newlink = ipcaif_newlink,
> + .changelink = ipcaif_changelink,
> + .get_size = ipcaif_get_size,
> + .fill_info = ipcaif_fill_info,
> +
> +};
> +
> +int chnl_net_ioctl(unsigned int cmd, unsigned long arg, bool from_user_land)
> +{
> + struct chnl_net *priv;
> + int result = -1;
> + struct chnl_net *dev;
> + struct net_device *netdevptr;
> + int ret;
> + struct ifreq ifreq;
> + struct ifcaif_param param;
> + rtnl_lock();
> + if (from_user_land) {
> + if (copy_from_user(&ifreq, (const void *)arg, sizeof(ifreq)))
> + return -EFAULT;
> + } else
> + memcpy(&ifreq, (void *)arg, sizeof(ifreq));
Why do you need both an ioctl and a netlink interface?
> +
> + if (cmd == SIOCCAIFNETREMOVE) {
> + pr_debug("CAIF: %s(): %s\n", __func__, ifreq.ifr_name);
> + dev = find_device(ifreq.ifr_name);
> + if (!dev)
> + ret = -ENODEV;
> + else
> + ret = delete_device(dev);
> + rtnl_unlock();
> + return ret;
> + }
> +
> + if (cmd != SIOCCAIFNETNEW) {
> + rtnl_unlock();
> + return -ENOIOCTLCMD;
> + }
> + if (ifreq.ifr_ifru.ifru_data != NULL) {
> + if (from_user_land) {
> + ret = copy_from_user(¶m,
> + ifreq.ifr_ifru.ifru_data,
> + sizeof(param));
> + if (ret) {
> + rtnl_unlock();
> + return -EFAULT;
> + }
> + } else
> + memcpy(¶m,
> + ifreq.ifr_ifru.ifru_data,
> + sizeof(param));
> + ifreq.ifr_ifru.ifru_data = ¶m;
> + }
> +
> + netdevptr = alloc_netdev(sizeof(struct chnl_net),
> + ifreq.ifr_name, ipcaif_net_init);
> + 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;
> +
> + if (param.loop)
> + priv->config.type = CAIF_CHTY_DATAGRAM_LOOP;
> + else
> + priv->config.type = CAIF_CHTY_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;
> +};
> +
> +struct net_device *chnl_net_create(char *name,
> + struct caif_channel_config *config)
> +{
> + struct net_device *dev;
> + ASSERT_RTNL();
> + dev = alloc_netdev(sizeof(struct chnl_net), name, ipcaif_net_init);
> + if (!dev)
> + return NULL;
> + ((struct chnl_net *)netdev_priv(dev))->config = *config;
> + dev_hold(dev);
> + return dev;
> +}
> +EXPORT_SYMBOL(chnl_net_create);
> +
> +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);
You don't need to unregister on error. The ioctl should be unregistered
I guess.
> + return err;
> + }
> + return 0;
> +}
> +
> +static void __exit chnl_exit_module(void)
> +{
> + struct chnl_net *dev = NULL;
> + struct list_head *list_node;
> + struct list_head *_tmp;
> + rtnl_lock();
> + list_for_each_safe(list_node, _tmp, &chnl_net_list) {
> + dev = list_entry(list_node, struct chnl_net, list_field);
> + delete_device(dev);
> + }
> + rtnl_unlock();
> + rtnl_link_unregister(&ipcaif_link_ops);
> + caif_register_ioctl(NULL);
This is racy, rtnl_link_unregister() will clean up all CAIF devices,
but the ioctl handler might register new ones after that. I'd suggest
to drop the ioctl interface completely.
> +}
> +
> +module_init(chnl_init_module);
> +module_exit(chnl_exit_module);
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH net-next-2.6 03/13] net-caif: add CAIF generic protocol stack header files
2010-01-20 22:55 ` [PATCH net-next-2.6 03/13] net-caif: add CAIF generic protocol stack " sjur.brandeland
@ 2010-01-21 8:13 ` Patrick McHardy
2010-01-22 11:02 ` Sjur Brændeland
2010-01-22 9:28 ` Marcel Holtmann
1 sibling, 1 reply; 47+ messages in thread
From: Patrick McHardy @ 2010-01-21 8:13 UTC (permalink / raw)
To: sjur.brandeland; +Cc: netdev, davem, marcel, stefano.babic, randy.dunlap
sjur.brandeland@stericsson.com wrote:
> +++ b/include/net/caif/generic/cfglue.h
> @@ -0,0 +1,110 @@
> +/*
> + * This file contains the OS and HW dependencies for CAIF.
This file looks like it should go away completely.
> +#endif /* CFGLUE_H_ */
> diff --git a/include/net/caif/generic/cflst.h b/include/net/caif/generic/cflst.h
> new file mode 100644
> index 0000000..6ff7168
> --- /dev/null
> +++ b/include/net/caif/generic/cflst.h
> @@ -0,0 +1,19 @@
> +/*
> + * Copyright (C) ST-Ericsson AB 2010
> + * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
> + * License terms: GNU General Public License (GPL) version 2
> + */
> +
> +#ifndef CFLST_H_
> +#define CFLST_H_
> +
> +#include <net/caif/generic/cfglue.h>
> +
> +int cflst_put(struct layer **lst, uint8 id, struct layer *node);
> +struct layer *cflst_get(struct layer **lst, uint8 id);
> +struct layer *cflst_del(struct layer **lst, uint8 id);
> +#define CFLST_FIRST(lst) lst
> +#define CFLST_MORE(node) ((node) != NULL)
> +#define CFLST_NEXT(node) ((node)->next)
> +void cflst_init(struct layer **lst);
> +#endif /* CFLST_H_ */
Why are you not using the list.h functions?
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH net-next-2.6 02/13] net-caif: add CAIF header files
2010-01-20 22:55 ` [PATCH net-next-2.6 02/13] net-caif: add CAIF header files sjur.brandeland
2010-01-20 23:27 ` Randy Dunlap
2010-01-21 7:44 ` Patrick McHardy
@ 2010-01-22 7:51 ` Marcel Holtmann
2010-01-22 8:18 ` Sjur Brændeland
2 siblings, 1 reply; 47+ messages in thread
From: Marcel Holtmann @ 2010-01-22 7:51 UTC (permalink / raw)
To: sjur.brandeland; +Cc: netdev, davem, stefano.babic, randy.dunlap
Hi Sjur,
> +/**
> + * struct sockaddr_caif - the sockaddr structure for CAIF sockets.
> + * @u: Union of address data 'switched' by familty.
> + * @at: Applies when family = CAIFPROTO_AT.
> + * @at.type: Type of AT link to set up (enum caif_at_type).
> + * @util: Applies when family = CAIFPROTO_UTIL
> + * @util.service: Service name.
> + * @dgm: Applies when family = CAIFPROTO_DATAGRAM
> + * @dgm.connection_id: Datagram connection id.
> + * @dgm.nsapi: NSAPI of the PDP-Context.
> + * @rfm: Applies when family = CAIFPROTO_RFM
> + * @rfm.connection_id: Connection ID for RFM.
> + * @rfm.volume: Volume to mount.
> + */
> +struct sockaddr_caif {
> + sa_family_t family;
> + union {
> + struct {
> + u_int8_t type; /* type: enum caif_at_type */
> + } at; /* CAIFPROTO_AT */
> + struct {
> + char service[16];
> + } util; /* CAIFPROTO_UTIL */
> + union {
> + u_int32_t connection_id;
> + u_int8_t nsapi;
> + } dgm; /* CAIFPROTO_DATAGRAM(_LOOP)*/
> + struct {
> + u_int32_t connection_id;
> + char volume[16];
> + } rfm; /* CAIFPROTO_RFM */
> + } u;
> +};
as mentioned on the oFono mailing list, what is the right procedure to
select a local CAIF device for usage with doing bing(). The use case I
am thinking of is that you have multiple CAIF device attached to the
same system. Think of desktops with USB or even Dual-SIM phones. Before
we set the API in stone, we need to have a way o bind the socket to a
specific device. Maybe it is possible, but I am missing it.
Regards
Marcel
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH net-next-2.6 02/13] net-caif: add CAIF header files
2010-01-22 7:51 ` Marcel Holtmann
@ 2010-01-22 8:18 ` Sjur Brændeland
2010-01-22 8:39 ` Marcel Holtmann
0 siblings, 1 reply; 47+ messages in thread
From: Sjur Brændeland @ 2010-01-22 8:18 UTC (permalink / raw)
To: Marcel Holtmann; +Cc: netdev, davem, stefano.babic, randy.dunlap
Hi Marcel.
Marcel Holtmann wrote:
>> +/**
>> + * struct sockaddr_caif - the sockaddr structure for CAIF sockets.
>> + * @u: Union of address data 'switched' by familty.
>> + * @at: Applies when family = CAIFPROTO_AT.
>> + * @at.type: Type of AT link to set up (enum caif_at_type).
>> + * @util: Applies when family = CAIFPROTO_UTIL
>> + * @util.service: Service name.
>> + * @dgm: Applies when family = CAIFPROTO_DATAGRAM
>> + * @dgm.connection_id: Datagram connection id.
>> + * @dgm.nsapi: NSAPI of the PDP-Context.
>> + * @rfm: Applies when family = CAIFPROTO_RFM
>> + * @rfm.connection_id: Connection ID for RFM.
>> + * @rfm.volume: Volume to mount.
>> + */
>> +struct sockaddr_caif {
>> + sa_family_t family;
>> + union {
>> + struct {
>> + u_int8_t type; /* type: enum caif_at_type */
>> + } at; /* CAIFPROTO_AT */
>> + struct {
>> + char service[16];
>> + } util; /* CAIFPROTO_UTIL */
>> + union {
>> + u_int32_t connection_id;
>> + u_int8_t nsapi;
>> + } dgm; /* CAIFPROTO_DATAGRAM(_LOOP)*/
>> + struct {
>> + u_int32_t connection_id;
>> + char volume[16];
>> + } rfm; /* CAIFPROTO_RFM */
>> + } u;
>> +};
>
> as mentioned on the oFono mailing list, what is the right procedure
> to select a local CAIF device for usage with doing bing(). The use
> case I am thinking of is that you have multiple CAIF device attached
> to the same system. Think of desktops with USB or even Dual-SIM
> phones. Before we set the API in stone, we need to have a way o bind
> the socket to a specific device. Maybe it is possible, but I am
> missing it.
The CAIF interface can be selected by using the following types:
[snip caif_config.h]
/**
* enum caif_phy_preference - Types of physical HW interfaces
* towards modem defined in CAIF stack
* @CAIF_PHYPREF_UNSPECIFIED: Default physical interface
* @CAIF_PHYPREF_LOW_LAT: Default physical interface for low-latency
* traffic
* @CAIF_PHYPREF_HIGH_BW: Default physical interface for high-bandwidth
* traffic
* @CAIF_PHYPREF_LOOP: TEST Loopback interface, simulating modem
* responses
*
* For client convenience, two special types are defined:
* CAIF_PHYPREF_LOW_LAT is the preferred low-latency physical link.
* Typically used for "control" purposes.
* CAIF_PHYPREF_HIGH_BW is the preferred high-bandwidth physical link.
* Typically used for "payload" purposes.
*/
...
enum caif_phy_preference {
CAIF_PHYPREF_UNSPECIFIED,
CAIF_PHYPREF_LOW_LAT,
CAIF_PHYPREF_HIGH_BW,
CAIF_PHYPREF_LOOP
};
[snip caif_socket.h]
...
/**
* struct caif_channel_opt - CAIF channel connect options.
* @priority: Priority of the channel (between 0 and 0x1f)
* @link_selector: Selector for the physical link.
* (see enum caif_phy_preference in caif_config.h)
* @link_name: Physical link to use. This is the instance name of the
* CAIF Physical Driver.
*/
struct caif_channel_opt {
u_int16_t priority;
u_int16_t link_selector;
char link_name[16];
};
...
/** enum caif_socket_opts - CAIF option values for getsockopt and setsockopt
* @CAIFSO_CHANNEL: Used to set the connect options on a CAIF
* socket. (struct caif_config_opt). This can only
* be set before connecting.
[end snip]
CAIFSO_CHANNEL is used for specifying the physical interface to use for the
CAIF Channel. You can select the type of interface to use
by setting link_selector:
CAIF_PHYPREF_LOW_LAT will typically be used for AT (or other control traffic),
and CAIF_PHYPREF_HIGH_BW for IP traffic.
When the CAIF interfaces registers itself it will inform about their type,
(low-latency or high-bandwidth). This approach assumes that you have only one
modem, but multiple links to it (e.g. USB and UART).
But you can also specify interface by name using link_name. In this case
you specify the name of the interface to use. I think this would support
your use case with multiple modems attached.
BR/Sjur
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH net-next-2.6 02/13] net-caif: add CAIF header files
2010-01-22 8:18 ` Sjur Brændeland
@ 2010-01-22 8:39 ` Marcel Holtmann
2010-01-22 8:56 ` Sjur Brændeland
0 siblings, 1 reply; 47+ messages in thread
From: Marcel Holtmann @ 2010-01-22 8:39 UTC (permalink / raw)
To: Sjur Brændeland; +Cc: netdev, davem, stefano.babic, randy.dunlap
Hi Sjur,
> >> +/**
> >> + * struct sockaddr_caif - the sockaddr structure for CAIF sockets.
> >> + * @u: Union of address data 'switched' by familty.
> >> + * @at: Applies when family = CAIFPROTO_AT.
> >> + * @at.type: Type of AT link to set up (enum caif_at_type).
> >> + * @util: Applies when family = CAIFPROTO_UTIL
> >> + * @util.service: Service name.
> >> + * @dgm: Applies when family = CAIFPROTO_DATAGRAM
> >> + * @dgm.connection_id: Datagram connection id.
> >> + * @dgm.nsapi: NSAPI of the PDP-Context.
> >> + * @rfm: Applies when family = CAIFPROTO_RFM
> >> + * @rfm.connection_id: Connection ID for RFM.
> >> + * @rfm.volume: Volume to mount.
> >> + */
> >> +struct sockaddr_caif {
> >> + sa_family_t family;
> >> + union {
> >> + struct {
> >> + u_int8_t type; /* type: enum caif_at_type */
> >> + } at; /* CAIFPROTO_AT */
> >> + struct {
> >> + char service[16];
> >> + } util; /* CAIFPROTO_UTIL */
> >> + union {
> >> + u_int32_t connection_id;
> >> + u_int8_t nsapi;
> >> + } dgm; /* CAIFPROTO_DATAGRAM(_LOOP)*/
> >> + struct {
> >> + u_int32_t connection_id;
> >> + char volume[16];
> >> + } rfm; /* CAIFPROTO_RFM */
> >> + } u;
> >> +};
> >
> > as mentioned on the oFono mailing list, what is the right procedure
> > to select a local CAIF device for usage with doing bing(). The use
> > case I am thinking of is that you have multiple CAIF device attached
> > to the same system. Think of desktops with USB or even Dual-SIM
> > phones. Before we set the API in stone, we need to have a way o bind
> > the socket to a specific device. Maybe it is possible, but I am
> > missing it.
>
> The CAIF interface can be selected by using the following types:
> [snip caif_config.h]
> /**
> * enum caif_phy_preference - Types of physical HW interfaces
> * towards modem defined in CAIF stack
> * @CAIF_PHYPREF_UNSPECIFIED: Default physical interface
> * @CAIF_PHYPREF_LOW_LAT: Default physical interface for low-latency
> * traffic
> * @CAIF_PHYPREF_HIGH_BW: Default physical interface for high-bandwidth
> * traffic
> * @CAIF_PHYPREF_LOOP: TEST Loopback interface, simulating modem
> * responses
> *
> * For client convenience, two special types are defined:
> * CAIF_PHYPREF_LOW_LAT is the preferred low-latency physical link.
> * Typically used for "control" purposes.
> * CAIF_PHYPREF_HIGH_BW is the preferred high-bandwidth physical link.
> * Typically used for "payload" purposes.
> */
> ...
> enum caif_phy_preference {
> CAIF_PHYPREF_UNSPECIFIED,
> CAIF_PHYPREF_LOW_LAT,
> CAIF_PHYPREF_HIGH_BW,
> CAIF_PHYPREF_LOOP
> };
> [snip caif_socket.h]
> ...
> /**
> * struct caif_channel_opt - CAIF channel connect options.
> * @priority: Priority of the channel (between 0 and 0x1f)
> * @link_selector: Selector for the physical link.
> * (see enum caif_phy_preference in caif_config.h)
> * @link_name: Physical link to use. This is the instance name of the
> * CAIF Physical Driver.
> */
> struct caif_channel_opt {
> u_int16_t priority;
> u_int16_t link_selector;
> char link_name[16];
> };
> ...
> /** enum caif_socket_opts - CAIF option values for getsockopt and setsockopt
> * @CAIFSO_CHANNEL: Used to set the connect options on a CAIF
> * socket. (struct caif_config_opt). This can only
> * be set before connecting.
> [end snip]
>
> CAIFSO_CHANNEL is used for specifying the physical interface to use for the
> CAIF Channel. You can select the type of interface to use
> by setting link_selector:
> CAIF_PHYPREF_LOW_LAT will typically be used for AT (or other control traffic),
> and CAIF_PHYPREF_HIGH_BW for IP traffic.
> When the CAIF interfaces registers itself it will inform about their type,
> (low-latency or high-bandwidth). This approach assumes that you have only one
> modem, but multiple links to it (e.g. USB and UART).
>
> But you can also specify interface by name using link_name. In this case
> you specify the name of the interface to use. I think this would support
> your use case with multiple modems attached.
sounds good, but why using a socket option and not allowing to just use
bind(). Maybe it is just my personal preference, because I am used to do
it like this for TCP and Bluetooth.
Regards
Marcel
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH net-next-2.6 02/13] net-caif: add CAIF header files
2010-01-22 8:39 ` Marcel Holtmann
@ 2010-01-22 8:56 ` Sjur Brændeland
2010-01-22 9:16 ` Marcel Holtmann
0 siblings, 1 reply; 47+ messages in thread
From: Sjur Brændeland @ 2010-01-22 8:56 UTC (permalink / raw)
To: Marcel Holtmann; +Cc: netdev, davem, stefano.babic, randy.dunlap
Marcel Holtmann wrote:
>> ...
>> CAIFSO_CHANNEL is used for specifying the physical interface to use
>> for the CAIF Channel. You can select the type of interface to use by
>> setting link_selector: CAIF_PHYPREF_LOW_LAT will typically be used
>> for AT (or other control traffic), and CAIF_PHYPREF_HIGH_BW for IP
>> traffic.
>> When the CAIF interfaces registers itself it will inform about their
>> type, (low-latency or high-bandwidth). This approach assumes that you
>> have only one modem, but multiple links to it (e.g. USB and UART).
>>
>> But you can also specify interface by name using link_name. In this
>> case you specify the name of the interface to use. I think this would
>> support your use case with multiple modems attached.
>
> sounds good, but why using a socket option and not allowing to just
> use bind(). Maybe it is just my personal preference, because I am
> used to do it like this for TCP and Bluetooth.
We actually considered this when designing the interface,
but I don't feel bind() fits in this case. bind() would normally bind
an address to the client socket. An interface did not seem
like a client socket address to us, so we decided to use sockopt instead.
Conceptually I don't think CAIF has a client address at all, it only
connects to a server side address.
BR/Sjur
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH net-next-2.6 02/13] net-caif: add CAIF header files
2010-01-22 8:56 ` Sjur Brændeland
@ 2010-01-22 9:16 ` Marcel Holtmann
2010-01-22 9:43 ` Sjur Brændeland
0 siblings, 1 reply; 47+ messages in thread
From: Marcel Holtmann @ 2010-01-22 9:16 UTC (permalink / raw)
To: Sjur Brændeland; +Cc: netdev, davem, stefano.babic, randy.dunlap
Hi Sjur,
> >> CAIFSO_CHANNEL is used for specifying the physical interface to use
> >> for the CAIF Channel. You can select the type of interface to use by
> >> setting link_selector: CAIF_PHYPREF_LOW_LAT will typically be used
> >> for AT (or other control traffic), and CAIF_PHYPREF_HIGH_BW for IP
> >> traffic.
> >> When the CAIF interfaces registers itself it will inform about their
> >> type, (low-latency or high-bandwidth). This approach assumes that you
> >> have only one modem, but multiple links to it (e.g. USB and UART).
> >>
> >> But you can also specify interface by name using link_name. In this
> >> case you specify the name of the interface to use. I think this would
> >> support your use case with multiple modems attached.
> >
> > sounds good, but why using a socket option and not allowing to just
> > use bind(). Maybe it is just my personal preference, because I am
> > used to do it like this for TCP and Bluetooth.
>
> We actually considered this when designing the interface,
> but I don't feel bind() fits in this case. bind() would normally bind
> an address to the client socket. An interface did not seem
> like a client socket address to us, so we decided to use sockopt instead.
> Conceptually I don't think CAIF has a client address at all, it only
> connects to a server side address.
seems fair enough to me. My personal preference would just be bind, but
I can follow your argumentation. I just wanted to make sure that we have
that option before setting CAIF socket address in stone.
Any reason why not just supporting SO_PRIORITY and SO_BINDTODEVICE on
CAIF sockets then?
Regards
Marcel
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH net-next-2.6 13/13] net-caif-driver: add CAIF serial driver (ldisc)
2010-01-20 22:55 ` [PATCH net-next-2.6 13/13] net-caif-driver: add CAIF serial driver (ldisc) sjur.brandeland
2010-01-20 23:36 ` Randy Dunlap
@ 2010-01-22 9:21 ` Marcel Holtmann
2010-01-22 9:56 ` Sjur Brændeland
1 sibling, 1 reply; 47+ messages in thread
From: Marcel Holtmann @ 2010-01-22 9:21 UTC (permalink / raw)
To: sjur.brandeland; +Cc: netdev, davem, stefano.babic, randy.dunlap
Hi Sjur,
> Add CAIF Serial driver. This driver is implemented as a line discipline.
> The TTY is opened from inside the kernel module.
>
> caif_serial uses the following module parameters:
> ser_ttyname - specifies the tty name.
> ser_use_stx - specifies if STart of frame eXtension is in use.
> ser_loop - sets the interface in loopback mode.
I think opening the TTY from within the kernel is the wrong approach. It
basically takes all the control away from the system people trying to
bring up the device. And for every special TTY you need to add hacks and
workarounds.
You should be just providing the TTY line discipline for CAIF. And then
have a program like caifattach that open the TTY and sets it. We do this
for Bluetooth with hciattach for IrDA with irattach etc.
Regards
Marcel
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH net-next-2.6 03/13] net-caif: add CAIF generic protocol stack header files
2010-01-20 22:55 ` [PATCH net-next-2.6 03/13] net-caif: add CAIF generic protocol stack " sjur.brandeland
2010-01-21 8:13 ` Patrick McHardy
@ 2010-01-22 9:28 ` Marcel Holtmann
2010-01-22 10:01 ` Sjur Brændeland
1 sibling, 1 reply; 47+ messages in thread
From: Marcel Holtmann @ 2010-01-22 9:28 UTC (permalink / raw)
To: sjur.brandeland; +Cc: netdev, davem, stefano.babic, randy.dunlap
Hi Sjur,
> Add include files for the generic CAIF protocol stack. This layer
> is somewhat generic in order to be able to use and test it outside
> the Linux Kernel.
>
> caif_layer.h - Defines the structure of the CAIF protocol layers
> cfcnfg.h - CAIF Configuration Module for services and link layers
> cfctrl.h - CAIF Control Protocol Layer
> cffrml.h - CAIF Framing Layer
> cfglue.h - CAIF Glue Layer for allocation, logging etc
> cflist.h - CAIF List implementation
> cfmuxl.h - CAIF Muxing Layer
> cfpkt.h - CAIF Packet layer (skb helper functions)
> cfserl.h - CAIF Serial Layer
> cfsrvl.h - CAIF Service Layer
>
> Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
> ---
> include/net/caif/generic/caif_layer.h | 225 +++++++++++++++++++++++++++
> include/net/caif/generic/cfcnfg.h | 110 +++++++++++++
> include/net/caif/generic/cfctrl.h | 136 ++++++++++++++++
> include/net/caif/generic/cffrml.h | 19 +++
> include/net/caif/generic/cfglue.h | 110 +++++++++++++
> include/net/caif/generic/cflst.h | 19 +++
> include/net/caif/generic/cfmuxl.h | 22 +++
> include/net/caif/generic/cfpkt.h | 274 +++++++++++++++++++++++++++++++++
> include/net/caif/generic/cfserl.h | 14 ++
> include/net/caif/generic/cfsrvl.h | 33 ++++
> 10 files changed, 962 insertions(+), 0 deletions(-)
is it really needed to keep the "generic" piece in the path here. I
would prefer if we get rid of it.
Regards
Marcel
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH net-next-2.6 11/13] net-caif: add CAIF Kconfig and Makefiles
2010-01-20 22:55 ` [PATCH net-next-2.6 11/13] net-caif: add CAIF Kconfig and Makefiles sjur.brandeland
@ 2010-01-22 9:40 ` Marcel Holtmann
0 siblings, 0 replies; 47+ messages in thread
From: Marcel Holtmann @ 2010-01-22 9:40 UTC (permalink / raw)
To: sjur.brandeland; +Cc: netdev, davem, stefano.babic, randy.dunlap
Hi Sjur,
> +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"
> + default CAIF
> + ---help---
> + Say Y if you will be using a CAIF based GPRS network device.
> + This can be either built-in or a loadable module,
> + If you select to build it as a built-in then the main CAIF device must
> + also be a built-in.
> + If unsure say Y.
any real use cases where NOT building CAIF_SOCK would make sense. Maybe
it would be better to move CAIF_SOCK and CAIF_NETDEV under EMBEDDED.
Regards
Marcel
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH net-next-2.6 00/13] net-caif: introducing CAIF protocol stack
2010-01-20 22:55 [PATCH net-next-2.6 00/13] net-caif: introducing CAIF protocol stack sjur.brandeland
` (11 preceding siblings ...)
2010-01-20 22:55 ` [PATCH net-next-2.6 13/13] net-caif-driver: add CAIF serial driver (ldisc) sjur.brandeland
@ 2010-01-22 9:43 ` Marcel Holtmann
2010-01-22 10:11 ` Sjur Brændeland
[not found] ` <1264028130-14364-13-git-send-email-sjur.brandeland@stericsson.com>
13 siblings, 1 reply; 47+ messages in thread
From: Marcel Holtmann @ 2010-01-22 9:43 UTC (permalink / raw)
To: sjur.brandeland; +Cc: netdev, davem, stefano.babic, randy.dunlap
Hi Sjur,
> This patch-set introduces the CAIF protocol Stack.
>
> 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.
>
> * CAIF provides a socket interface which can be used to open virtual AT
> channels, create and configure GPRS Data connections.
>
> * A CAIF Network device is also provided for GPRS access.
> This interface can be managed with socket IOCTLs or RTNL.
>
> * A kernel interface is provided allowing Kernel modules to use CAIF.
>
> * A serial link layer layer implemented as a line discipline is currently
> implemented. Other link interfaces may be contributed at a later stage.
any details on potential USB, SPI or SDIO drivers?
Regards
Marcel
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH net-next-2.6 02/13] net-caif: add CAIF header files
2010-01-22 9:16 ` Marcel Holtmann
@ 2010-01-22 9:43 ` Sjur Brændeland
0 siblings, 0 replies; 47+ messages in thread
From: Sjur Brændeland @ 2010-01-22 9:43 UTC (permalink / raw)
To: Marcel Holtmann; +Cc: netdev, davem, stefano.babic, randy.dunlap
Marcel Holtmann wrote:
> Hi Sjur,
>
>>>> CAIFSO_CHANNEL is used for specifying the physical interface to use
>>>> for the CAIF Channel. You can select the type of interface to use
>>>> by setting link_selector: CAIF_PHYPREF_LOW_LAT will typically be
>>>> used for AT (or other control traffic), and CAIF_PHYPREF_HIGH_BW
>>>> for IP traffic.
>>>> When the CAIF interfaces registers itself it will inform about
>>>> their type, (low-latency or high-bandwidth). This approach assumes
>>>> that you have only one modem, but multiple links to it (e.g. USB
>>>> and UART).
>>>>
>>>> But you can also specify interface by name using link_name. In this
>>>> case you specify the name of the interface to use. I think this
>>>> would support your use case with multiple modems attached.
>>>
>>> sounds good, but why using a socket option and not allowing to just
>>> use bind(). Maybe it is just my personal preference, because I am
>>> used to do it like this for TCP and Bluetooth.
>>
>> We actually considered this when designing the interface, but I don't
>> feel bind() fits in this case. bind() would normally bind an address
>> to the client socket. An interface did not seem like a client socket
>> address to us, so we decided to use sockopt instead.
>> Conceptually I don't think CAIF has a client address at all, it only
>> connects to a server side address.
>
> seems fair enough to me. My personal preference would just be bind,
> but I can follow your argumentation. I just wanted to make sure that
> we have that option before setting CAIF socket address in stone.
>
> Any reason why not just supporting SO_PRIORITY and SO_BINDTODEVICE on
> CAIF sockets then?
Sounds like a good idea, I'll definitely look into that, thanks.
BR/Sjur
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH net-next-2.6 13/13] net-caif-driver: add CAIF serial driver (ldisc)
2010-01-22 9:21 ` Marcel Holtmann
@ 2010-01-22 9:56 ` Sjur Brændeland
2010-01-22 10:07 ` Marcel Holtmann
0 siblings, 1 reply; 47+ messages in thread
From: Sjur Brændeland @ 2010-01-22 9:56 UTC (permalink / raw)
To: Marcel Holtmann; +Cc: netdev, davem, stefano.babic, randy.dunlap
Marcel Holtmann wrote:
> Hi Sjur,
>
>> Add CAIF Serial driver. This driver is implemented as a line
>> discipline. The TTY is opened from inside the kernel module.
>>
>> caif_serial uses the following module parameters:
>> ser_ttyname - specifies the tty name.
>> ser_use_stx - specifies if STart of frame eXtension is in use.
>> ser_loop - sets the interface in loopback mode.
>
> I think opening the TTY from within the kernel is the wrong approach.
> It basically takes all the control away from the system people trying
> to bring up the device. And for every special TTY you need to add
> hacks and workarounds.
>
> You should be just providing the TTY line discipline for CAIF. And
> then have a program like caifattach that open the TTY and sets it. We
> do this for Bluetooth with hciattach for IrDA with irattach etc.
I see your point, actually we did this in a while back,
but changed it to open the tty from kernel side.
I think this is a trade off between flexibility and making it simple to
use from user space. Another problem with the current approach miss the
flexibility of configuring the UART and the possibility to have multiple
serial links...
BR/Sjur
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH net-next-2.6 03/13] net-caif: add CAIF generic protocol stack header files
2010-01-22 9:28 ` Marcel Holtmann
@ 2010-01-22 10:01 ` Sjur Brændeland
2010-01-22 10:12 ` Marcel Holtmann
0 siblings, 1 reply; 47+ messages in thread
From: Sjur Brændeland @ 2010-01-22 10:01 UTC (permalink / raw)
To: Marcel Holtmann; +Cc: netdev, davem, stefano.babic, randy.dunlap
Hi Marcel.
Marcel Holtmann wrote:
>> Add include files for the generic CAIF protocol stack. This layer is
>> somewhat generic in order to be able to use and test it outside the
>> Linux Kernel.
>>
>> caif_layer.h - Defines the structure of the CAIF protocol layers
>> cfcnfg.h - CAIF Configuration Module for services and link layers
>> cfctrl.h - CAIF Control Protocol Layer
>> cffrml.h - CAIF Framing Layer
>> cfglue.h - CAIF Glue Layer for allocation, logging etc
>> cflist.h - CAIF List implementation
>> cfmuxl.h - CAIF Muxing Layer
>> cfpkt.h - CAIF Packet layer (skb helper functions)
>> cfserl.h - CAIF Serial Layer
>> cfsrvl.h - CAIF Service Layer
>
> is it really needed to keep the "generic" piece in the path here. I
> would prefer if we get rid of it.
Are you suggesting to move this files to include/net/caif?
I can do this in the next patch set.
The reason for the term "generic" is that this that the core part of the CAIF
stack originally was designed to be OS independent.
BR/Sjur
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH net-next-2.6 13/13] net-caif-driver: add CAIF serial driver (ldisc)
2010-01-22 9:56 ` Sjur Brændeland
@ 2010-01-22 10:07 ` Marcel Holtmann
0 siblings, 0 replies; 47+ messages in thread
From: Marcel Holtmann @ 2010-01-22 10:07 UTC (permalink / raw)
To: Sjur Brændeland; +Cc: netdev, davem, stefano.babic, randy.dunlap
Hi Sjur,
> >> Add CAIF Serial driver. This driver is implemented as a line
> >> discipline. The TTY is opened from inside the kernel module.
> >>
> >> caif_serial uses the following module parameters:
> >> ser_ttyname - specifies the tty name.
> >> ser_use_stx - specifies if STart of frame eXtension is in use.
> >> ser_loop - sets the interface in loopback mode.
> >
> > I think opening the TTY from within the kernel is the wrong approach.
> > It basically takes all the control away from the system people trying
> > to bring up the device. And for every special TTY you need to add
> > hacks and workarounds.
> >
> > You should be just providing the TTY line discipline for CAIF. And
> > then have a program like caifattach that open the TTY and sets it. We
> > do this for Bluetooth with hciattach for IrDA with irattach etc.
>
> I see your point, actually we did this in a while back,
> but changed it to open the tty from kernel side.
> I think this is a trade off between flexibility and making it simple to
> use from user space. Another problem with the current approach miss the
> flexibility of configuring the UART and the possibility to have multiple
> serial links...
that is why am I proposing just providing the line discipline and an
extra caifattach program.
With that we can add some plugin to oFono that just does the same as
caifattach would do and get a really smooth integration for embedded
platform, but on the other hand enough flexibility for everything else.
For Bluetooth and IrDA this approach has been working nicely for years
now.
Regards
Marcel
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH net-next-2.6 00/13] net-caif: introducing CAIF protocol stack
2010-01-22 9:43 ` [PATCH net-next-2.6 00/13] net-caif: introducing CAIF protocol stack Marcel Holtmann
@ 2010-01-22 10:11 ` Sjur Brændeland
2010-01-22 10:19 ` Marcel Holtmann
0 siblings, 1 reply; 47+ messages in thread
From: Sjur Brændeland @ 2010-01-22 10:11 UTC (permalink / raw)
To: Marcel Holtmann; +Cc: netdev, davem, stefano.babic, randy.dunlap
Marcel Holtmann wrote:
> Hi Sjur,
>
>> This patch-set introduces the CAIF protocol Stack.
>>
>> 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.
>>
>> * CAIF provides a socket interface which can be used to open virtual
>> AT channels, create and configure GPRS Data connections.
>>
>> * A CAIF Network device is also provided for GPRS access.
>> This interface can be managed with socket IOCTLs or RTNL.
>>
>> * A kernel interface is provided allowing Kernel modules to use CAIF.
>>
>> * A serial link layer layer implemented as a line discipline is
>> currently implemented. Other link interfaces may be contributed at
>> a later stage.
>
> any details on potential USB, SPI or SDIO drivers?
We have a SPI driver that we could contribute,
but I think it might need some more work before submitting it.
Also I think it would be good to have this patch set accepted before we
start on SPI.
BR/Sjur
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH net-next-2.6 03/13] net-caif: add CAIF generic protocol stack header files
2010-01-22 10:01 ` Sjur Brændeland
@ 2010-01-22 10:12 ` Marcel Holtmann
2010-01-22 10:16 ` Sjur Brændeland
0 siblings, 1 reply; 47+ messages in thread
From: Marcel Holtmann @ 2010-01-22 10:12 UTC (permalink / raw)
To: Sjur Brændeland; +Cc: netdev, davem, stefano.babic, randy.dunlap
Hi Sjur,
> >> Add include files for the generic CAIF protocol stack. This layer is
> >> somewhat generic in order to be able to use and test it outside the
> >> Linux Kernel.
> >>
> >> caif_layer.h - Defines the structure of the CAIF protocol layers
> >> cfcnfg.h - CAIF Configuration Module for services and link layers
> >> cfctrl.h - CAIF Control Protocol Layer
> >> cffrml.h - CAIF Framing Layer
> >> cfglue.h - CAIF Glue Layer for allocation, logging etc
> >> cflist.h - CAIF List implementation
> >> cfmuxl.h - CAIF Muxing Layer
> >> cfpkt.h - CAIF Packet layer (skb helper functions)
> >> cfserl.h - CAIF Serial Layer
> >> cfsrvl.h - CAIF Service Layer
> >
> > is it really needed to keep the "generic" piece in the path here. I
> > would prefer if we get rid of it.
>
> Are you suggesting to move this files to include/net/caif?
> I can do this in the next patch set.
> The reason for the term "generic" is that this that the core part of the CAIF
> stack originally was designed to be OS independent.
I understand where you are coming from, but for the Linux implementation
it doesn't really sound like a good idea. Especially with the move to a
socket based implementation you really diverge here already.
Also the cfglue.[ch] pieces are really controversial. I would prefer not
to have OS glue code here. Just use native lists, locks etc. It makes
the code a lot easier to review for all the Linux people ;)
Regards
Marcel
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH net-next-2.6 03/13] net-caif: add CAIF generic protocol stack header files
2010-01-22 10:12 ` Marcel Holtmann
@ 2010-01-22 10:16 ` Sjur Brændeland
2010-01-22 10:24 ` Marcel Holtmann
0 siblings, 1 reply; 47+ messages in thread
From: Sjur Brændeland @ 2010-01-22 10:16 UTC (permalink / raw)
To: Marcel Holtmann; +Cc: netdev, davem, stefano.babic, randy.dunlap
Hi Marcel.
Marcel Holtmann wrote:
> Hi Sjur,
>
>>>> Add include files for the generic CAIF protocol stack. This layer
>>>> is somewhat generic in order to be able to use and test it outside
>>>> the Linux Kernel.
>>>>
>>>> caif_layer.h - Defines the structure of the CAIF protocol layers
>>>> cfcnfg.h - CAIF Configuration Module for services and link
>>>> layers cfctrl.h - CAIF Control Protocol Layer
>>>> cffrml.h - CAIF Framing Layer
>>>> cfglue.h - CAIF Glue Layer for allocation, logging etc
>>>> cflist.h - CAIF List implementation
>>>> cfmuxl.h - CAIF Muxing Layer
>>>> cfpkt.h - CAIF Packet layer (skb helper functions)
>>>> cfserl.h - CAIF Serial Layer
>>>> cfsrvl.h - CAIF Service Layer
>>>
>>> is it really needed to keep the "generic" piece in the path here. I
>>> would prefer if we get rid of it.
>>
>> Are you suggesting to move this files to include/net/caif?
>> I can do this in the next patch set.
>> The reason for the term "generic" is that this that the core part of
>> the CAIF stack originally was designed to be OS independent.
>
> I understand where you are coming from, but for the Linux
> implementation it doesn't really sound like a good idea. Especially
> with the move to a socket based implementation you really diverge
> here already.
>
> Also the cfglue.[ch] pieces are really controversial. I would prefer
> not to have OS glue code here. Just use native lists, locks etc. It
> makes the code a lot easier to review for all the Linux people ;)
>
Yes, I was kind of expecting this feedback. The problem is that I have
to ditch more than 50 unit tests that I have in user space. They are
very handy when debugging protocol problems.
BR/Sjur
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH net-next-2.6 00/13] net-caif: introducing CAIF protocol stack
2010-01-22 10:11 ` Sjur Brændeland
@ 2010-01-22 10:19 ` Marcel Holtmann
0 siblings, 0 replies; 47+ messages in thread
From: Marcel Holtmann @ 2010-01-22 10:19 UTC (permalink / raw)
To: Sjur Brændeland; +Cc: netdev, davem, stefano.babic, randy.dunlap
Hi Sjur,
> >> This patch-set introduces the CAIF protocol Stack.
> >>
> >> 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.
> >>
> >> * CAIF provides a socket interface which can be used to open virtual
> >> AT channels, create and configure GPRS Data connections.
> >>
> >> * A CAIF Network device is also provided for GPRS access.
> >> This interface can be managed with socket IOCTLs or RTNL.
> >>
> >> * A kernel interface is provided allowing Kernel modules to use CAIF.
> >>
> >> * A serial link layer layer implemented as a line discipline is
> >> currently implemented. Other link interfaces may be contributed at
> >> a later stage.
> >
> > any details on potential USB, SPI or SDIO drivers?
>
> We have a SPI driver that we could contribute,
> but I think it might need some more work before submitting it.
> Also I think it would be good to have this patch set accepted before we
> start on SPI.
personally I would like to see the SPI driver. So that we get a feeling
on how the low-level hardware abstraction has been done. And what are
the difference between the busses.
Also it doesn't have to be part of this patch set. Just send it as RFC
for review/reference. Submit early is always a good idea.
Regards
Marcel
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH net-next-2.6 03/13] net-caif: add CAIF generic protocol stack header files
2010-01-22 10:16 ` Sjur Brændeland
@ 2010-01-22 10:24 ` Marcel Holtmann
0 siblings, 0 replies; 47+ messages in thread
From: Marcel Holtmann @ 2010-01-22 10:24 UTC (permalink / raw)
To: Sjur Brændeland; +Cc: netdev, davem, stefano.babic, randy.dunlap
Hi Sjur,
> >>>> Add include files for the generic CAIF protocol stack. This layer
> >>>> is somewhat generic in order to be able to use and test it outside
> >>>> the Linux Kernel.
> >>>>
> >>>> caif_layer.h - Defines the structure of the CAIF protocol layers
> >>>> cfcnfg.h - CAIF Configuration Module for services and link
> >>>> layers cfctrl.h - CAIF Control Protocol Layer
> >>>> cffrml.h - CAIF Framing Layer
> >>>> cfglue.h - CAIF Glue Layer for allocation, logging etc
> >>>> cflist.h - CAIF List implementation
> >>>> cfmuxl.h - CAIF Muxing Layer
> >>>> cfpkt.h - CAIF Packet layer (skb helper functions)
> >>>> cfserl.h - CAIF Serial Layer
> >>>> cfsrvl.h - CAIF Service Layer
> >>>
> >>> is it really needed to keep the "generic" piece in the path here. I
> >>> would prefer if we get rid of it.
> >>
> >> Are you suggesting to move this files to include/net/caif?
> >> I can do this in the next patch set.
> >> The reason for the term "generic" is that this that the core part of
> >> the CAIF stack originally was designed to be OS independent.
> >
> > I understand where you are coming from, but for the Linux
> > implementation it doesn't really sound like a good idea. Especially
> > with the move to a socket based implementation you really diverge
> > here already.
> >
> > Also the cfglue.[ch] pieces are really controversial. I would prefer
> > not to have OS glue code here. Just use native lists, locks etc. It
> > makes the code a lot easier to review for all the Linux people ;)
> >
>
> Yes, I was kind of expecting this feedback. The problem is that I have
> to ditch more than 50 unit tests that I have in user space. They are
> very handy when debugging protocol problems.
what about having a script for that. Something that takes the "clean"
kernel code. Does some sed etc. magic and still lets you run your unit
tests in userspace.
Or what a about a kernel module that does these unit tests in kernel
space. I really don't know how feasible this is, but think about it.
Regards
Marcel
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH net-next-2.6 02/13] net-caif: add CAIF header files
2010-01-21 7:44 ` Patrick McHardy
@ 2010-01-22 10:53 ` Sjur Brændeland
0 siblings, 0 replies; 47+ messages in thread
From: Sjur Brændeland @ 2010-01-22 10:53 UTC (permalink / raw)
To: Patrick McHardy; +Cc: netdev, davem, marcel, stefano.babic, randy.dunlap
Hi Patrick.
Patrick McHardy wrote:
> sjur.brandeland@stericsson.com wrote:
>> +/**
>> + * enum ifla_caif
>> + * When using RT Netlink to create, destroy or configure a CAIF IP
>> +interface, + * enum ifla_caif is used to specify the configuration
>> attributes. + * + * @IFLA_CAIF_IPV4_CONNID: Connection ID for IPv4
>> PDP Context. + * The type of attribute is NLA_U32.
>> + * @IFLA_CAIF_IPV6_CONNID: Connection ID for IPv6 PDP Context.
>> + * The type of attribute is NLA_U32.
>> + * @IFLA_CAIF_LOOPBACK: If different from zero, device is doing
>> loopback + * The type of attribute is NLA_U8.
>> + */
>> +enum ifla_caif {
>> + IFLA_CAIF_IPV4_CONNID,
>> + IFLA_CAIF_IPV6_CONNID,
>> + IFLA_CAIF_LOOPBACK,
>> + __IFLA_CAIF_MAX
>> +};
>> +#define IFLA_CAIF_MAX (__IFLA_CAIF_MAX-1)
>> +
>> +#endif /*IF_CAIF_H_*/
>
> You should add an IFLA_CAIF_UNSPEC at the beginning, some of the
> nlattr helpers skip attributes with a value of zero.
OK, thanks. I'll fix this in the next patch set.
BR/Sjur
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH net-next-2.6 03/13] net-caif: add CAIF generic protocol stack header files
2010-01-21 8:13 ` Patrick McHardy
@ 2010-01-22 11:02 ` Sjur Brændeland
0 siblings, 0 replies; 47+ messages in thread
From: Sjur Brændeland @ 2010-01-22 11:02 UTC (permalink / raw)
To: Patrick McHardy; +Cc: netdev, davem, marcel, stefano.babic, randy.dunlap
Hi Patrick.
Patrick McHardy wrote:
> sjur.brandeland@stericsson.com wrote:
>> +++ b/include/net/caif/generic/cfglue.h
...
> This file looks like it should go away completely.
...
> Why are you not using the list.h functions?
I kind of expected this feedback.
The reason is that CAIF originally was written as a OS independent stack,
with a thin glue layer for OS specific things.
I am aware that this is not the preferred way of doing it in the Linux kernel.
As mentioned to Marcel Holtmann, my problem is that I have more than 50 unit
tests in User Space that I might have to ditch if I remove cfglue.h and list.h.
But I'll probably do as you suggest in the next patch set...
BR/Sjur
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH net-next-2.6 02/13] net-caif: add CAIF header files
2010-01-20 23:27 ` Randy Dunlap
@ 2010-01-22 11:05 ` Sjur Brændeland
0 siblings, 0 replies; 47+ messages in thread
From: Sjur Brændeland @ 2010-01-22 11:05 UTC (permalink / raw)
To: Randy Dunlap; +Cc: netdev, davem, marcel, stefano.babic
Hi Randy.
Randy Dunlap wrote:
> Just a couple of nits here (below):
>> +++ b/include/linux/caif/caif_config.h
> ...
>> +struct caif_channel_config {
>> + char name[16];
>> + enum caif_channel_type type;
>> + unsigned priority;
>> + enum caif_phy_preference phy_pref;
>> + char phy_name[16];
>> +
>> + /** Union of channel type-specific configuration parameters.
>
> Don't use /** here.
OK,
>
>> +
>> +/**
>> + * struct ifcaif_param - Parameters for creating CAIF Network
>> Interface. + * + * When using SIOCCAIFNETNEW to create a CAIF IP
>> interface, this +structure + * is used for configuration data.
>> + * The attribute ifr_ifru.ifru_data in struct struct ifreq must be
>> +set + * point at an instance of struct ifcaif_param.
>> + *
>> + * @ipv4_connid: Connection ID for IPv4 PDP Context.
>> + * @ipv6_connid: Connection ID for IPv6 PDP Context.
>> + * @loop: If different from zero, device is doing loopback + */
>> +struct ifcaif_param {
>> + __u32 ipv4_connid;
>> + __u32 ipv6_connid;
>> + __u8 loop;
>> +};
>> +
>> +/**
>> + * enum ifla_caif
>
> Above line needs " - <short description>" on it.
Sure, I'll add that - thanks.
>
BR/Sjur
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH net-next-2.6 13/13] net-caif-driver: add CAIF serial driver (ldisc)
2010-01-20 23:36 ` Randy Dunlap
@ 2010-01-22 11:07 ` Sjur Brændeland
0 siblings, 0 replies; 47+ messages in thread
From: Sjur Brændeland @ 2010-01-22 11:07 UTC (permalink / raw)
To: Randy Dunlap; +Cc: netdev, davem, marcel, stefano.babic
Hi Randy.
Randy Dunlap wrote:
>> +++ b/drivers/net/caif/caif_serial.c
>> +struct net_device *device;
>> +char *ser_ttyname = "/dev/ttyS0";
>> +module_param(ser_ttyname, charp, S_IRUGO);
>> +MODULE_PARM_DESC(ser_ttyname, "TTY to open.");
>> +
>
> Can't a lot of these data items be static? (above & below)
Yes, you're right. Thank you.
....
>> +static int start_ldisc(struct ser_device *ser) {
>> + struct file *f;
>> + mm_segment_t oldfs;
>> + struct termios tio;
>> + int ldiscnr = N_CAIF;
>> + int ret;
>> + f = filp_open(ser->tty_name, 0, 0);
>> + if (IS_ERR(f)) {
>> + dev_err(&ser->dev->dev, "CAIF cannot open:%s\n", ser->tty_name);
>> + ret = -EINVAL; + goto error;
>> + }
>> + if (f == NULL || f->f_op == NULL || f->f_op->unlocked_ioctl ==
>> NULL) { + dev_err(&ser->dev->dev, "TTY cannot do IOCTL:%s\n",
>> + ser->tty_name); + ret = -EINVAL;
>
> I'm thinking that the errcode should be (negative) ENOTTY...
Yes, agree - I'll fix this.
BR/Sjur
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH net-next-2.6 10/13] net-caif: add kernel-client API for CAIF
2010-01-20 22:55 ` [PATCH net-next-2.6 10/13] net-caif: add kernel-client API for CAIF sjur.brandeland
@ 2010-01-26 17:50 ` Randy Dunlap
2010-01-26 19:56 ` Sjur Brændeland
0 siblings, 1 reply; 47+ messages in thread
From: Randy Dunlap @ 2010-01-26 17:50 UTC (permalink / raw)
To: sjur.brandeland; +Cc: netdev, davem, marcel, stefano.babic
On Wed, 20 Jan 2010 23:55:27 +0100 sjur.brandeland@stericsson.com wrote:
> From: Sjur Braendeland <sjur.brandeland@stericsson.com>
>
> This patch provides a CAIF API for managing CAIF devices from
> kernel modules.
>
> Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
> ---
> include/net/caif/caif_kernel.h | 309 ++++++++++++++++++++++++++++++++++++++++
> net/caif/caif_chnlif.c | 177 +++++++++++++++++++++++
> 2 files changed, 486 insertions(+), 0 deletions(-)
>
> diff --git a/include/net/caif/caif_kernel.h b/include/net/caif/caif_kernel.h
> new file mode 100644
> index 0000000..6617a8f
> --- /dev/null
> +++ b/include/net/caif/caif_kernel.h
> @@ -0,0 +1,309 @@
> +/*
> + * CAIF Kernel Internal interface for configuring and accessing
> + * CAIF Channels.
> + * Copyright (C) ST-Ericsson AB 2010
> + * Author: Sjur Brendeland/ sjur.brandeland@stericsson.com
> + * License terms: GNU General Public License (GPL) version 2
> + */
> +
> +#ifndef CAIF_KERNEL_H_
> +#define CAIF_KERNEL_H_
> +#include <linux/caif/caif_config.h>
> +struct sk_buff;
> +
Hi,
I'm probably repeating myself here, but this function documentation
is not formatted in the way which the Linux kernel expects/wants.
Some of the others (after this one) use "/**" to begin the comment block.
In kernel-land, that is used only for kernel-doc formatted notation.
> +/*!\page caif_kernel.h
> + * This is the specification of the CAIF kernel internal interface to
> + * CAIF Channels.
> + * This interface follows the pattern used in Linux device drivers with a
> + * struct \ref caif_device
> + * holding control data handling each device instance.
> + *
> + * The functional interface consists of a few basic functions:
> + * - \ref caif_add_device Configure and connect the CAIF
> + * channel to the remote end. Configuration is described in
> + * \ref caif_channel_config.
> + * - \ref caif_remove_device Disconnect and remove the channel.
> + * - \ref caif_transmit Sends a CAIF message on the link.
> + * - \ref caif_device.receive_cb Receive callback function for
> + * receiving packets.
> + * - \ref caif_device.control_cb Control information from the CAIF stack.
> + * - \ref caif_flow_control Send flow control message to remote end.
> + *
> + *
> + * Details:
> + * \see { caif_kernel }
> + *
> + * \code
> + *
> +#include <net/caif/caif_kernel.h>"
> +
> + static void my_receive(struct caif_device *dev, struct sk_buff *skb)
> + {
> + ...
> + }
> +
> + static void my_control(struct caif_device *dev, enum caif_control ctrl)
> + {
> + ....
> + }
> +
> + int kernel_caif_usage_example()
> + {
> + struct sk_buff *skb;
> + char *message = "hello";
> +
> + // Connect the channel
> + struct caif_device caif_dev = {
> + .caif_config = {
> + .name = "MYDEV",
> + .priority = CAIF_PRIO_NORMAL,
> + .type = CAIF_CHTY_UTILITY,
> + .phy_pref = CAIF_PHYPREF_LOW_LAT,
> + .u.utility.name = "CAIF_PSOCK_TEST",
> + .u.utility.params = {0x01},
> + .u.utility.paramlen = 1,
> + },
> +
> + .receive_cb = my_receive,
> + .control_cb = my_control,
> +
> + };
> + ret = caif_add_device(&caif_dev);
> + if (ret)
> + goto error;
> +
> + // Send a packet
> + skb = caif_create_skb(message, strlen(message));
> + ret = caif_transmit(&caif_dev, skb);
> + if (ret)
> + goto error;
> +
> + // Remove device
> + ret = caif_remove_device(&caif_dev);
> + if (ret)
> + goto error;
> +
> +}
> +
> +* \endcode
> +*
> +* \section Linux Socket Buffer (SKB)
> + * When sending out packets on a connection (\ref caif_transmit)
> + * the CAIF stack will add CAIF protocol headers.
> + * This requires space in the SKB.
> + * CAIF has defined \ref CAIF_SKB_HEAD_RESERVE for minimum
> + * required reserved head-space in the packet and
> + * \ref CAIF_SKB_TAIL_RESERVE for minimum reserved tail-space.
> + *
> + * \b NOTE The Linux kernel SKB operations panic if not
> + * enough space is available!
> + *
> + */
> +
> + /*! \addtogroup caif_kernel
> + * @{
> + */
> +
> +struct caif_device;
> +
> + /** Minimum required CAIF socket buffer head-space */
> +#define CAIF_SKB_HEAD_RESERVE 32
> +
> + /** Minimum required CAIF socket buffer tail-space */
> +#define CAIF_SKB_TAIL_RESERVE 32
> +
> + /** CAIF control information (used in \ref caif_device.control_cb)
> + * used for receiving control information from the modem.
> + */
> +enum caif_control {
> + /** Modem has sent Flow-ON, Clients can start transmitting
> + * data using \ref caif_transmit.
> + */
> + CAIF_CONTROL_FLOW_ON = 0,
> + /** Modem has sent Flow-OFF, Clients must stop transmitting
> + * data using \ref caif_transmit.
> + */
> + CAIF_CONTROL_FLOW_OFF = 1,
> +
> + /** Channel creation is complete. This is an acknowledgement to
> + * \ref caif_add_device from the modem.
> + * The channel is ready for transmit (Flow-state is ON).
> + */
> + CAIF_CONTROL_DEV_INIT = 3,
> +
> + /** Spontaneous close request from the modem, only applicable
> + * for utility link. The client should respond by calling
> + * \ref caif_remove_device.
> + */
> + CAIF_CONTROL_REMOTE_SHUTDOWN = 4,
> +
> + /** Channel disconnect is complete. This is an acknowledgement to
> + * \ref caif_remove_device from the modem.
> + * \ref caif_transmit or \ref caif_flow_control must not be
> + * called after this.
> + */
> + CAIF_CONTROL_DEV_DEINIT = 5,
> +
> + /** Channel creation has failed. This is a negative acknowledgement
> + * to \ref caif_add_device from the modem.
> + */
> + CAIF_CONTROL_DEV_INIT_FAILED = 6
> +};
> +
> +/** Flow control information (used in \ref caif_device.control_cb) used
> + * for controlling outgoing flow.
> + */
> +enum caif_flowctrl {
> + /** Flow Control is ON, transmit function can start sending data */
> + CAIF_FLOWCTRL_ON = 0,
> + /** Flow Control is OFF, transmit function should stop sending data */
> + CAIF_FLOWCTRL_OFF = 1,
> +};
> +
> +/** Transmits CAIF packets on channel.
> + * This function is non-blocking and safe to use in tasklet context.
> + * The CAIF stack takes ownership of the socket buffer (SKB) after calling
> + * \ref caif_transmit.
> + * This means that the user cannot access the SKB afterwards; this applies
> + * even in error situations.
> + *
> + * @return 0 on success, < 0 upon error.
> + *
> + * @param[in] skb Socket buffer holding data to be written.
> + * @param[in] dev Structure used when creating the channel
> + *
> + *
> + * Error codes:
> + * - \b ENOTCONN, The channel is not connected.
> + * - \b EPROTO, Protocol error (or SKB is faulty)
> + * - \b EIO IO error (unspecified error)
> + */
> +int caif_transmit(struct caif_device *dev, struct sk_buff *skb);
> +
> +/** Function for sending flow ON / OFF to remote end.
> + * This function is non-blocking and safe to use in tasklet context.
> + *
> + * @param[in] dev Reference to device data.
> + * @param[in] flow Flow control information.
> +
> + * @return 0 on success, < 0 upon error.
> + * Error codes:
> + * - \b ENOTCONN, The channel is not connected.
> + * - \b EPROTO, Protocol error.
> + * - \b EIO IO error (unspecified error).
> + */
> +int caif_flow_control(struct caif_device *dev, enum caif_flowctrl flow);
> +
> +/** Handle for kernel internal CAIF channels.
> + * All fields in this structure must be filled in by client before calling
> + * \ref caif_add_device (except _caif_handle).
> + */
> +struct caif_device {
> +
> + /** Channel configuration parameter. Contains information about type
> + * and configuration of the channel.
> + * This must be set before calling \ref caif_add_device.
> + */
> + struct caif_channel_config caif_config;
> +
> +
> + /** Callback function for receiving CAIF Packets from channel.
> + * This callback is called from softirq context (tasklet).
> + * The receiver <b> must </b> free the SKB.
> + * <b> DO NOT BLOCK IN THIS FUNCTION! </b>
> + *
> + * If the client has to do blocking operations then
> + * it must start its own work queue (or kernel thread).
> + *
> + * @param[in] dev Reference to device data.
> + * @param[in] skb Socket buffer with received data.
> + */
> + void (*receive_cb) (struct caif_device *dev, struct sk_buff *skb);
> +
> +
> + /** Callback function for notifying flow control from remote end - see
> + * \ref caif_control.
> + * This callback is called from from softirq context (tasklet).
> + *
> + * <b> DO NOT BLOCK IN THIS FUNCTION! </b>
> + *
> + * Client must not call \ref caif_transmit from this function.
> + *
> + * If the client has queued packets to send then
> + * it must start its own thread to do \ref caif_transmit.
> + *
> + * @param[in] dev Reference to device data.
> + * @param[in] ctrl CAIF control info \ref caif_control.
> + * e.g. Flow control
> + * \ref CAIF_CONTROL_FLOW_ON or
> + * \ref CAIF_CONTROL_FLOW_OFF
> + */
> + void (*control_cb) (struct caif_device *dev, enum caif_control ctrl);
> +
> + /** This is a CAIF private attribute, holding CAIF internal reference
> + * to the CAIF stack. Do not update this field.
> + */
> + void *_caif_handle;
> +
> + /** This field may be filled in by client for their own usage. */
> + void *user_data;
> +};
> +
> +/** Add (connect) a CAIF Channel.
> + * This function is non-blocking. The channel connect is reported in
> + * \ref caif_device.control_cb.
> + * The channel is not open until \ref caif_device.control_cb is called with
> + * \ref CAIF_CONTROL_DEV_INIT.
> + * If setting up the channel fails then \ref caif_device.control_cb is called
> + * with \ref CAIF_CONTROL_DEV_INIT_FAILED.
> + *
> + * \ref caif_transmit, \ref caif_flow_control or \ref caif_remove_device must
> + * not be called before receiveing CAIF_CONTROL_DEV_INIT.
> + * @return 0 on success, < 0 on failure.
> + *
> + * Error codes:
> + * - \b -EINVAL Invalid arguments
> + * - \b -ENODEV No PHY device exists.
> + * - \b -EIO IO error (unspecified error)
> + */
> +int caif_add_device(struct caif_device *dev);
> +
> +/** Disconnect a CAIF Channel
> + * This function is non-blocking.
> + * The channel has not been disconnected until \ref caif_device : control_cb is
> + * called with \ref CAIF_CONTROL_DEV_DEINIT.
> + * \ref caif_transmit or \ref caif_flow_control \b must not be called after
> + * receiving \ref CAIF_CONTROL_DEV_DEINIT.
> + * The client is responsible for freeing the \ref caif_device structure after
> + * receiving \ref CAIF_CONTROL_DEV_DEINIT (if applicable).
> + * @return 0 on success.
> + *
> + * - \b EIO IO error (unspecified error)
> + */
> +int caif_remove_device(struct caif_device *caif_dev);
> +
> +/** Convenience function for allocating a socket buffer for usage with CAIF
> + * and copy user data into the socket buffer.
> + * @param[in] data User data to send with CAIF.
> + * @param[in] data_length Length of data to send.
> + * @return New socket buffer containing user data.
> + */
> +struct sk_buff *caif_create_skb(unsigned char *data, unsigned int data_length);
> +
> +/** Convenience function for extracting data from a socket buffer (SKB) and
> + * then destroying the SKB.
> + * Copies data from the SKB and then frees the SKB.
> + * @param[in] skb SKB to extract data from. SKB will be freed after
> + * extracting data.
> + *
> + * @param[in] data User data buffer to extract packet data into.
> + * @param[in] max_length User data buffer length,
> + * @return number of bytes extracted; < 0 upon error.
> + *
> + */
> +int caif_extract_and_destroy_skb(struct sk_buff *skb, unsigned char *data,
> + unsigned int max_length);
> +
> +/*! @} */
> +
> +#endif /* CAIF_KERNEL_H_ */
---
~Randy
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH net-next-2.6 12/13] net-caif: add CAIF documentation
[not found] ` <1264028130-14364-13-git-send-email-sjur.brandeland@stericsson.com>
@ 2010-01-26 18:00 ` Randy Dunlap
0 siblings, 0 replies; 47+ messages in thread
From: Randy Dunlap @ 2010-01-26 18:00 UTC (permalink / raw)
To: sjur.brandeland; +Cc: netdev, davem, marcel, stefano.babic
On Wed, 20 Jan 2010 23:55:29 +0100 sjur.brandeland@stericsson.com wrote:
> From: Sjur Braendeland <sjur.brandeland@stericsson.com>
>
> Documentation of the CAIF Protocol.
>
> Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
> ---
> Documentation/networking/caif/Linux-CAIF.txt | 235 ++++++++++++++++++++++++++
> Documentation/networking/caif/README | 27 +++
> 2 files changed, 262 insertions(+), 0 deletions(-)
>
> diff --git a/Documentation/networking/caif/Linux-CAIF.txt b/Documentation/networking/caif/Linux-CAIF.txt
> new file mode 100644
> index 0000000..5576e42
> --- /dev/null
> +++ b/Documentation/networking/caif/Linux-CAIF.txt
> @@ -0,0 +1,235 @@
> +Linux CAIF
> +===========
> +copyright (C) ST-Ericsson AB 2010
> +Author: Sjur Brendeland/ sjur.brandeland@stericsson.com
> +License terms: GNU General Public License (GPL) version 2
> +
> +
> +Introduction
> +------------
> +CAIF is a MUX protocol used by ST-Ericsson cellular modems for
> +communication between Modem and host. The host processes can open virtual AT
> +channels, initiate GPRS Data connections, Video channels and Utility Channels.
> +The Utility Channels are general purpose pipes between modem and host.
> +
> +ST-Ericsson modems support a number of transports between modem
> +and host. Currently, UART and Loopback are available for Linux.
> +
> +
> +Architecture:
> +------------
> +The implementation of CAIF is divided into:
> +* CAIF Socket Layer, Kernel API, and Net Device.
> +* CAIF Generic Protocol Implementation
> +* CAIF Link Layer, implemented as NET devices.
> +
> +
> + RTNL/IOCTL
> + !
> + ! +------+ +------+ +------+
> + ! +------+! +------+! +------+!
> + ! ! Sock !! !Kernel!! ! Net !!
> + ! ! API !+ ! API !+ ! Dev !+ <- CAIF Client APIs
> + ! +------+ +------! +------+
> + ! ! ! !
> + ! +----------!----------+
> + ! +------+ <- CAIF Protocol Implementation
> + +-------> ! CAIF !
> + +------+
> + +--------!--------+
> + ! !
> + +------+ +-----+
> + !Loop ! ! TTY ! <- Link Layer (Net Devices)
> + +------+ +-----+
> +
> +
> +Using CAIF (IP) Net Device
> +----------------------
> +CAIF Net device can be created by use of Socket IOCTLs,
> +or RT Netlink.
> +
> + static struct ifcaif_param param = {
> + .ipv4_connid = 1,
> + .loop = 0
> + };
> + static struct ifreq ifr = {
> + .ifr_name = "caif%d",
> + .ifr_ifru.ifru_data = ¶m
> +
> + };
> + s = socket(AF_CAIF, SOCK_SEQPACKET, CAIFPROTO_AT);
> + ioctl(s, SIOCCAIFNETNEW, &ifr);
> +
> +
> +Using the Kernel API
> +----------------------
> +The Kernel API is used for accessing CAIF channels from the
> +kernel.
> +The user of the API has to implement two callbacks for receive
> +and control.
> +The receive callback gives a CAIF packet as a SKB. The control
> +callback will
> +notify of channel initialization complete, and flow-on/flow-
> +off.
> +
> +
> + struct caif_device caif_dev = {
> + .caif_config = {
> + .name = "MYDEV"
> + .type = CAIF_CHTY_AT
> + }
> + .receive_cb = my_receive,
> + .control_cb = my_control,
> + };
> + caif_add_device(&caif_dev);
> + caif_transmit(&caif_dev, skb);
> +
> +See the caif_kernel.h for details about the CAIF kernel API.
> +
> +
> +I M P L E M E N T A T I O N
> +===========================
> +===========================
> +
> +Generic CAIF Protocol Layer
> +=========================================
> +
> +caif/generic is a generic CAIF protocol implementation. It implements the CAIF
> +protocol as defined by ST-Ericsson.
> +It implements the CAIF protocol stack in a layered approach, where
> +each layer described in the specification is implemented as a separate layer.
> +The architecture is inspired by the design patterns "Protocol Layer" and
> +"Protocol Packet".
> +
> +== CAIF structure ==
> +
> +The goal is to have generic CAIF as OS and system independent as possible.
> +All generic CAIF code can be found under net/caif/generic and
> +include/net/caif/generic.
> +
> +The generic CAIF implementation is:
> + - Simple implementation of CAIF.
> + - Layered architecture (a la Streams), each layer in the CAIF
> + specification is implemented in a separate c-file.
> + - Clients must implement PHY layer to access physical HW
> + with receive and transmit functions.
> + - Clients must call configuration function to add PHY layer.
> + - Clients must implement adaptation layer to consume/produce
> + CAIF payload with receive and transmit functions.
> + - Clients must call configuration function to add adaptation
> + layer.
> + - When receiving / transmitting CAIF Packets (cfpkt), ownership is passed
> + to the called function (except for framing layers' receive functions
> + or if a transmit function returns an error, in which case the caller
> + must free the packet).
> +
> +Layered Architecture
> +--------------------
> +The CAIF protocol can be divided into two parts: Support functions and Protocol
> +Implementation. The support functions include:
> +
> + - CFPKT CAIF Packet. Implementation of CAIF Protocol Packet. The
> + CAIF Packet has functions for creating, destroying and adding content
> + and for adding/extracting header and trailers to protocol packets.
> +
> + - CFLST CAIF list implementation.
> +
> + - CFGLUE CAIF Glue. Contains OS Specifics, such as memory
> + allocation, endianness, etc.
> +
> +The CAIF Protocol implementation contains:
> +
> + - CFCNFG CAIF Configuration layer. Configures the CAIF Protocol
> + Stack and has a Client interface for adding Link-Layer and
> + Driver interfaces on top of the CAIF Stack.
> +
> + - CFCTRL CAIF Control layer. Encodes and Decodes control messages
> + such as enumeration and channel setup. Also matches request and
> + response messages.
> +
> + - CFSERVL General CAIF Service Layer functionality; handles flow
> + control and remote shutdown requests.
> +
> + - CFVEI CAIF VEI layer. Handles CAIF VEI layer (AT-Channel),
> + encodes/decodes VEI frames.
> +
What is VEI?
> + - CFDGML CAIF Data-gram layer. Handles CAIF Data-gram layer (IP
> + traffic), encodes/decodes Datagram frames.
Drop the '-' in data-gram. Just use "datagram".
> +
> + - CFMUX CAIF Mux layer. Handles multiplexing between multiple
> + physical bearers and multiple channels such as VEI, Datagram, etc.
> + The MUX keeps track of the existing CAIF Channels and
> + Physical Instances and selects the apropriate instance based
> + on Channel-Id and Physical-ID.
> +
> + - CFFRML CAIF Framing layer. Handles Framing i.e. Frame length
> + and frame checksum.
> +
> + - CFSERL CAIF Serial layer. Handles concatenation/split of frames
> + into CAIF Frames with correct length.
> +
> +
> +
> + +---------+
> + | Config |
> + | CFCNFG |
> + +---------+
> + !
> + +---------+ +---------+ +---------+
> + | AT | | Control | | Datagram|
> + | CFVEIL | | CFCTRL | | CFDGML |
> + +---------+ +---------+ +---------+
> + \_____________!______________/
> + !
> + +---------+
> + | MUX |
> + | |
> + +---------+
> + _____!_____
> + / \
> + +---------+ +---------+
> + | CFFRML | | CFFRML |
> + | Framing | | Framing |
> + +---------+ +---------+
> + ! !
> + +---------+ +---------+
> + | | | Serial |
> + | | | CFSERL |
> + +---------+ +---------+
> +
> +
> +In this layered approach the following "rules" applies.
apply:
> + - All layers embedd the same structure "struct layer"
embed
> + - A layer does not depend on any other layer's private data.
> + - Layers are stacked by setting the pointers
> + layer->up , layer->dn
> + - In order to send data upwards, each layer should do
> + layer->up->receive(layer->up, packet);
> + - In order to send data downwards, each layer should do
> + layer->dn->transmit(layer->dn, packet);
> +
> +
> +Linux Driver Implementation
> +===========================
> +
> +Linux GPRS Net Device and CAIF socket are implemented on top of the
> +Generic CAIF protocol. The Net device and CAIF socket have an instance of
> +'struct layer', just like the generic CAIF protocol stack.
> +Net device and Socket implement the 'receive()' function defined by
> +'struct layer', just like the rest of the CAIF stack. In this way, transmit and
> +receive of packets is handled as by the rest of the layers: the 'dn->transmit()'
> +function is called in order to transmit data.
> +
> +The layer on top of the generic CAIF implementation is
> +sometimes refered to as the "adaptation layer".
referred
> +
> +
> +Configuration of Link Layer
> +---------------------------
> +The Link Layer is implemented as Linux net devices (struct net_device).
> +Payload handling and registration is done using standard Linux mecanisms.
mechanisms.
> +
> +The CAIF Protocol relies on a lossless link layer without implementing
> +retransmission. This implies that packet drops must not happen.
> +Therefor a flow-control mecanism is implemented where the physical
Therefore a flow-control mechanism
> +interface can initiate flow stop for all CAIF Channels.
---
~Randy
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH net-next-2.6 10/13] net-caif: add kernel-client API for CAIF
2010-01-26 17:50 ` Randy Dunlap
@ 2010-01-26 19:56 ` Sjur Brændeland
0 siblings, 0 replies; 47+ messages in thread
From: Sjur Brændeland @ 2010-01-26 19:56 UTC (permalink / raw)
To: Randy Dunlap; +Cc: netdev, davem, marcel, stefano.babic
Hi Randy,
Randy Dunlap wrote:
> On Wed, 20 Jan 2010 23:55:27 +0100 sjur.brandeland@stericsson.com
>
> Hi,
>
> I'm probably repeating myself here, but this function documentation
> is not formatted in the way which the Linux kernel expects/wants.
>
> Some of the others (after this one) use "/**" to begin the comment
> block.
> In kernel-land, that is used only for kernel-doc formatted notation.
Thanks for feedback, it is rather my fault letting this slip through.
As you probably have figured CAIF was originally documented using
doxygen for formatting. Unfortunately I have not yet managed to get rid of this.
>
>
>> +/*!\page caif_kernel.h
>> + * This is the specification of the CAIF kernel internal interface
>> to + * CAIF Channels. + * This interface follows the pattern used in
>> Linux device drivers +with a + * struct \ref caif_device
>> + * holding control data handling each device instance. + *
>> + * The functional interface consists of a few basic functions:
>> + * - \ref caif_add_device Configure and connect the
>> CAIF + * channel to the remote end. Configuration is
>> described in + * \ref caif_channel_config.
>> + * - \ref caif_remove_device Disconnect and remove the
>> channel. + * - \ref caif_transmit Sends a CAIF
>> message on the link. + * - \ref caif_device.receive_cb Receive
>> callback function for + * receiving packets.
>> + * - \ref caif_device.control_cb Control information from the
>> CAIF stack. + * - \ref caif_flow_control Send flow
>> control message to remote end. + * + *
>> + * Details:
>> + * \see { caif_kernel }
>> + *
>> + * \code
>> + *
>> +#include <net/caif/caif_kernel.h>"
BR/Sjur
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH net-next-2.6 09/13] net-caif: add CAIF netdevice
2010-01-21 8:03 ` Patrick McHardy
@ 2010-02-02 12:37 ` Sjur Brændeland
2010-02-02 14:14 ` Marcel Holtmann
2010-02-02 14:17 ` Patrick McHardy
0 siblings, 2 replies; 47+ messages in thread
From: Sjur Brændeland @ 2010-02-02 12:37 UTC (permalink / raw)
To: Patrick McHardy; +Cc: netdev, davem, marcel, stefano.babic, randy.dunlap
Hi Patrick.
Sorry for late response.
Patrick McHardy wrote:
> sjur.brandeland@stericsson.com wrote:
>> +static void ipcaif_net_init(struct net_device *dev) { + struct
>> chnl_net *priv; + dev->netdev_ops = &netdev_ops;
>> + dev->destructor = free_netdev;
>
> These (especially ->destructor) should be set in the setup function.
The ipcaif_net_init is actually the setup function, but it is badly named.
I'll rename it to ipcaif_net_setup, sorry for the confusion.
>
>> + dev->flags |= IFF_NOARP;
>> + dev->flags |= IFF_POINTOPOINT;
>> + dev->needed_headroom = CAIF_NEEDED_HEADROOM;
>> + dev->needed_tailroom = CAIF_NEEDED_TAILROOM;
>> + dev->mtu = SIZE_MTU;
>> + dev->tx_queue_len = CAIF_NET_DEFAULT_QUEUE_LEN;
>
> These too I guess, its uncommon to reinitialize mtu and tx_queue_len
> when setting a device down and up again.
>
>> +
>> + priv = (struct chnl_net *)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->flowenabled = false; +
>> + ASSERT_RTNL();
>> + init_waitqueue_head(&priv->netmgmt_wq);
>> + list_add(&priv->list_field, &chnl_net_list); }
>
>> +static int chnl_net_hard_start_xmit(struct sk_buff *skb, struct
>> +net_device *dev)
>
> static netdev_tx_t
>
>> +{
>> + struct chnl_net *priv;
>> + struct cfpkt *pkt = NULL;
>> + int len;
>> + int result = -1;
>> +
>> + /* Get our private data. */
>> + priv = (struct chnl_net *)netdev_priv(dev);
>> + if (!priv)
>> + return -ENOSPC;
>
> This is an impossible condition, netdev_priv() will never return NULL.
> The cast is also unnecessary.
Thanks, I'll fix this.
>> +
>> +
>> + if (skb->len > priv->netdev->mtu) {
>> + pr_warning("CAIF: %s(): Size of skb exceeded MTU\n", __func__);
>> + return -ENOSPC; + }
>> +
>> + if (!priv->flowenabled) {
>> + pr_debug("CAIF: %s(): dropping packets flow off\n", __func__);
>> + return NETDEV_TX_BUSY; + }
>> +
>> + if (priv->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;
>
> swap()?
The modem provides a loopback function for the CAIF link type (CAIF_CHTY_DATAGRAM_LOOP).
This is useful for testing the physical link between modem and host.
In this scenario we need to swap src and destination ip address.
I'll move this to a separate function in next patch set.
>
>
>> +
>> +static int ipcaif_newlink(struct net *src_net, struct net_device
>> *dev, + struct nlattr *tb[], struct nlattr *data[]) { + int err;
>> + struct chnl_net *caifdev;
>> + ASSERT_RTNL();
>> + caifdev = netdev_priv(dev);
>> + caif_netlink_parms(data, &caifdev->config);
>> + err = register_netdevice(dev);
>> + if (err) {
>> + pr_warning("CAIF: %s(): device rtml registration failed\n", +
>> __func__); + goto out;
>> + }
>> + dev_hold(dev);
>
> What is this reference used for? You don't have a dellink function,
> so this looks like a leak.
I don't think it leaks because I do dev_put in chnl_net_uninit,
but you're right - I don't really need this. I'll remove the
dev_hold and dev_put completely in next patch-set.
>> +static struct rtnl_link_ops ipcaif_link_ops __read_mostly = {
>> + .kind = "caif", + .priv_size = (size_t)sizeof(struct chnl_net),
>
> Unnecessary cast.
OK, Thanks.
>
>> + .setup = ipcaif_net_init,
>> + .maxtype = IFLA_CAIF_MAX,
>> + .policy = ipcaif_policy,
>> + .newlink = ipcaif_newlink,
>> + .changelink = ipcaif_changelink,
>> + .get_size = ipcaif_get_size,
>> + .fill_info = ipcaif_fill_info,
>> +
>> +};
>> +
>> +int chnl_net_ioctl(unsigned int cmd, unsigned long arg, bool
>> +from_user_land) { + struct chnl_net *priv;
>> + int result = -1;
>> + struct chnl_net *dev;
>> + struct net_device *netdevptr;
>> + int ret;
>> + struct ifreq ifreq;
>> + struct ifcaif_param param;
>> + rtnl_lock();
>> + if (from_user_land) {
>> + if (copy_from_user(&ifreq, (const void *)arg, sizeof(ifreq)))
>> + return -EFAULT; + } else
>> + memcpy(&ifreq, (void *)arg, sizeof(ifreq));
>
> Why do you need both an ioctl and a netlink interface?
Well, I would like to keep support for both netlink and ioctl.
Internally we are using a netlink interface, and oFono is using ioctl to create interfaces.
>> +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);
>
> You don't need to unregister on error. The ioctl should be
> unregistered I guess.
Very well spotted, thanks. This could leave wild pointers.
>> +static void __exit chnl_exit_module(void) {
>> + struct chnl_net *dev = NULL;
>> + struct list_head *list_node;
>> + struct list_head *_tmp;
>> + rtnl_lock();
>> + list_for_each_safe(list_node, _tmp, &chnl_net_list) {
>> + dev = list_entry(list_node, struct chnl_net, list_field);
>> + delete_device(dev); + }
>> + rtnl_unlock();
>> + rtnl_link_unregister(&ipcaif_link_ops);
>> + caif_register_ioctl(NULL);
>
> This is racy, rtnl_link_unregister() will clean up all CAIF devices,
> but the ioctl handler might register new ones after that. I'd suggest
> to drop the ioctl interface completely.
You're right thank you, I'll fix this.
BR/Sjur
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH net-next-2.6 09/13] net-caif: add CAIF netdevice
2010-02-02 12:37 ` Sjur Brændeland
@ 2010-02-02 14:14 ` Marcel Holtmann
2010-02-02 14:19 ` Patrick McHardy
2010-02-02 14:17 ` Patrick McHardy
1 sibling, 1 reply; 47+ messages in thread
From: Marcel Holtmann @ 2010-02-02 14:14 UTC (permalink / raw)
To: Sjur Brændeland
Cc: Patrick McHardy, netdev, davem, stefano.babic, randy.dunlap
Hi Sjur,
> >> +int chnl_net_ioctl(unsigned int cmd, unsigned long arg, bool
> >> +from_user_land) { + struct chnl_net *priv;
> >> + int result = -1;
> >> + struct chnl_net *dev;
> >> + struct net_device *netdevptr;
> >> + int ret;
> >> + struct ifreq ifreq;
> >> + struct ifcaif_param param;
> >> + rtnl_lock();
> >> + if (from_user_land) {
> >> + if (copy_from_user(&ifreq, (const void *)arg, sizeof(ifreq)))
> >> + return -EFAULT; + } else
> >> + memcpy(&ifreq, (void *)arg, sizeof(ifreq));
> >
> > Why do you need both an ioctl and a netlink interface?
>
> Well, I would like to keep support for both netlink and ioctl.
> Internally we are using a netlink interface, and oFono is using ioctl to create interfaces.
we could try to convert oFono in using netlink. Shouldn't be that hard
as long as it is RTNL. Only the generic netlink is painful in userspace
applications with a mainloop.
Regards
Marcel
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH net-next-2.6 09/13] net-caif: add CAIF netdevice
2010-02-02 12:37 ` Sjur Brændeland
2010-02-02 14:14 ` Marcel Holtmann
@ 2010-02-02 14:17 ` Patrick McHardy
1 sibling, 0 replies; 47+ messages in thread
From: Patrick McHardy @ 2010-02-02 14:17 UTC (permalink / raw)
To: Sjur Brændeland; +Cc: netdev, davem, marcel, stefano.babic, randy.dunlap
Sjur Brændeland wrote:
>>> + 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;
>> swap()?
>
>
> The modem provides a loopback function for the CAIF link type (CAIF_CHTY_DATAGRAM_LOOP).
> This is useful for testing the physical link between modem and host.
> In this scenario we need to swap src and destination ip address.
> I'll move this to a separate function in next patch set.
There is already a swap() function which you can use.
>>> +static int ipcaif_newlink(struct net *src_net, struct net_device
>>> *dev, + struct nlattr *tb[], struct nlattr *data[]) { + int err;
>>> + struct chnl_net *caifdev;
>>> + ASSERT_RTNL();
>>> + caifdev = netdev_priv(dev);
>>> + caif_netlink_parms(data, &caifdev->config);
>>> + err = register_netdevice(dev);
>>> + if (err) {
>>> + pr_warning("CAIF: %s(): device rtml registration failed\n", +
>>> __func__); + goto out;
>>> + }
>>> + dev_hold(dev);
>> What is this reference used for? You don't have a dellink function,
>> so this looks like a leak.
>
> I don't think it leaks because I do dev_put in chnl_net_uninit,
> but you're right - I don't really need this. I'll remove the
> dev_hold and dev_put completely in next patch-set.
But it wouldn't get released when deleting a link using netlink
I think since without a dellink function I don't see what would
invoke chnl_net_uninit(). Anyways, removing it will fix this :)
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH net-next-2.6 09/13] net-caif: add CAIF netdevice
2010-02-02 14:14 ` Marcel Holtmann
@ 2010-02-02 14:19 ` Patrick McHardy
0 siblings, 0 replies; 47+ messages in thread
From: Patrick McHardy @ 2010-02-02 14:19 UTC (permalink / raw)
To: Marcel Holtmann
Cc: Sjur Brændeland, netdev, davem, stefano.babic, randy.dunlap
Marcel Holtmann wrote:
> Hi Sjur,
>
>>>> +int chnl_net_ioctl(unsigned int cmd, unsigned long arg, bool
>>>> +from_user_land) { + struct chnl_net *priv;
>>>> + int result = -1;
>>>> + struct chnl_net *dev;
>>>> + struct net_device *netdevptr;
>>>> + int ret;
>>>> + struct ifreq ifreq;
>>>> + struct ifcaif_param param;
>>>> + rtnl_lock();
>>>> + if (from_user_land) {
>>>> + if (copy_from_user(&ifreq, (const void *)arg, sizeof(ifreq)))
>>>> + return -EFAULT; + } else
>>>> + memcpy(&ifreq, (void *)arg, sizeof(ifreq));
>>> Why do you need both an ioctl and a netlink interface?
>> Well, I would like to keep support for both netlink and ioctl.
>> Internally we are using a netlink interface, and oFono is using ioctl to create interfaces.
>
> we could try to convert oFono in using netlink. Shouldn't be that hard
> as long as it is RTNL. Only the generic netlink is painful in userspace
> applications with a mainloop.
I would prefer that too.
^ permalink raw reply [flat|nested] 47+ messages in thread
end of thread, other threads:[~2010-02-02 14:19 UTC | newest]
Thread overview: 47+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-01-20 22:55 [PATCH net-next-2.6 00/13] net-caif: introducing CAIF protocol stack sjur.brandeland
2010-01-20 22:55 ` [PATCH net-next-2.6 01/13] net-caif: add CAIF protocol definitions sjur.brandeland
2010-01-20 22:55 ` [PATCH net-next-2.6 02/13] net-caif: add CAIF header files sjur.brandeland
2010-01-20 23:27 ` Randy Dunlap
2010-01-22 11:05 ` Sjur Brændeland
2010-01-21 7:44 ` Patrick McHardy
2010-01-22 10:53 ` Sjur Brændeland
2010-01-22 7:51 ` Marcel Holtmann
2010-01-22 8:18 ` Sjur Brændeland
2010-01-22 8:39 ` Marcel Holtmann
2010-01-22 8:56 ` Sjur Brændeland
2010-01-22 9:16 ` Marcel Holtmann
2010-01-22 9:43 ` Sjur Brændeland
2010-01-20 22:55 ` [PATCH net-next-2.6 03/13] net-caif: add CAIF generic protocol stack " sjur.brandeland
2010-01-21 8:13 ` Patrick McHardy
2010-01-22 11:02 ` Sjur Brændeland
2010-01-22 9:28 ` Marcel Holtmann
2010-01-22 10:01 ` Sjur Brændeland
2010-01-22 10:12 ` Marcel Holtmann
2010-01-22 10:16 ` Sjur Brændeland
2010-01-22 10:24 ` Marcel Holtmann
2010-01-20 22:55 ` [PATCH net-next-2.6 04/13] net-caif: add CAIF " sjur.brandeland
2010-01-20 22:55 ` [PATCH net-next-2.6 05/13] net-caif: add CAIF generic protocol stack sjur.brandeland
2010-01-20 22:55 ` [PATCH net-next-2.6 06/13] net-caif: add CAIF generic caif support functions sjur.brandeland
2010-01-20 22:55 ` [PATCH net-next-2.6 07/13] net-caif: add CAIF device registration functionality sjur.brandeland
2010-01-20 22:55 ` [PATCH net-next-2.6 08/13] net-caif: add CAIF socket implementation sjur.brandeland
2010-01-20 22:55 ` [PATCH net-next-2.6 09/13] net-caif: add CAIF netdevice sjur.brandeland
2010-01-21 8:03 ` Patrick McHardy
2010-02-02 12:37 ` Sjur Brændeland
2010-02-02 14:14 ` Marcel Holtmann
2010-02-02 14:19 ` Patrick McHardy
2010-02-02 14:17 ` Patrick McHardy
2010-01-20 22:55 ` [PATCH net-next-2.6 10/13] net-caif: add kernel-client API for CAIF sjur.brandeland
2010-01-26 17:50 ` Randy Dunlap
2010-01-26 19:56 ` Sjur Brændeland
2010-01-20 22:55 ` [PATCH net-next-2.6 11/13] net-caif: add CAIF Kconfig and Makefiles sjur.brandeland
2010-01-22 9:40 ` Marcel Holtmann
2010-01-20 22:55 ` [PATCH net-next-2.6 13/13] net-caif-driver: add CAIF serial driver (ldisc) sjur.brandeland
2010-01-20 23:36 ` Randy Dunlap
2010-01-22 11:07 ` Sjur Brændeland
2010-01-22 9:21 ` Marcel Holtmann
2010-01-22 9:56 ` Sjur Brændeland
2010-01-22 10:07 ` Marcel Holtmann
2010-01-22 9:43 ` [PATCH net-next-2.6 00/13] net-caif: introducing CAIF protocol stack Marcel Holtmann
2010-01-22 10:11 ` Sjur Brændeland
2010-01-22 10:19 ` Marcel Holtmann
[not found] ` <1264028130-14364-13-git-send-email-sjur.brandeland@stericsson.com>
2010-01-26 18:00 ` [PATCH net-next-2.6 12/13] net-caif: add CAIF documentation Randy Dunlap
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).