All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/9] XSHM: Shared Memory Driver for ST-E Thor M7400 LTE modem
@ 2011-12-07  9:27 Sjur Brændeland
  2011-12-07  9:28 ` [PATCH 1/9] xshm: Shared Memory layout for ST-E M7400 driver Sjur Brændeland
                   ` (10 more replies)
  0 siblings, 11 replies; 26+ messages in thread
From: Sjur Brændeland @ 2011-12-07  9:27 UTC (permalink / raw)
  To: linux-kernel; +Cc: Linus Walleij, Paul Bolle, Sjur Brændeland

Introduction:
~~~~~~~~~~~~~
This patch-set introduces the Shared Memory Driver for ST-Ericsson's
Thor M7400 LTE modem.
The shared memory model is implemented for Chip-to-chip and
uses a reserved memory area and a number of bi-directional
channels. Each channel has it's own designated data area where payload
data is copied into.

Two different channel types are defined, one stream channel which is
implemented as a traditional ring-buffer, and a packet channel which
is a ring-buffer of fix sized buffers where each buffer contains
an array of CAIF frames.

The notification of read and write index updates are handled in a
separate driver called c2c_genio. This driver will be contributed separately,
but the API is included in this patch-set.

The channel configuration is stored in shared memory, each channel
has a designated area in shared memory for configuration data,
read and write indexes and a data area.

Configuration
~~~~~~~~~~~~~~~

        IPC-TOC
        +--------------------+	   Index Area
        | Channel Descr 0    | -----> +------------+
	|                    | -+     |	Read Index |
	|--------------------|	|     |------------|
	| Channel Descr 1    | 	|     |	Write Index|
	|                    |	|     +------------+
	|--------------------|	|   Data Area
	|       ...    	     |	|     +------------+
	|                    |	+---> |            |
	+--------------------+	      |	       	   |
				      |	       	   |
				      +------------+

A IPC-TOC (table of content) is used for holding configuration data
for the channels (struct xshm_ipctoc). It contains an array of
channel descriptors (struct xshm_ipctoc_channel). The channel
descriptors points out the data area and the location of
read and write indexes.

The configuration is provided from user-space using gen-netlink.
The gen-netlink format is defined in xshm_netlink.h and and handled
in xshm_boot.c

Packet data
~~~~~~~~~~~
The packet channel is set up to minimize interrupts needed to
transfer a packet and to allow efficient DMA operations on the modem.

Ring Buffer Indexes:
    +------------+
  +-| Read Index |
  | |------------|
  | | Write Index|------------------------+
  | +------------+   		 	  |
  |    		    			  |
  V    	  				  |
Buffer-0:				  V Buffer-1:
 +----------------------------------------+---------------+---
 |ofs,len|ofs,len| ....| frm-0|frm-1| ... | ofs,len | ....|...
 +----------------------------------------+---------------+--
   |      |              ^      ^
   +---------------------+      |
	  +---------------------+

Packet data is organized in a channel containing a number of fixed-
size buffers. The channel has a read and write pointer to a buffer in a normal
ring-buffer fashion.

Each buffer holds an array of CAIF-frames, and starts with an descriptor array
containing pointers to the data frames in the buffer. The descriptor array
contains offset and length of each frame.

The packet device (caif_xshm.c) is implemented as a network interface of
type ARPHRD_CAIF.

Stream data
~~~~~~~~~~~
The driver for the stream channel is implemented as a character device
interface to user space. The character device implements non-blocking open
and non-blocking IO in general. The character device is implementing
a traditional circular buffer directly in the shared memory region for
the channel.

Driver model
~~~~~~~~~~~~~~
Current implementation is using the platform bus for XSHM devices.
The packet channels are named "xshmp", and stream channel "xshms".

/sys/bus:
|-- platform
|   |-- devices
|   |   |-- xshmp.1 -> ../../../devices/platform/xshm/xshmp.1
|   |   `-- xshms.0 -> ../../../devices/platform/xshm/xshms.0
|   |-- drivers
|   |   |-- xshmp
|   |   |   |-- module -> ../../../../module/caif_xshm
|   |   |   `-- xshmp.1 -> ../../../../devices/platform/xshm/xshmp.1
|   |   `-- xshms
|   |       |-- module -> ../../../../module/xshm_chr
|   |       `-- xshms.0 -> ../../../../devices/platform/xshm/xshms.0
 
/sys/devices:
|-- platform
|   |-- uevent
|   `-- xshm
|       |-- bootimg
|       |-- caif_ready
|       |-- ipc_ready
|       |-- subsystem -> ../../../bus/platform
|       |-- xshmp.1
|       |   |-- driver -> ../../../../bus/platform/drivers/xshmp
|       |   |-- subsystem -> ../../../../bus/platform
|       `-- xshms.0
|           |-- driver -> ../../../../bus/platform/drivers/xshms
|           |-- misc
|           |   `-- xshm0
|           |       |-- device -> ../../../xshms.0
|           |       |-- subsystem -> ../../../../../../class/misc
`-- virtual
    |-- net
    |   |-- cfshm0


Review comments and feedback is welcome.

Patchset is based on todays linux-next.

Regards,
Sjur Brændeland


Sjur Brændeland (9):
  xshm: Shared Memory layout for ST-E M7400 driver.
  xshm: Channel config definitions for ST-E M7400 driver.
  xshm: Config data use for platform devices.
  xshm: geni/geno driver interface.
  xshm: genio dummy driver
  xshm: Platform device for XSHM
  xshm: Character device for XSHM channel access.
  xshm: Makefile and Kconfig for M7400 Shared Memory Drivers
  caif-xshm: Add CAIF driver for Shared memory for M7400

 drivers/Kconfig                   |    2 +
 drivers/Makefile                  |    1 +
 drivers/net/caif/Kconfig          |   10 +
 drivers/net/caif/Makefile         |    1 +
 drivers/net/caif/caif_xshm.c      |  935 +++++++++++++++++++++++++++
 drivers/xshm/Kconfig              |   17 +
 drivers/xshm/Makefile             |    3 +
 drivers/xshm/genio_dummy.c        |   61 ++
 drivers/xshm/xshm_boot.c          | 1187 ++++++++++++++++++++++++++++++++++
 drivers/xshm/xshm_chr.c           | 1269 +++++++++++++++++++++++++++++++++++++
 drivers/xshm/xshm_dev.c           |  468 ++++++++++++++
 include/linux/Kbuild              |    1 +
 include/linux/c2c_genio.h         |  195 ++++++
 include/linux/xshm/Kbuild         |    1 +
 include/linux/xshm/xshm_ipctoc.h  |  160 +++++
 include/linux/xshm/xshm_netlink.h |   95 +++
 include/linux/xshm/xshm_pdev.h    |  188 ++++++
 17 files changed, 4594 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/caif/caif_xshm.c
 create mode 100644 drivers/xshm/Kconfig
 create mode 100644 drivers/xshm/Makefile
 create mode 100644 drivers/xshm/genio_dummy.c
 create mode 100644 drivers/xshm/xshm_boot.c
 create mode 100644 drivers/xshm/xshm_chr.c
 create mode 100644 drivers/xshm/xshm_dev.c
 create mode 100644 include/linux/c2c_genio.h
 create mode 100644 include/linux/xshm/Kbuild
 create mode 100644 include/linux/xshm/xshm_ipctoc.h
 create mode 100644 include/linux/xshm/xshm_netlink.h
 create mode 100644 include/linux/xshm/xshm_pdev.h


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 1/9] xshm: Shared Memory layout for ST-E M7400 driver.
  2011-12-07  9:27 [PATCH 0/9] XSHM: Shared Memory Driver for ST-E Thor M7400 LTE modem Sjur Brændeland
@ 2011-12-07  9:28 ` Sjur Brændeland
  2011-12-07  9:28 ` [PATCH 2/9] xshm: Channel config definitions " Sjur Brændeland
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 26+ messages in thread
From: Sjur Brændeland @ 2011-12-07  9:28 UTC (permalink / raw)
  To: linux-kernel; +Cc: Linus Walleij, Paul Bolle, Sjur Brændeland

Add structures defining the channel configuration and the
shared memory layout used for ST-Ericsson M7400.

These data structures are shared between the Linux host and the
M7400 modem.

Signed-off-by: Sjur Brændeland <sjur.brandeland@stericsson.com>
---
 include/linux/xshm/xshm_ipctoc.h |  160 ++++++++++++++++++++++++++++++++++++++
 1 files changed, 160 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/xshm/xshm_ipctoc.h

diff --git a/include/linux/xshm/xshm_ipctoc.h b/include/linux/xshm/xshm_ipctoc.h
new file mode 100644
index 0000000..73ea25b
--- /dev/null
+++ b/include/linux/xshm/xshm_ipctoc.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland / sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef XSHM_TOC
+#define XSHM_TOC
+
+/**
+ * DOC: XSHM Shared Memory Layout
+ *
+ * XSHM defines a set of structures describing the memory layout used
+ * for the Shared Memory IPC. In short &toc_entry points out &ipc_toc,
+ * which points out the &xshm_ipctoc_channel. &xshm_ipctoc_channel defines
+ * the channels used to communicate between host and external device (modem).
+ *
+ *  &xshm_ipctoc_channel can be used in packet-mode or stream-mode,
+ *  and points out &xshm_bufidx holding information about cirular
+ *  buffers, andtheir read/write indices etc.
+ */
+
+#pragma pack(1)
+struct _xshm_offsets {
+	__le32 rx;
+	__le32 tx;
+};
+
+/**
+ * struct xshm_ipctoc - Table Of Content definition for IPC.
+ *
+ * @magic:	Magic shall always be set to Ascii coded string "TC" (2 bytes)
+ * @version:	Main version of the TOC header.
+ * @subver:	Sub version of the TOC header.
+ * @channel_offsets: Offset to both rx and tx direction must be set.
+ *			The array must be terminated by a zero value.
+ *
+ * This struct is stored at the start of the External Shared memory, and
+ * serves as a extended table of contents defining the channel configurations
+ * for the external shared memory protocol between a modem and host.
+ *
+ * This extended table of content (ipctoc) is written to a predefine memory
+ * location and the modem will read this ipctoc during start-up and use this
+ * for setting up the IPC channels and it's buffers.
+ *
+ */
+
+struct xshm_ipctoc {
+	__u8 magic[2];
+	__u8 version;
+	__u8 subver;
+	struct _xshm_offsets channel_offsets[8];
+};
+#define XSHM_PACKET_MODE 0x1
+#define XSHM_STREAM_MODE 0x2
+#define XSHM_LOOP_MODE	 0x4
+#define XSHM_PAIR_MODE	 0x8
+#define XSHM_MODE_MASK	 0x3
+
+#define XSHM_IPCTOC_MAGIC1 'T'
+#define XSHM_IPCTOC_MAGIC2 'C'
+
+/**
+ * struct xshm_ipctoc_channel - Channel descriptor for External Shared memory.
+ *
+ * @offset: Relative address to channel data area.
+ * @size: Total size of a SHM channel area partition.
+ * @mtu: Maximum Transfer Unit for packets in a buffer (packet mode).
+ * @packets: Maximum Number of packets in a buffer (packet mode).
+ * @mode: Mode of channel: Packet mode=1, Stream mode (shm_channel_mode = 2).
+ * @buffers: Number of buffers for the channel.
+ * @ipc: Offset to IPC message location (of type struct xshm_bufidx).
+ * @read_bit: GENI/O bit used to indicate update of the read pointer for
+ *	this channel (at offset ipc).
+ * @write_bit: GENI/O bit used to indicate update of the write pointer for
+ *	this channel (at offset ipc).
+ * @alignment: Protocol specific options for the protocol,
+ *	e.g. packet alignment.
+ *
+ * This struct defines the channel configuration for a single direction.
+ *
+ * This structure is pointed out by the &xshm_toc and is written by
+ * host during start-up and read by modem at firmware boot.
+ *
+ */
+
+struct xshm_ipctoc_channel {
+	__le32 offset;
+	__le32 size;
+/* private: */
+	__u8 unused[3];
+/* public: */
+	__u8 mode;
+	__le32 buffers;
+	__le32 ipc;
+	__le16 write_bit;
+	__le16 read_bit;
+	__u16 mtu;
+	__u8 packets;
+	__u8 alignment;
+};
+
+/**
+ * struct xshm_bufidx - Indices's for a uni-directional xshm channel.
+ *
+ * @read_index: Specify the read index for a channel. This field can
+ *	have value in range of [0.. xshm_ipctoc_channel.buffers -1].
+ *	In stream mode - this is the read index in the ringbuffer.
+ *	In packet mode - this index will at any time refer to the next
+ *	buffer available for read.
+ *
+ * @write_index: Specify the write index for a channel.
+ *	This field can have value in range of [0.. buffers -1].
+ *	In stream mode - this is the write index in the ringbuffer.
+ *	In packet mode - this index will at any time refer to the next
+ *	buffer available for write.
+ *
+ * @size: The actual number of bytes for a buffer at each index.
+ *	  This array has xshm_ipctoc_channel.buffers slots, one for each buffer.
+ *	  The size is updated every time data is written to the buffer.
+ *
+ * @state: The state of the channel, 0 - Closed, 1 - Open
+ *
+ *
+ * This structure contains data for the ring-buffer used in packet and stream
+ * mode, for the external shared memory protocol.
+ * Note that the read_buf_index and the write_buf_index
+ * refer to two different channels. So for a ring buffer used to communicate
+ * from modem, the modem will update the write_buf_index while Linux host
+ * will update read_buf_index.
+ */
+struct xshm_bufidx {
+	__le32 state;
+	__le32 read_index;
+	__le32 write_index;
+	__le32 size[0];
+};
+
+/** struct toc_entry - Points out the boot imiages
+ *
+ * @start: Offset counting from start of memory area to the image data.
+ * @size:  Size of the images in bytes.
+ * @flags: Use 0 if no flags are in use.
+ * @entry: Where to jump to start exeuting. Only applicable
+ *		when using SDRAM. Set to 0xffffffff if unused.
+ * @load_addr: Location in SDRAM to move image. Set to 0xffffffff if
+ *		not applicable.
+ * @name: Name of image.
+ */
+struct toc_entry {
+	__le32 start;
+	__le32 size;
+	__le32 flags;
+	__le32 entry_point;
+	__le32 load_addr;
+	char name[12];
+};
+#pragma pack()
+
+#endif
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 2/9] xshm: Channel config definitions for ST-E M7400 driver.
  2011-12-07  9:27 [PATCH 0/9] XSHM: Shared Memory Driver for ST-E Thor M7400 LTE modem Sjur Brændeland
  2011-12-07  9:28 ` [PATCH 1/9] xshm: Shared Memory layout for ST-E M7400 driver Sjur Brændeland
@ 2011-12-07  9:28 ` Sjur Brændeland
  2011-12-07  9:28 ` [PATCH 3/9] xshm: Config data use for platform devices Sjur Brændeland
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 26+ messages in thread
From: Sjur Brændeland @ 2011-12-07  9:28 UTC (permalink / raw)
  To: linux-kernel; +Cc: Linus Walleij, Paul Bolle, Sjur Brændeland

Netlink parameters used for configuring channels from user space.

Signed-off-by: Sjur Brændeland <sjur.brandeland@stericsson.com>
---
 include/linux/Kbuild              |    1 +
 include/linux/xshm/Kbuild         |    1 +
 include/linux/xshm/xshm_netlink.h |   95 +++++++++++++++++++++++++++++++++++++
 3 files changed, 97 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/xshm/Kbuild
 create mode 100644 include/linux/xshm/xshm_netlink.h

diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index f6ae2fa..504e113 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -18,6 +18,7 @@ header-y += netfilter_bridge/
 header-y += netfilter_ipv4/
 header-y += netfilter_ipv6/
 header-y += usb/
+header-y += xshm/
 header-y += wimax/
 
 objhdr-y += version.h
diff --git a/include/linux/xshm/Kbuild b/include/linux/xshm/Kbuild
new file mode 100644
index 0000000..4315a21
--- /dev/null
+++ b/include/linux/xshm/Kbuild
@@ -0,0 +1 @@
+header-y += xshm_netlink.h
diff --git a/include/linux/xshm/xshm_netlink.h b/include/linux/xshm/xshm_netlink.h
new file mode 100644
index 0000000..80b01de
--- /dev/null
+++ b/include/linux/xshm/xshm_netlink.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2011
+ * Author: Sjur Brendeland / sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef XSHM_NL_H_
+#define XSHM_NL_H_
+
+#define XSHM_PROTO_VERSION 1
+#define XSHM_PROTO_SUB_VERSION 0
+#define XSHM_NETLINK_VERSION 1
+/**
+ * enum XSHM_COMMANDS - Attributes used for configuring XSHM device.
+ *
+ * @XSHM_C_ADD_STREAM_CHANNEL: Adds a Stream Channel.
+ * This will cause an instance of XSHM-CHR to be created.
+ * @XSHM_C_ADD_PACKET_CHANNEL: Adds a Packet Channel.
+ * This will cause an instance of CAIF-SHM to be created.
+ * @XSHM_C_COMMIT: formats and write Channel configuration data to
+ *	Shared Memory.
+ * @XSHM_C_SET_ADDR: Writes the TOC address to GENO register.
+ * @XSHM_C_REGISTER:  Initiates registration of the channel devices.
+ *	This will cause xshm - character devices or
+ *	CAIF network instances to be created.
+ * @XSHM_C_RESET:  Reset the configuration data and removes the
+ *	platform devices and their associated channel configuration.
+ *	ipc_ready and caif_ready is set to false.
+ *
+ * A normal sequence of events is: [XSHM_C_RESET], [XSHM_C_ADD_X_CHANNEL],
+ *	XSHM_C_COMMIT, XSHM_C_REGISTER, XSHM_C_SET_ADDR.
+ */
+enum XSHM_COMMANDS {
+	XSHM_C_ADD_STREAM_CHANNEL = 1,
+	XSHM_C_ADD_PACKET_CHANNEL,
+	XSHM_C_RESET,
+	XSHM_C_SET_ADDR,
+	XSHM_C_COMMIT,
+	XSHM_C_REGISTER,
+	__XSHM_C_VERIFY,
+	__XSHM_C_MAX
+};
+
+/**
+ * enum XSHM_ATTRIBUTES - Attributes used for configuring XSHM device.
+ * @XSHM_A_VERSION: Version of XSHM netlink protocol. Type NLA_U8
+ * @XSHM_A_SUB_VERSION: Sub-version of XSHM netlink protocol. Type NLA_U8
+ * @XSHM_A_NAME: Name of the channel, max 15 characters. Type NLA_NUL_STRING
+ * @XSHM_A_EXCL_GROUP: Devices may be part of a group. Devices from the
+ *	same group are allowed to be open simultaneously,
+ *	but devices from different groups cannot be opened
+ *	at the same time. Type NLA_U8.
+ * @XSHM_A_RX_CHANNEL: The RX direction attributes. Type NLA_NESTED.
+ *	Each channel may contain the attributes - XSHM_A_CHANNEL_SIZE,
+ *	XSHM_A_CHANNEL_BUFFERS, XSHM_A_ALIGNMENT, XSHM_A_MTU.
+ *
+ * @XSHM_A_TX_CHANNEL: The TX direction attributes. Type NLA_NESTED.
+ *
+ * @XSHM_A_CHANNEL_SIZE: Size of the data area for a channel. Specified
+ *	for RX, TX. Type NLA_U32,
+ * @XSHM_A_CHANNEL_BUFFERS: Numer of buffers for a packet channel.
+ *	This attribute is only used for packet channels.  Specified for RX, TX.
+ *	Type NLA_U32,
+ * @XSHM_A_ALIGNMENT: Alignment for each packet in a buffer. This attribute
+ *	 is only used for packet channels.  Specified for RX, TX.Type NLA_U8,
+ * @XSHM_A_MTU: Maximum Transfer Unit for packets in a buffer.
+ *	This is only appplicable for packet channels.
+ *	Specified for RX, TX.Type NLA_U16,
+ * @XSHM_A_PACKETS: Maximum number of packets in a buffer. Type NLA_U8
+ * @XSHM_A_PRIORITY: Priority of the channel, legal range is 0-7 where
+ *	0 is lowest priority. Type NLA_U8.
+ * @XSHM_A_LATENCY: Latency for channel, value:0 means low latency
+ *	 and low bandwidth,
+ *	 value 1 means high latency and high bandwidth. Type NLA_U8.
+ */
+enum XSHM_ATTRIBUTES {
+	__XSHM_A_FLAGS = 1,		/* Test flags: NLA_U32 */
+	XSHM_A_VERSION,
+	XSHM_A_SUB_VERSION,
+	XSHM_A_NAME,
+	XSHM_A_EXCL_GROUP,
+	XSHM_A_RX_CHANNEL,
+	XSHM_A_TX_CHANNEL,
+	XSHM_A_CHANNEL_SIZE,
+	XSHM_A_CHANNEL_BUFFERS,
+	XSHM_A_ALIGNMENT,
+	XSHM_A_MTU,
+	XSHM_A_PACKETS,
+	XSHM_A_PRIORITY,
+	XSHM_A_LATENCY,
+	__XSHM_A_MAX,
+};
+#define XSHM_A_MAX (__XSHM_A_MAX - 1)
+
+#endif /* XSHM_NL_H_ */
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 3/9] xshm: Config data use for platform devices.
  2011-12-07  9:27 [PATCH 0/9] XSHM: Shared Memory Driver for ST-E Thor M7400 LTE modem Sjur Brændeland
  2011-12-07  9:28 ` [PATCH 1/9] xshm: Shared Memory layout for ST-E M7400 driver Sjur Brændeland
  2011-12-07  9:28 ` [PATCH 2/9] xshm: Channel config definitions " Sjur Brændeland
@ 2011-12-07  9:28 ` Sjur Brændeland
  2011-12-07  9:28 ` [PATCH 4/9] xshm: geni/geno driver interface Sjur Brændeland
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 26+ messages in thread
From: Sjur Brændeland @ 2011-12-07  9:28 UTC (permalink / raw)
  To: linux-kernel; +Cc: Linus Walleij, Paul Bolle, Sjur Brændeland

This patch defines the configuration data needed to operate
on a channel. Each channel is represented as a platform device
containing the necessary configuration data to operate on the channel.

Signed-off-by: Sjur Brændeland <sjur.brandeland@stericsson.com>
---
 include/linux/xshm/xshm_pdev.h |  188 ++++++++++++++++++++++++++++++++++++++++
 1 files changed, 188 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/xshm/xshm_pdev.h

diff --git a/include/linux/xshm/xshm_pdev.h b/include/linux/xshm/xshm_pdev.h
new file mode 100644
index 0000000..0c223fb
--- /dev/null
+++ b/include/linux/xshm/xshm_pdev.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland / sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef XSHM_PDEV_H_
+#define XSHM_PDEV_H_
+#include <linux/platform_device.h>
+
+#define XSHM_NAMESZ 16
+
+/**
+ * struct xshm_udchannel - Unidirectional channel for xshm driver.
+ *
+ * @addr: Base address of the channel, address must be
+ *		  a kernel logical address.
+ * @buffers: The number of buffers in the channel.
+ * @ch_size: The size of data area for the channel in one direction.
+ * @xfer_bit: GENI/O bit used when sending data (write pointer move)
+ * @xfer_done_bit: GENI/O bit used to indicate avilable buffers
+ *	(read pointer move).
+ * @alignment: Alignment used in payload protocol.
+ * @mtu: Maxium Transfer Unit used for packet in a buffer (Packet mode).
+ * @packets: Maxium number of packets in a buffer (Packet mode).
+ * @state: State of the device 0 - Closed, 1 - Open
+ * @read: Specify the read index for a channel. In packed mode
+ *	this index will at any time refer to the next buffer available for read.
+ *	In stream mode, this will be the read index in the ring-buffer.
+ * @write: Specify the write index for a channel. In packed mode
+ *	this index will at any time refer to the next buffer available for
+ *	write. In stream mode, this will be the write index in the ring-buffer.
+ * @buf_size: In packet mode, this array contains the size of each buffer.
+ *	In stream mode this is unused.
+ *
+ * This external shared memory channel configuration is exported from the
+ * platform device. It gives the platform driver the
+ * necessary information for running the shared memory protocol
+ * between modem and host.
+ *
+ * Note that two instances of this configuration is needed in order to run a
+ * bi-directional channel.
+ */
+struct xshm_udchannel {
+	void *addr;
+	u32 buffers;
+	u32 ch_size;
+	u8 xfer_done_bit;
+	u8 xfer_bit;
+	u32 mtu;
+	u32 alignment;
+	u32 packets;
+	__le32 *state;
+	__le32 *read;
+	__le32 *write;
+	__le32 *buf_size;
+/* private: */
+	struct kobject kobj; /* kobj must be located at the end */
+};
+
+/**
+ * struct xshm_channel - Channel definition for xshm driver.
+ * @rx: Configuration for RX channel
+ * @tx: Configuration for TX channel
+ * @excl_group: Only channels with the same group ID can be open simultaneously.
+ * @mode: Configuring type of channel PACKET(1), STREAM(2)
+ * @name: Name of interface.
+ * @priority: Priority of the channel.
+ * @latency: Latency of the channel.
+ */
+struct xshm_channel {
+	struct xshm_udchannel rx, tx;
+	u32 excl_group;
+	u32 mode;
+	char name[XSHM_NAMESZ];
+	u32 priority;
+	u32 latency;
+};
+
+#define XSHM_OPEN   1
+#define XSHM_CLOSED 0
+
+enum xshm_dev_state {
+	XSHM_DEV_CLOSED = 0,
+	XSHM_DEV_OPENING,
+	XSHM_DEV_OPEN,
+	XSHM_DEV_ACTIVE,
+};
+
+/**
+ * struct xshm_dev - Device definition for xshm platform device.
+ *
+ * @pdev: Platform device
+ * @cfg: Configuration for the Channel
+ * @state: State of the device: Closed - No user space client is using it,
+ *	Open - Open but no payload queued, Active - Payload queued on device.
+ *
+ * @open: The driver calls open() when channel is taken into use.
+ *	This function will fail if channel configuration is inconsistent,
+ *	or upon resource conflicts with other channels.
+ *
+ * @open_cb: The device calls open_cb() when is ready for use.
+ *
+ * @close: Called by the driver when a channel is no longer in use.
+ *
+ * @close_cb: The device calls close_cb() to notify about remote side closure.
+ *
+ * @ipc_tx_release_cb: This callback is triggered by the modem when a
+ *	transmit operation has completed and the buffer can be reused.
+ *	This function must be set by the driver upon device registration.
+ *	The "more" flag is set if ipc_rx_cb() call is coming immediately
+ *	after this call to ipc_tx_release_cb().
+ *
+ * @ipc_rx_cb: The driver gets this callback when the modem sends a buffer
+ *	from the modem. The driver must call ipc_rx_release()
+ *	to make the buffer available again when the received buffer has been
+ *	processed.
+ *	This function pointer must be set by the driver upon device
+ *	registration.
+ *
+ * @ipc_rx_release: Called by the driver when a RX operation has completed
+ *	and that the rx-buffer is released.
+ *
+ * @ipc_tx: Called by the driver when a TX buffer shall be sent to the modem.
+ *
+ * @driver_data: pointer to driver specific data.
+ *
+ * When communicating between two systems (e.g. modem and host),
+ * external shared memory can bused (e.g. C2C or DPRAM).
+ *
+ * This structure is used by the platform device representing the
+ * External Shared Memory.
+ *
+ * The this structure contains configuration data for the platform device and
+ * functions pointers for IPC communication between Linux host and modem.
+ * The external shared memory protocol memory e.g. C2C or DPRAM
+ * together is a IPC mechanism for transporting small commands such as
+ * Mailbox or GENI/O.
+ *
+ * This data structure is initiated by the xshm platform device, except
+ * for the functions ipc_rx_cb() and ipc_tx_release_cb(). They must be set by
+ * the platform driver when device is registering.
+ */
+
+struct xshm_dev {
+	struct platform_device pdev;
+	struct xshm_channel cfg;
+	enum xshm_dev_state state;
+	int (*open)(struct xshm_dev *dev);
+	void (*close)(struct xshm_dev *dev);
+	int (*ipc_rx_release)(struct xshm_dev *dev, bool more);
+	int (*ipc_tx)(struct xshm_dev *dev);
+	int (*open_cb)(void *drv);
+	void (*close_cb)(void *drv);
+	int (*ipc_rx_cb)(void *drv);
+	int (*ipc_tx_release_cb)(void *drv);
+	void *driver_data;
+	/* private: */
+	struct list_head node;
+	void *priv;
+};
+
+/**
+ * xshm_create_dev() - Create an instance of the xshm platform device.
+ * @shmdev: Device configuration data.
+ */
+int xshm_register_dev(struct xshm_dev *shmdev);
+
+/** xshm_ipc_ready() - Notify that GENO bit READY_FOR_IPC is set
+*/
+void xshm_ipc_ready(void);
+
+/** xshm_caif_ready() - Notify that GENO bit READY_FOR_CAIF is set
+*/
+void xshm_caif_ready(void);
+
+extern bool ready_for_ipc;
+extern bool ready_for_caif;
+void xshm_put(struct xshm_dev *shmdev);
+struct xshm_dev *xshmdev_alloc(void);
+void xshmdev_free(struct xshm_dev *dev);
+void remove_devices(void);
+void close_devices(void);
+void xshm_boot_exit(void);
+int xshm_boot_init(void);
+void xshm_reset(void);
+void genio_ipc_ready_cb(void);
+#endif
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 4/9] xshm: geni/geno driver interface.
  2011-12-07  9:27 [PATCH 0/9] XSHM: Shared Memory Driver for ST-E Thor M7400 LTE modem Sjur Brændeland
                   ` (2 preceding siblings ...)
  2011-12-07  9:28 ` [PATCH 3/9] xshm: Config data use for platform devices Sjur Brændeland
@ 2011-12-07  9:28 ` Sjur Brændeland
  2011-12-07  9:28 ` [PATCH 5/9] xshm: genio dummy driver Sjur Brændeland
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 26+ messages in thread
From: Sjur Brændeland @ 2011-12-07  9:28 UTC (permalink / raw)
  To: linux-kernel; +Cc: Linus Walleij, Paul Bolle, Sjur Brændeland

The Shared Memory driver is using two paired registers called
GENI and GENO in for triggering events/interrupts on modem and Linux host.

It's primary use is notification about read and write indexes changes
on each channel. But GENI/GENO are also used for communicating the
address of the shared memory upon modem boot and to notify about start-up
event such as when IPC channels and CAIF is ready for use.

Signed-off-by: Sjur Brændeland <sjur.brandeland@stericsson.com>
---
 include/linux/c2c_genio.h |  195 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 195 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/c2c_genio.h

diff --git a/include/linux/c2c_genio.h b/include/linux/c2c_genio.h
new file mode 100644
index 0000000..e7a846e
--- /dev/null
+++ b/include/linux/c2c_genio.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2011
+ * Author: Sjur Brendeland / sjur.brandeland@stericsson.com
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef __INC_GENIO_H
+#define __INC_GENIO_H
+#include <linux/types.h>
+
+/**
+ * DOC: C2C GENI/GENO interface.
+ *
+ * This API defines the API for the C2C driver and the GENI/GENO registers.
+ */
+
+/**
+ * enum GENIO_BITS - Definition of special GENIO BITS.
+ * @READY_FOR_IPC_BIT:  Remote side is ready for IPC.
+ * This GENI/GENO bit is triggered when ring-buffer protocol
+ * is enabled from the remote end (modem)
+ *
+ * @READY_FOR_CAIF_BIT: Remote side is ready for CAIF.
+ * This GENI/GENO bit is triggered when CAIF protocol
+ * is enabled from the remote end (modem)
+ */
+enum GENIO_BITS {
+	READY_FOR_CAIF_BIT = 28,
+	READY_FOR_IPC_BIT = 29
+};
+
+/**
+ * genio_subscribe - Subscribe for notifications on bit-change of GENI/O bits.
+ *
+ * @bit:	The GENI/O bit where we want to be called back when it changes.
+ *
+ * @bit_set_cb:	Callback function to be called when the requested GENI/O bit
+ *		is set by the external device (modem).
+ *
+ * @data: Client data to be provided in the callback function.
+ *
+ * Install a callback function for a GENI/O bit. Returns negative upon error.
+ *
+ * The genio driver is expected to handle the 4-state handshake for geni/geno
+ * update, for the bit this function subscribe to. This function may block,
+ * and cannot be called from IRQ context.
+ *
+ * Returns zero on success, and negative upon error.
+ *
+ * Precondition: This function is called after genio_set_shm_addr() and
+ * genio_bit_alloc(). @bit must be defined as as getter in genio_bit_alloc().
+ *
+ * Callback context:
+ *		The "bit_set_cb" callback must be called from the
+ *		IRQ context. The callback function is not allowed to block
+ *		or spend much CPU time in the callback, It must defer
+ *		work to soft-IRQ or work queues.
+ */
+int genio_subscribe(int bit, void (*bit_set_cb)(void *data), void *data);
+
+/**
+ * genio_unsubscribe - Unsubscribe for callback  GENI/O bit.
+ * @bit:       The GENI/O bit to we want release.
+ *
+ * This function may block. It returns zero on success, and negative upon error.
+ *
+ * Precondition: @bit must be defined as as getter in genio_bit_alloc().
+ */
+int genio_unsubscribe(int bit);
+
+/**
+ * genio_bit_alloc - Allocate the usage of GENI/O bits.
+ *
+ * @setter_mask:	Bit-mask defining the bits that can be set by
+ *			genio_set_bit()
+ * @getter_mask:	Bit-mask defining the bits that can be subscribed by
+ *			genio_subscribe().
+ *
+ * The @getter_mask defines the bit for RX direction, i.e. bits that can
+ * be subscribed by the function genio_subscribe().
+ * The @setter_mask defines the bit for TX direction, i.e. bits that can
+ * be set by the function genio_set_bit().
+ * This function may block.
+ *
+ * Returns zero on success, and negative upon error.
+ *
+ * Precondition:
+ * This function cannot be called before ipc_ready_cb() has been called,
+ * and must be called prior to any call on genio_subscribe() or genio_set_bit().
+ *
+ */
+int genio_bit_alloc(u32 setter_mask, u32 getter_mask);
+
+/**
+ * genio_set_shm_addr - Inform remote device about the shared memory address.
+ * @addr:		The shared memory address.
+ * @ipc_ready_cb:	The callback function indicating IPC is ready.
+ *
+ * Description:
+ * Implements the setting of shared memory address involving the
+ * READY_FOR_IPC GENIO bits, and READY_FOR_IPC handshaking.
+ * When handshaking is done ipc_ready_cb is called.
+ * The usage of this bit is during start/restart.
+ * This function may block.
+ *
+ * When the ipc_ready_cb() with @ready=%true, Stream channels can be opened.
+ *
+ * Sequence:
+ * (1) Write the address to the GENO register,
+ *
+ * (2) wait for interrupt on IPC_READY (set to one in GENI register)
+ *
+ * (3) write zero to the GENO register
+ *
+ * (4) wait for interrupt on IPC_READY (set to zero)
+ *
+ * (5) call the callback function for IPC_READY
+ *
+ * Returns zero on success, and negative upon error.
+ *
+ * Precondition:
+ * This function must be called initially upon start, or after remote device
+ * has been reset.
+ */
+int genio_set_shm_addr(u32 addr, void (*ipc_ready_cb) (void));
+
+/**
+ * genio_subscribe_caif_ready - Notify that CAIF channels can be
+ * opened.
+ *
+ * @caif_ready_cb: is called with @ready = %true in the start up sequence,
+ * when the remote side is ready for CAIF enumeration. Upon reset,
+ * caif_ready_cb() will be called with @ready = %false.
+ *
+ * The %READY_FOR_CAIF_BIT is set to %one as long as the
+ * modem is able to run CAIF traffic. Upon modem restart/crash it will
+ * be set back to %zero.
+ * This function may block.
+ *
+ * Returns zero on success, and negative upon error.
+ */
+int genio_subscribe_caif_ready(void (*caif_ready_cb) (bool ready));
+
+/**
+ * genio_register_errhandler - Register an error handler.
+ *
+ * @errhandler: error handler called from driver upon severe errors
+ *		that requires reset of the remote device.
+ */
+void genio_register_errhandler(void (*errhandler)(int errno));
+
+/**
+ * genio_reset() - Reset the C2C driver
+ *
+ * Reset the C2C Driver due to remote device restart.
+ * This shall reset state back to initial state, and should only
+ * be used when remote device (modem) has reset.
+ *
+ * All settings, subscriptions and state information in the driver must
+ * be reset. GENIO client must do all subscriptions again.
+ * This function may block.
+ *
+ * Returns zero on success, and negative upon error.
+ */
+int genio_reset(void);
+
+/**
+ * genio_set_bit() -	Set a single GENI/O bit.
+ *
+ * @bit:	The GENI/O bit to set
+ *
+ * This function is used to signal over GENI/GENO the driver must
+ * perform the 4-state protocol to signal the change to remote device.
+ * This function is non-blocking, and can be called from Soft-IRQ context.
+ *
+ * Returns zero on success, and negative upon error.
+ *
+ * Precondition: @bit must be defined as as setter in genio_bit_alloc().
+ */
+int genio_set_bit(int bit);
+
+/**
+ * genio_power_req() -	Request power on on remote C2C block.
+ *
+ * @state:	1 - power-on request , 0 - power-off request
+ *
+ * This function will request power on of remote C2C block.
+ * This function is non-blocking, and can be called from Soft-IRQ context.
+ *
+ * Returns zero on success, and negative upon error.
+ */
+int genio_power_req(int state);
+
+#endif /*INC_GENIO_H*/
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 5/9] xshm: genio dummy driver
  2011-12-07  9:27 [PATCH 0/9] XSHM: Shared Memory Driver for ST-E Thor M7400 LTE modem Sjur Brændeland
                   ` (3 preceding siblings ...)
  2011-12-07  9:28 ` [PATCH 4/9] xshm: geni/geno driver interface Sjur Brændeland
@ 2011-12-07  9:28 ` Sjur Brændeland
  2011-12-07  9:28 ` [PATCH 6/9] xshm: Platform device for XSHM Sjur Brændeland
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 26+ messages in thread
From: Sjur Brændeland @ 2011-12-07  9:28 UTC (permalink / raw)
  To: linux-kernel; +Cc: Linus Walleij, Paul Bolle, Sjur Brændeland

Dummy driver for genio. A proper driver for Nova A9540 will be
contributed at a later stage.

Signed-off-by: Sjur Brændeland <sjur.brandeland@stericsson.com>
---
 drivers/xshm/genio_dummy.c |   61 ++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 61 insertions(+), 0 deletions(-)
 create mode 100644 drivers/xshm/genio_dummy.c

diff --git a/drivers/xshm/genio_dummy.c b/drivers/xshm/genio_dummy.c
new file mode 100644
index 0000000..d359ee6
--- /dev/null
+++ b/drivers/xshm/genio_dummy.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author:	Sjur Brendeland / sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+/* This is a dummy implementation of GENIO */
+
+#include <linux/c2c_genio.h>
+int genio_subscribe(int bit, void (*bit_set_cb)(void *data), void *data)
+{
+	return 0;
+}
+
+int genio_unsubscribe(int bit)
+{
+	return 0;
+}
+
+int genio_set_bit(int bit)
+{
+	return 0;
+}
+
+int genio_init(void)
+{
+	return 0;
+}
+
+void genio_exit(void)
+{
+}
+
+int genio_reset(void)
+{
+	return 0;
+}
+
+int genio_subscribe_caif_ready(void (*caif_ready_cb) (bool ready))
+{
+	return 0;
+}
+
+int genio_set_shm_addr(u32 addr, void (*ipc_ready_cb) (void))
+{
+	return 0;
+}
+
+int genio_bit_alloc(u32 setter_mask, u32 getter_mask)
+{
+	return 0;
+}
+
+void genio_register_errhandler(void (*errhandler)(int errno))
+{
+}
+
+int genio_power_req(int state)
+{
+	return 0;
+}
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 6/9] xshm: Platform device for XSHM
  2011-12-07  9:27 [PATCH 0/9] XSHM: Shared Memory Driver for ST-E Thor M7400 LTE modem Sjur Brændeland
                   ` (4 preceding siblings ...)
  2011-12-07  9:28 ` [PATCH 5/9] xshm: genio dummy driver Sjur Brændeland
@ 2011-12-07  9:28 ` Sjur Brændeland
  2011-12-07  9:28 ` [PATCH 7/9] xshm: Character device for XSHM channel access Sjur Brændeland
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 26+ messages in thread
From: Sjur Brændeland @ 2011-12-07  9:28 UTC (permalink / raw)
  To: linux-kernel; +Cc: Linus Walleij, Paul Bolle, Sjur Brændeland

This patch implements the configuration handling and interface to XSHM
drivers. Specifically:
- reception of gen-netlink requests from user-space.
- calculating address of where to put information in shared memory, i.e
  the offset of: IPC-TOC, channel-descriptors, read/write indexes for each
  directional channel, and data area of each channel.
- formatting of the shared memory area, so modem can read out configuration
  data.
- Creation of configuration data given to the XSHM drivers: xshm_chr and
  caif_xshm.
- Platform device management.
- Channel management such as open/close.
- Modem start-up synchronization events.
- C2C power request indications, requesting power-on when transmit is ongoing,
  and power-off upon inactivity timeout.

Signed-off-by: Sjur Brændeland <sjur.brandeland@stericsson.com>
---
 drivers/xshm/xshm_boot.c | 1187 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/xshm/xshm_dev.c  |  468 ++++++++++++++++++
 2 files changed, 1655 insertions(+), 0 deletions(-)
 create mode 100644 drivers/xshm/xshm_boot.c
 create mode 100644 drivers/xshm/xshm_dev.c

diff --git a/drivers/xshm/xshm_boot.c b/drivers/xshm/xshm_boot.c
new file mode 100644
index 0000000..e56d779
--- /dev/null
+++ b/drivers/xshm/xshm_boot.c
@@ -0,0 +1,1187 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author:	Sjur Brændeland / sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": %s :" fmt, __func__
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kobject.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/crc-ccitt.h>
+#include <linux/kdev_t.h>
+#include <net/netlink.h>
+#include <net/genetlink.h>
+#include <linux/xshm/xshm_ipctoc.h>
+#include <linux/xshm/xshm_pdev.h>
+#include <linux/xshm/xshm_netlink.h>
+#include <linux/c2c_genio.h>
+
+#define XSHM_VERSION	0x1
+#define XSHM_SUBVER	0x0
+#define TOC_SZ		512
+#define IMG_MAX_SZ	65536
+#define XSHM_ALIGNMT	sizeof(u32)
+#define XSHM_MAX_CHANNELS 7
+#define XSHM_MIN_CHSZ 3
+#define XSHM_PAYL_ALIGN max(32, L1_CACHE_BYTES)
+
+#define GET_OFFSET(base, ptr) (((u8 *)(ptr)) - ((u8 *)(base)))
+#define OFFS2PTR(base, offs) ((void *) ((u8 *)base + offs))
+#define LEOFFS2PTR(base, offs) ((void *) ((u8 *)base + le32_to_cpu(offs)))
+
+/* Structure use in debug mode for integrity checking */
+struct ipctoc_hash {
+	u16 img_hash;
+	u16 ch_hash;
+	u16 ch_size;
+};
+
+static bool config_error;
+static bool commited;
+static bool registered;
+static bool addr_set;
+static u32 modem_bootimg_size;
+static void *shm_start;
+static u32 xshm_channels;
+static struct xshm_dev *xshmdevs[XSHM_MAX_CHANNELS + 1];
+static struct xshm_ipctoc *ipctoc;
+static struct device _parentdev;
+static struct device *parentdev;
+
+static unsigned long xshm_start;
+module_param(xshm_start, ulong, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(xshm_start, "Address for memory shared by host/modem.");
+
+static unsigned long xshm_c2c_bootaddr;
+module_param(xshm_c2c_bootaddr, ulong, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(xshm_c2c_bootaddr, "Address given to modem (through GENI register)");
+
+static long xshm_size;
+module_param(xshm_size, long, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(xshm_size, "Size of SHM area");
+
+#ifdef DEBUG
+
+/* In debug mode we pad around all payload area in order to detect overwrite */
+#define MAGIC_PAD_LEN 32
+#define MAGIC_PAD 0xbc
+
+/* Verify a magic-pad area */
+static inline bool padok(void *mag)
+{
+	u32 *p = mag, v = 0xbcbcbcbc;
+	int i;
+
+	for (i = 0; i < 8; i++)
+		if (*p++ != v)
+			return false;
+	return true;
+}
+
+/* Insert a magic-pad area */
+static inline void add_magic_pad(u32 *offset, void *base)
+{
+	if (*offset < xshm_size)
+		memset(base + *offset, MAGIC_PAD, MAGIC_PAD_LEN);
+	*offset += MAGIC_PAD_LEN;
+}
+
+/* Abuse the pad area to create a checksum of the ipc-toc and descriptors */
+static inline void store_checksum(struct xshm_ipctoc *ipctoc, u32 size)
+{
+	struct ipctoc_hash *hash = (void *)ipctoc;
+	--hash;
+	hash->img_hash =
+		crc_ccitt(0xffff, (u8 *) shm_start, modem_bootimg_size);
+	hash->ch_hash = crc_ccitt(0xffff, (u8 *) ipctoc, size);
+	hash->ch_size = size;
+}
+
+/* Verify that shm config has not been accidently tampered. */
+static inline bool ok_checksum(struct xshm_ipctoc *ipctoc)
+{
+	struct ipctoc_hash *hash = (void *) ipctoc;
+	u16 new_hash, new_imghash;
+	int i;
+	u8 *p;
+
+	if (!commited)
+		return false;
+
+	for (i = 0; i < xshm_channels; i++) {
+		struct xshm_ipctoc_channel *ch;
+
+		ch = LEOFFS2PTR(shm_start, ipctoc->channel_offsets[i].rx);
+		p = LEOFFS2PTR(shm_start, ch->ipc);
+		if (!padok(p - MAGIC_PAD_LEN))
+			return false;
+		p = LEOFFS2PTR(shm_start, ch->offset);
+		if (!padok(p - MAGIC_PAD_LEN))
+			return false;
+		ch = LEOFFS2PTR(shm_start, ipctoc->channel_offsets[i].tx);
+		p = LEOFFS2PTR(shm_start, ch->ipc);
+		if (!padok(p - MAGIC_PAD_LEN))
+			return false;
+		p = LEOFFS2PTR(shm_start, ch->offset);
+		if (!padok(p - MAGIC_PAD_LEN))
+			return false;
+	}
+
+	--hash;
+	new_hash = crc_ccitt(0xffff, (u8 *) ipctoc, hash->ch_size);
+	new_imghash =
+		crc_ccitt(0xffff, (u8 *) shm_start, modem_bootimg_size);
+	pr_debug("Hash result:size:%d chksm:%u/%u img:%u/%u\n",
+			hash->ch_size, hash->ch_hash, new_hash,
+			hash->img_hash, new_imghash);
+	return hash->ch_hash == new_hash && hash->img_hash == new_imghash;
+}
+
+static inline void init_data(u32 offset, int ch, u32 size)
+{
+	memset((u8 *)shm_start + offset, ch + 1, size);
+}
+#else
+#define MAGIC_PAD_LEN 0
+static inline void add_magic_pad(u32 *offset, void *base)
+{
+}
+static inline void store_checksum(void *ipctoc, u32 size)
+{
+}
+static inline bool ok_checksum(void *ipctoc)
+{
+	return true;
+}
+static inline void init_data(u32 offs, int ch, u32 size)
+{
+}
+#endif
+
+/* write_to_shm - Write SHM Channel descriptors to SHM.
+ *
+ * Based on the configuration data channel configuration
+ * is written to the shared memory area.
+ * This is the data layout:
+ *
+ * +------------+  <---- xshm_start
+ * |	TOC	|
+ * +------------+
+ * | Boot IMG	|
+ * +------------+ <---- rw_start
+ * | RW Data	|
+ * +------------+
+ * | RW Buf idx |
+ * +------------+ <---- ipctoc
+ * | IPC TOC	|
+ * +------------+
+ * | RW Ch Decr |
+ * +------------+ <---- ro_start
+ * | RO Ch Decr |
+ * +------------+
+ * | RO Buf idx |
+ * +------------+
+ * | RO Data	|
+ * +------------+
+ */
+
+static int write_to_shm(void)
+{
+	int i, pri, bitno;
+	u32 offset, ro_start, rw_start, ipctoc_offs, ipcro_offs;
+	bool found;
+	struct xshm_ipctoc_channel *ch;
+	struct toc_entry *toc_entry;
+	struct xshm_bufidx *bix;
+
+	/*
+	 * Find where to put IPC-TOC by adding up
+	 * the size of Payload buffers pluss buf-indices
+	 */
+	ipctoc_offs = ALIGN(modem_bootimg_size, XSHM_PAYL_ALIGN);
+	rw_start = ipctoc_offs;
+	for (i = 0; i < xshm_channels; i++) {
+		int n = xshmdevs[i]->cfg.tx.buffers;
+		ipctoc_offs += MAGIC_PAD_LEN;
+		ipctoc_offs += offsetof(struct xshm_bufidx, size[n + 2]);
+		ipctoc_offs = ALIGN(ipctoc_offs, XSHM_PAYL_ALIGN);
+		ipctoc_offs += MAGIC_PAD_LEN;
+		ipctoc_offs += xshmdevs[i]->cfg.tx.ch_size;
+		ipctoc_offs = ALIGN(ipctoc_offs, XSHM_PAYL_ALIGN);
+	}
+	add_magic_pad(&ipctoc_offs, shm_start);
+	pr_debug("IPC toc @ %08x\n", ipctoc_offs);
+
+	/*
+	 * Allocate the IPC-TOC and, initiatlize it.
+	 * The IPC toc will be located after the RW Data and
+	 * buffer indices.
+	 */
+	offset = ipctoc_offs;
+	ipctoc = OFFS2PTR(shm_start, ipctoc_offs);
+	ipctoc->magic[0] = XSHM_IPCTOC_MAGIC1;
+	ipctoc->magic[1] = XSHM_IPCTOC_MAGIC2;
+	ipctoc->version = XSHM_VERSION;
+	ipctoc->subver = XSHM_SUBVER;
+	memset(ipctoc->channel_offsets, 0, sizeof(ipctoc->channel_offsets));
+
+	/* Find start of first channel descriptor */
+	offset += sizeof(struct xshm_ipctoc);
+
+	/*
+	 * Allocate the location for the RW Channel descriptors.
+	 * It will be located after the IPC-TOC.
+	 */
+	offset = ALIGN(offset, XSHM_ALIGNMT);
+	for (i = 0; i < xshm_channels; i++) {
+		pr_debug("Channel descriptor %d RW @ 0x%08x\n", i, offset);
+		ipctoc->channel_offsets[i].tx = cpu_to_le32(offset);
+		offset += sizeof(struct xshm_ipctoc_channel);
+		offset = ALIGN(offset, XSHM_ALIGNMT);
+		if (offset > xshm_size)
+			goto badsize;
+	}
+	ro_start = offset;
+
+	/*
+	 * Allocate the location for the RO Channel descriptors.
+	 * It will be located after the RW Channels.
+	 */
+	for (i = 0; i < xshm_channels; i++) {
+		pr_debug("Channel descriptor %d RO @ 0x%08x\n", i, offset);
+		ipctoc->channel_offsets[i].rx = cpu_to_le32(offset);
+		offset += sizeof(struct xshm_ipctoc_channel);
+		offset = ALIGN(offset, XSHM_ALIGNMT);
+		if (offset > xshm_size)
+			goto badsize;
+	}
+
+	/*
+	 * Allocate the location for the RO Buffer Indices.
+	 * It will be located after the RO Channels.
+	 */
+	offset = ALIGN(offset, XSHM_PAYL_ALIGN);
+	ipcro_offs = offset;
+	for (i = 0; i < xshm_channels; i++) {
+		int n = xshmdevs[i]->cfg.rx.buffers;
+		ch = LEOFFS2PTR(shm_start, ipctoc->channel_offsets[i].rx);
+		add_magic_pad(&offset, shm_start);
+		ch->ipc = cpu_to_le32(offset);
+
+		bix = OFFS2PTR(shm_start, offset);
+		bix->read_index = cpu_to_le32(0);
+		bix->write_index = cpu_to_le32(0);
+		bix->state = cpu_to_le32(XSHM_CLOSED);
+		bix->size[0] = cpu_to_le32(0);
+
+		pr_debug("IPC RO[%d] @: 0x%08x\n",  i, offset);
+		offset += offsetof(struct xshm_bufidx, size[n + 2]);
+		offset = ALIGN(offset, XSHM_PAYL_ALIGN);
+		if (offset > xshm_size)
+			goto badsize;
+	}
+
+	/*
+	 * Allocate RO Data Area. This will located after
+	 * the RO Buffer Indices at the end of the Share Memory
+	 * area.
+	 */
+	offset = ALIGN(offset, XSHM_PAYL_ALIGN);
+	for (i = 0; i < xshm_channels; i++) {
+		u8 align;
+		u32 size;
+		ch = LEOFFS2PTR(shm_start, ipctoc->channel_offsets[i].rx);
+		add_magic_pad(&offset, shm_start);
+		ch->offset = cpu_to_le32(offset);
+
+		BUILD_BUG_ON(sizeof(ch->mode) != 1);
+		ch->mode = xshmdevs[i]->cfg.mode & XSHM_MODE_MASK;
+		ch->buffers = cpu_to_le32(xshmdevs[i]->cfg.rx.buffers);
+		align = rounddown_pow_of_two(xshmdevs[i]->cfg.rx.alignment);
+		ch->alignment =	align;
+		ch->packets = xshmdevs[i]->cfg.rx.packets;
+		ch->mtu = xshmdevs[i]->cfg.rx.mtu;
+		size = xshmdevs[i]->cfg.tx.ch_size;
+		if (xshmdevs[i]->cfg.mode & XSHM_PACKET_MODE) {
+			u32 buf_size;
+			buf_size = size / xshmdevs[i]->cfg.tx.buffers;
+			buf_size = rounddown(buf_size, align);
+			size = buf_size * xshmdevs[i]->cfg.tx.buffers;
+		}
+		pr_debug("Buffer area RO for Channel[%d] at: 0x%08x size:%d\n",
+				i, offset, size);
+		ch->size = cpu_to_le32(size);
+
+		init_data(offset, i, xshmdevs[i]->cfg.rx.ch_size);
+		offset += xshmdevs[i]->cfg.rx.ch_size;
+		offset = ALIGN(offset, XSHM_PAYL_ALIGN);
+		if (offset > xshm_size)
+			goto badsize;
+	}
+
+	/*
+	 * Allocate RW Data Area. This will located in the beginning
+	 * just after the Modem Boot Image.
+	 */
+	offset = rw_start;
+	for (i = 0; i < xshm_channels; i++) {
+		u8 align;
+		u32 size;
+		ch = LEOFFS2PTR(shm_start, ipctoc->channel_offsets[i].tx);
+		add_magic_pad(&offset, shm_start);
+		ch->offset = cpu_to_le32(offset);
+		init_data(offset, i, xshmdevs[i]->cfg.tx.ch_size);
+		ch->mode = xshmdevs[i]->cfg.mode &
+				XSHM_MODE_MASK;
+		ch->buffers = cpu_to_le32(xshmdevs[i]->cfg.tx.buffers);
+		align = rounddown_pow_of_two(xshmdevs[i]->cfg.rx.alignment);
+		ch->alignment =	align;
+		ch->packets = xshmdevs[i]->cfg.rx.packets;
+		ch->mtu = xshmdevs[i]->cfg.rx.mtu;
+		size = xshmdevs[i]->cfg.tx.ch_size;
+		if (xshmdevs[i]->cfg.mode & XSHM_PACKET_MODE) {
+			u32 buf_size;
+			buf_size = size / xshmdevs[i]->cfg.tx.buffers;
+			buf_size = rounddown(buf_size, align);
+			size = buf_size * xshmdevs[i]->cfg.tx.buffers;
+		}
+		ch->size = cpu_to_le32(size);
+		pr_debug("Buffer area RW for Channel[%d] at: 0x%08x size:%d\n",
+				i, offset, size);
+		offset += xshmdevs[i]->cfg.tx.ch_size;
+		offset = ALIGN(offset, XSHM_PAYL_ALIGN);
+		if (offset > ro_start)
+			goto badsize;
+	}
+
+	/*
+	 * Allocate RW IPC Area. This will located after RW data area,
+	 * just before the IPC-TOC.
+	 */
+	offset = ALIGN(offset, XSHM_PAYL_ALIGN);
+	for (i = 0; i < xshm_channels; i++) {
+		int n = xshmdevs[i]->cfg.tx.buffers;
+		ch = LEOFFS2PTR(shm_start, ipctoc->channel_offsets[i].tx);
+		add_magic_pad(&offset, shm_start);
+		ch->ipc = cpu_to_le32(offset);
+		bix = OFFS2PTR(shm_start, offset);
+		bix->read_index = cpu_to_le32(0);
+		bix->write_index = cpu_to_le32(0);
+		bix->state = cpu_to_le32(XSHM_CLOSED);
+		bix->size[0] = cpu_to_le32(0);
+
+		pr_debug("IPC RW[%d] @: 0x%08x\n",  i, offset);
+		offset += offsetof(struct xshm_bufidx, size[n + 2]);
+		offset = ALIGN(offset, XSHM_PAYL_ALIGN);
+		if (offset > xshm_size)
+			goto badsize;
+	}
+
+	/* Allocate genio bits for each channel according to priority*/
+	bitno = 0;
+	for (pri = 0; pri < 8; pri++) {
+		for (i = 0; i < xshm_channels; i++) {
+			if (xshmdevs[i]->cfg.priority == pri) {
+				ch = LEOFFS2PTR(shm_start,
+						ipctoc->channel_offsets[i].tx);
+				ch->write_bit = cpu_to_le16(bitno * 4);
+				ch->read_bit = cpu_to_le16(bitno * 4 + 2);
+				ch = LEOFFS2PTR(shm_start,
+						ipctoc->channel_offsets[i].rx);
+				ch->write_bit = cpu_to_le16(bitno * 4 + 1);
+				ch->read_bit = cpu_to_le16(bitno * 4 + 3);
+				bitno++;
+			}
+		}
+	}
+
+	/*
+	 * The Master TOC points out the boot images for the modem,
+	 * Use the first avilable entry in the toc to write the pointer,
+	 * to the IPC-TOC defined above.
+	 */
+	found = false;
+	for (toc_entry = shm_start, i = 0; i < 16; i++, toc_entry++)
+		if (toc_entry->start == cpu_to_le32(0xffffffff)) {
+			pr_debug("IPCTOC address written into Master TOC"
+					" @ 0x%08x\n", i * 32);
+			toc_entry->start =
+				cpu_to_le32(GET_OFFSET(shm_start, ipctoc));
+			toc_entry->size = cpu_to_le32(0);
+			toc_entry->flags = cpu_to_le32(0);
+			toc_entry->entry_point = cpu_to_le32(0);
+			toc_entry->load_addr = cpu_to_le32(0xffffffff);
+			memset(toc_entry->name, 0, sizeof(toc_entry->name));
+			sprintf(toc_entry->name, "ipc-toc");
+			found = true;
+			break;
+		}
+	if (!found) {
+		pr_debug("Cannot insert IPC-TOC in toc\n");
+		goto bad_config;
+	}
+
+	store_checksum(ipctoc, ipcro_offs - ipctoc_offs);
+
+	return 0;
+
+badsize:
+	pr_debug("IPCTOC not enough space offset (size:0x%lx offset:0x%x\n",
+			xshm_size, offset);
+	return -ENOSPC;
+
+bad_config:
+	pr_debug("IPCTOC bad configuration data\n");
+	return -EINVAL;
+}
+
+static int xshm_verify_config(struct xshm_channel *xcfg)
+{
+	int j;
+
+	if ((xcfg->mode & XSHM_MODE_MASK) != XSHM_PACKET_MODE &&
+			(xcfg->mode & XSHM_MODE_MASK) != XSHM_STREAM_MODE) {
+		pr_debug("Bad config:"
+				"channel mode must be set\n");
+		return -EINVAL;
+	}
+	if (xcfg->mode & XSHM_PACKET_MODE && xcfg->rx.buffers < 2) {
+		pr_debug("Bad config:minimum 2 buffers "
+				"must be set for packet mode\n");
+		return -EINVAL;
+	}
+
+	if (xcfg->rx.ch_size < XSHM_MIN_CHSZ) {
+		pr_debug("Bad config:"
+				"Channel size must be larger than %d\n",
+				XSHM_MIN_CHSZ);
+		return -EINVAL;
+	}
+
+	if (xcfg->mode & XSHM_PACKET_MODE) {
+		if (xcfg->tx.buffers < 2) {
+			pr_debug("Bad config:"
+				"buffers must be minimum 2 packet mode\n");
+			return -EINVAL;
+		}
+		if (xcfg->tx.packets < 1) {
+			pr_debug("Bad config:"
+				"packets must be set for packet mode\n");
+			return -EINVAL;
+		}
+	}
+
+	if (xcfg->tx.ch_size < XSHM_MIN_CHSZ) {
+		pr_debug("Bad config:"
+				"Channel size must be larger than %d\n",
+				XSHM_MIN_CHSZ);
+		return -EINVAL;
+	}
+
+	if (xcfg->name[0] == '\0') {
+		pr_debug("Channel must be named\n");
+		return -EINVAL;
+	}
+	for (j = 0; j < xshm_channels; j++) {
+		struct xshm_channel *xcfg2 = &xshmdevs[j]->cfg;
+		if (xcfg != xcfg2 && strcmp(xcfg->name, xcfg2->name) == 0) {
+			pr_debug("Channels has same name:%s\n",
+					 xcfg->name);
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+static int verify_config(void)
+{
+	int i;
+
+	if (xshm_channels == 0) {
+		pr_debug("Bad config: minimum one channel must be defined\n");
+		return -EINVAL;
+	}
+	for (i = 0; i < xshm_channels; i++) {
+		int err = xshm_verify_config(&xshmdevs[i]->cfg);
+		if (err)
+			return err;
+	}
+	return 0;
+}
+
+/*
+ * Create Configuration data for the platform devices.
+ */
+static void create_devs(void)
+{
+	int i;
+
+	for (i = 0; i < xshm_channels; i++) {
+		struct xshm_bufidx *buf_rx, *buf_tx;
+		struct xshm_ipctoc_channel *ch_rx, *ch_tx;
+		struct xshm_channel *xcfg = &xshmdevs[i]->cfg;
+		ch_rx = LEOFFS2PTR(shm_start,
+				ipctoc->channel_offsets[i].rx);
+		buf_rx = LEOFFS2PTR(shm_start, ch_rx->ipc);
+		ch_tx = LEOFFS2PTR(shm_start,
+				ipctoc->channel_offsets[i].tx);
+		buf_tx = LEOFFS2PTR(shm_start, ch_tx->ipc);
+
+		/*
+		 * Due to restricted read-only access
+		 * we swap positions for read/write
+		 * pointers.
+		 */
+		xcfg->tx.write = &buf_tx->write_index;
+		xcfg->tx.read = &buf_rx->read_index;
+
+		xcfg->rx.write = &buf_rx->write_index;
+		xcfg->rx.read = &buf_tx->read_index;
+
+		xcfg->rx.addr = LEOFFS2PTR(shm_start, ch_rx->offset);
+		xcfg->tx.addr = LEOFFS2PTR(shm_start, ch_tx->offset);
+		xcfg->rx.state = &buf_rx->state;
+		xcfg->tx.state = &buf_tx->state;
+		xcfg->tx.buf_size = buf_tx->size;
+		xcfg->rx.buf_size = buf_rx->size;
+
+		xcfg->rx.xfer_bit = le16_to_cpu(ch_rx->write_bit);
+		xcfg->tx.xfer_bit = le16_to_cpu(ch_tx->write_bit);
+		xcfg->rx.xfer_done_bit = le16_to_cpu(ch_rx->read_bit);
+		xcfg->tx.xfer_done_bit = le16_to_cpu(ch_tx->read_bit);
+
+		if (xcfg->mode & XSHM_PAIR_MODE) {
+			struct xshm_channel *pair;
+			pr_debug("Channel[%d] is in PAIR mode\n", i);
+			if (i < 1) {
+				pr_debug("No channel to pair with\n");
+				continue;
+			}
+			/* Cross couple rx/tx on the pair */
+			pair = &xshmdevs[i - 1]->cfg;
+
+			/* Copy everything but the kobj which is at the end */
+			memcpy(&xcfg->tx, &pair->rx,
+					offsetof(struct xshm_udchannel, kobj));
+			memcpy(&xcfg->rx, &pair->tx,
+					offsetof(struct xshm_udchannel, kobj));
+		} else if (xcfg->mode & XSHM_LOOP_MODE) {
+			pr_debug("Channel[%d] is in LOOP mode\n", i);
+			/*
+			 * Connect rx/tx in a pair. Copy everything,
+			 * but the kobj which is at the end
+			 */
+			memcpy(&xcfg->tx, &xcfg->rx,
+					offsetof(struct xshm_udchannel, kobj));
+		}
+
+		pr_devel("RX[%d] wi:%p ri:%p\n", i, xcfg->rx.read,
+				xcfg->rx.write);
+		pr_devel("TX[%d] wi:%p ri:%p\n", i, xcfg->tx.read,
+				xcfg->tx.write);
+	}
+}
+
+static int do_commit(void)
+{
+	int err;
+
+	if (config_error) {
+		pr_devel("config error detected\n");
+		return -EINVAL;
+	}
+
+	if (commited) {
+		pr_devel("already commited\n");
+		config_error = true;
+		return -EINVAL;
+	}
+	err = verify_config();
+	if (err) {
+		pr_devel("bad config\n");
+		config_error = true;
+		return err;
+	}
+	err = write_to_shm();
+	if (err) {
+		pr_devel("writei to SHM failed\n");
+		config_error = true;
+		return err;
+	}
+	commited = true;
+	create_devs();
+	return 0;
+}
+
+static int do_register(void)
+{
+	int i, err;
+
+	if (!commited || registered || config_error) {
+		pr_devel("bad sequence of requests\n");
+		config_error = true;
+		return -EINVAL;
+	}
+
+	err = verify_config();
+	if (err) {
+		config_error = true;
+		pr_devel("bad config\n");
+		return err;
+	}
+	registered = true;
+
+	for (i = 0; i < xshm_channels; i++)
+		xshm_register_dev(xshmdevs[i]);
+
+	return 0;
+}
+
+static void do_reset(void)
+{
+	xshm_reset();
+	config_error = false;
+	ready_for_ipc = false;
+	ready_for_caif = false;
+	registered = false;
+	commited = false;
+	addr_set = false;
+	modem_bootimg_size = TOC_SZ;
+	xshm_channels = 0;
+}
+
+static int do_set_addr(void)
+{
+	int err;
+	if (!commited || addr_set || config_error) {
+		pr_devel("bad sequence of requests\n");
+		config_error = true;
+		return -EINVAL;
+	}
+	err = verify_config();
+	if (err) {
+		config_error = true;
+		pr_devel("bad config\n");
+		return err;
+	}
+	addr_set = true;
+	return genio_set_shm_addr(xshm_c2c_bootaddr, genio_ipc_ready_cb);
+}
+
+static void parent_release(struct device *dev)
+{
+}
+
+static int copy_name(const char *src, char *d, size_t count)
+{
+	const char *s, *end = src + count;
+	for (s = src; *s && s < end; s++, d++)
+		if (*s == '\0' || *s == '\n')
+			break;
+		else if (!isalnum(*s)) {
+			pr_debug("Illegal chr:'%c' in name:'%s'\n", *s, src);
+			return -EINVAL;
+		} else if (s - src >= XSHM_NAMESZ - 1) {
+			pr_debug("Name '%s'too long\n", src);
+			return -EINVAL;
+		} else
+			*d = *s;
+	*d = '\0';
+
+	return count;
+}
+
+inline struct xshm_dev *get_dev2xshm(struct device *dev)
+{
+       struct platform_device *pdev;
+       struct xshm_dev *xshmdev;
+       pdev = container_of(dev, struct platform_device, dev);
+       xshmdev = container_of(pdev, struct xshm_dev, pdev);
+       return xshmdev;
+}
+
+static void xshmdev_release(struct device *dev)
+{
+	struct xshm_dev *xshm = get_dev2xshm(dev);
+	kfree(xshm);
+}
+
+/* sysfs: Read the modem firmware (actually the whole shared memory area) */
+static ssize_t modemfw_read(struct file *file, struct kobject *kobj,
+			struct bin_attribute *attr,
+			char *buf, loff_t off, size_t count)
+{
+#ifdef DEBUG
+	/* Read shm area is usefull for debug */
+	if (off > xshm_size)
+		return 0;
+	if (off + count > xshm_size)
+		count = xshm_size - off;
+	memcpy(buf, shm_start + off, count);
+	return count;
+#else
+	return -EINVAL;
+#endif
+}
+
+/* sysfs: Write the modem firmware including TOC */
+static ssize_t modemfw_write(struct file *f, struct kobject *kobj,
+			struct bin_attribute *attr,
+			char *buf, loff_t off, size_t count)
+{
+	if (commited)
+		return -EBUSY;
+
+	if (off + count > xshm_size)
+		return -ENOSPC;
+	memcpy(shm_start + off, buf, count);
+	modem_bootimg_size = off + count;
+	return count;
+}
+
+/* sysfs: Modem firmware attribute */
+static struct bin_attribute modemfw_attr = {
+	.attr = {
+		 .name = "bootimg",
+		 .mode = S_IRUGO | S_IWUSR,
+		 },
+	.size = IMG_MAX_SZ,
+	.read = modemfw_read,
+	.write = modemfw_write
+};
+
+/* sysfs: ipc_ready file */
+static ssize_t ipc_ready_show(struct device *dev, struct device_attribute *attr,
+				char *buf)
+{
+	return sprintf(buf, "%d\n", ready_for_ipc);
+}
+
+/* sysfs: ipc_ready file */
+static ssize_t caif_ready_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", ready_for_caif);
+}
+
+static DEVICE_ATTR(ipc_ready, S_IRUSR | S_IRUGO, ipc_ready_show, NULL);
+static DEVICE_ATTR(caif_ready, S_IRUSR | S_IRUGO, caif_ready_show, NULL);
+
+/* sysfs: notification on change of ipc_ready to user space */
+void xshm_ipc_ready(void)
+{
+	sysfs_notify(&parentdev->kobj, NULL, dev_attr_ipc_ready.attr.name);
+}
+
+/* sysfs: notification on change of caif_ready to user space */
+void xshm_caif_ready(void)
+{
+	sysfs_notify(&parentdev->kobj, NULL, dev_attr_caif_ready.attr.name);
+}
+
+/* XSHM Generic NETLINK family */
+static struct genl_family xshm_gnl_family = {
+	.id = GENL_ID_GENERATE,
+	.hdrsize = 0,
+	.name = "XSHM",
+	.version = XSHM_PROTO_VERSION,
+	.maxattr = XSHM_A_MAX,
+};
+
+/* XSHM Netlink attribute policy */
+static const struct nla_policy xshm_genl_policy[XSHM_A_MAX + 1] = {
+	[XSHM_A_VERSION] = { .type = NLA_U8 },
+	[XSHM_A_SUB_VERSION] = { .type = NLA_U8 },
+	[__XSHM_A_FLAGS] = { .type = NLA_U32 },
+	[XSHM_A_NAME] = { .type = NLA_NUL_STRING, .len = XSHM_NAMESZ},
+	[XSHM_A_RX_CHANNEL] = { .type = NLA_NESTED },
+	[XSHM_A_TX_CHANNEL] = { .type = NLA_NESTED },
+	[XSHM_A_PRIORITY] = { .type = NLA_U8 },
+	[XSHM_A_LATENCY] = { .type = NLA_U8 },
+};
+
+/* Policy for uni-directional attributes for stream */
+static const struct nla_policy stream_policy[XSHM_A_MAX + 1] = {
+	[XSHM_A_CHANNEL_SIZE] = { .type = NLA_U32 },
+};
+
+/* Policy for uni-directional attributes for packet */
+static const struct nla_policy packet_policy[XSHM_A_MAX + 1] = {
+	[XSHM_A_CHANNEL_SIZE] = { .type = NLA_U32 },
+	[XSHM_A_CHANNEL_BUFFERS] = { .type = NLA_U32 },
+	[XSHM_A_MTU] = { .type = NLA_U16 },
+	[XSHM_A_ALIGNMENT] = { .type = NLA_U8 },
+	[XSHM_A_PACKETS] = { .type = NLA_U8 },
+};
+
+static int xshm_add_udchannel(struct xshm_udchannel *chn, int attr,
+			struct genl_info *info, struct nla_policy const *policy)
+{
+	struct nlattr *nla;
+	int nla_rem;
+
+	if (!info->attrs[attr])
+		return -EINVAL;
+
+	if (nla_validate_nested(info->attrs[attr],
+					XSHM_A_MAX,
+					policy) != 0) {
+		pr_info("Invalid RX channel attributes\n");
+		return -EINVAL;
+	}
+
+	nla_for_each_nested(nla, info->attrs[attr], nla_rem) {
+
+		if (nla_type(nla) == XSHM_A_CHANNEL_SIZE)
+			chn->ch_size = nla_get_u32(nla);
+
+		if (nla_type(nla) == XSHM_A_CHANNEL_BUFFERS)
+			chn->buffers = nla_get_u32(nla);
+
+		if (nla_type(nla) == XSHM_A_MTU)
+			chn->mtu = nla_get_u16(nla);
+
+		if (nla_type(nla) == XSHM_A_PACKETS)
+			chn->packets = nla_get_u8(nla);
+
+		if (nla_type(nla) == XSHM_A_ALIGNMENT) {
+			chn->alignment = nla_get_u8(nla);
+			chn->alignment = rounddown_pow_of_two(chn->alignment);
+		}
+
+	}
+	return 0;
+}
+
+static int xshm_add_channel(struct xshm_channel *cfg, struct genl_info *info,
+			int mode)
+{
+	int len, err;
+	struct nla_policy const *policy;
+	char name[XSHM_NAMESZ];
+
+	policy = (mode == XSHM_PACKET_MODE) ? packet_policy : stream_policy;
+
+	if (info->attrs[XSHM_A_VERSION]) {
+		u8 version;
+		u8 sub_version;
+
+		version = nla_get_u8(info->attrs[XSHM_A_VERSION]);
+		if (!info->attrs[XSHM_A_SUB_VERSION])
+			return -EINVAL;
+		sub_version = nla_get_u8(info->attrs[XSHM_A_SUB_VERSION]);
+		if (version != 1 || sub_version != 0) {
+			pr_info("Bad version or sub_version\n");
+			return -EINVAL;
+		}
+	}
+
+	if (!info->attrs[XSHM_A_NAME]) {
+		pr_debug("Name not specified\n");
+		return -EINVAL;
+	}
+
+	len = nla_strlcpy(name, info->attrs[XSHM_A_NAME],
+			XSHM_NAMESZ);
+
+	if (len > XSHM_NAMESZ)
+		return -EINVAL;
+
+	err = copy_name(name, cfg->name, sizeof(name));
+	if (err < 0)
+		return err;
+
+	cfg->excl_group = 1;
+	if (info->attrs[XSHM_A_EXCL_GROUP])
+		cfg->excl_group = nla_get_u8(info->attrs[XSHM_A_EXCL_GROUP]);
+
+	err = xshm_add_udchannel(&cfg->rx, XSHM_A_RX_CHANNEL, info, policy);
+
+	if (err)
+		return err;
+	err = xshm_add_udchannel(&cfg->tx, XSHM_A_TX_CHANNEL, info, policy);
+
+	if (err)
+		return err;
+
+	if (info->attrs[XSHM_A_PRIORITY]) {
+		cfg->priority = nla_get_u8(info->attrs[XSHM_A_PRIORITY]);
+		/* silently fixup bad value */
+		if (cfg->priority > 7)
+			cfg->priority = 0;
+	}
+
+	if (info->attrs[XSHM_A_LATENCY])
+		cfg->latency = nla_get_u8(info->attrs[XSHM_A_LATENCY]);
+
+	if (info->attrs[__XSHM_A_FLAGS])
+		cfg->mode |= nla_get_u32(info->attrs[__XSHM_A_FLAGS]);
+
+
+	return 0;
+}
+
+static int do_reply(struct genl_info *info, int result)
+{
+	struct sk_buff *msg;
+	int err;
+	void *reply;
+
+	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (msg == NULL)
+		return -ENOMEM;
+
+	reply = genlmsg_put_reply(msg, info, &xshm_gnl_family, 0, result);
+	if (reply == NULL) {
+		kfree_skb(msg);
+		return -EMSGSIZE;
+	}
+
+	genlmsg_end(msg, reply);
+	err = genlmsg_reply(msg, info);
+	return err;
+}
+
+static int xshm_add_ch(struct sk_buff *skb, struct genl_info *info, int mode)
+{
+	int err;
+	struct xshm_channel cfg;
+	struct xshm_dev *xshmdev;
+
+	if (xshm_channels + 1 > XSHM_MAX_CHANNELS) {
+		pr_debug("Too many channels added\n");
+		return -EINVAL;
+	}
+
+	memset(&cfg, 0, sizeof(cfg));
+	cfg.mode = mode;
+	err = xshm_add_channel(&cfg, info, mode);
+	if (err)
+		return err;
+
+	xshmdev = kzalloc(sizeof(*xshmdev), GFP_KERNEL);
+	if (xshmdev == NULL)
+		return -ENOMEM;
+
+	if (mode == XSHM_PACKET_MODE)
+		xshmdev->pdev.name = "xshmp";
+	else
+		xshmdev->pdev.name = "xshms";
+
+	xshmdevs[xshm_channels] = xshmdev;
+	xshmdevs[xshm_channels]->cfg = cfg;
+	xshmdev->pdev.id = xshm_channels;
+	xshmdev->pdev.dev.parent = parentdev;
+	xshmdev->pdev.dev.release = xshmdev_release;
+	xshmdevs[xshm_channels] = xshmdev;
+
+	++xshm_channels;
+
+	err = xshm_verify_config(&xshmdev->cfg);
+	if (err)
+		goto error;
+	err = do_reply(info, 0);
+	if (err)
+		goto error;
+	return err;
+
+error:
+	--xshm_channels;
+	kfree(xshmdev);
+	return err;
+}
+
+static int xshm_add_packet_ch(struct sk_buff *skb, struct genl_info *info)
+{
+	return xshm_add_ch(skb, info, XSHM_PACKET_MODE);
+}
+
+static int xshm_add_stream_ch(struct sk_buff *skb, struct genl_info *info)
+{
+	return xshm_add_ch(skb, info, XSHM_STREAM_MODE);
+}
+
+
+static int xshm_c_commit(struct sk_buff *skb, struct genl_info *info)
+{
+	int err = do_commit();
+	if (!err)
+		do_reply(info, 0);
+	return err;
+}
+
+static int xshm_c_register(struct sk_buff *skb, struct genl_info *info)
+{
+	int err = do_register();
+	if (!err)
+		do_reply(info, 0);
+	return err;
+}
+
+static int xshm_c_set_addr(struct sk_buff *skb, struct genl_info *info)
+{
+	int err = do_set_addr();
+	if (!err)
+		do_reply(info, 0);
+	return err;
+}
+
+static int xshm_c_reset(struct sk_buff *skb, struct genl_info *info)
+{
+	do_reset();
+	do_reply(info, 0);
+	return 0;
+}
+
+static int xshm_c_verify(struct sk_buff *skb, struct genl_info *info)
+{
+	int err = verify_config();
+	if (!err)
+		do_reply(info, 0);
+	return err;
+}
+
+static struct genl_ops xshm_genl_ops[] = {
+	{
+	.cmd = XSHM_C_ADD_STREAM_CHANNEL,
+	.flags = GENL_ADMIN_PERM,
+	.policy = xshm_genl_policy,
+	.doit = xshm_add_stream_ch,
+	.dumpit = NULL,
+	},
+	{
+	.cmd = XSHM_C_ADD_PACKET_CHANNEL,
+	.flags = GENL_ADMIN_PERM,
+	.policy = xshm_genl_policy,
+	.doit = xshm_add_packet_ch,
+	.dumpit = NULL,
+	},
+	{
+	.cmd = XSHM_C_COMMIT,
+	.flags = GENL_ADMIN_PERM,
+	.doit = xshm_c_commit,
+	.dumpit = NULL,
+	},
+	{
+	.cmd = XSHM_C_REGISTER,
+	.flags = GENL_ADMIN_PERM,
+	.doit = xshm_c_register,
+	.dumpit = NULL,
+	},
+	{
+	.cmd = XSHM_C_SET_ADDR,
+	.flags = GENL_ADMIN_PERM,
+	.doit = xshm_c_set_addr,
+	.dumpit = NULL,
+	},
+	{
+	.cmd = XSHM_C_RESET,
+	.flags = GENL_ADMIN_PERM,
+	.doit = xshm_c_reset,
+	.dumpit = NULL,
+	},
+	{
+	.cmd = __XSHM_C_VERIFY,
+	.flags = GENL_ADMIN_PERM,
+	.doit = xshm_c_verify,
+	.dumpit = NULL,
+	},
+
+};
+
+static bool gennetl_reg;
+
+/* Initialize boot handling and create sysfs entries*/
+int xshm_boot_init(void)
+{
+	int err = -EINVAL;
+	bool xshm_fake = false;
+
+	/* Negative xshm_size indicates module test without real SHM */
+	if (xshm_size < 0) {
+		xshm_fake = true;
+		xshm_size = abs(xshm_size);
+	}
+
+	if (xshm_size < TOC_SZ)
+		goto bad_config;
+
+	if (xshm_fake) {
+		shm_start = kzalloc(xshm_size, GFP_KERNEL);
+		err = -ENOMEM;
+		if (!shm_start)
+			goto error_nodev;
+		xshm_start = (unsigned long) shm_start;
+		memset(shm_start, 0xaa, xshm_size);
+	} else {
+		if (xshm_start == 0)
+			goto bad_config;
+		shm_start = ioremap(xshm_start, xshm_size);
+	}
+
+	/* Initiate the Master TOC to 0xff for the first 512 bytes */
+	if (xshm_size > TOC_SZ)
+		memset(shm_start, 0xff, TOC_SZ);
+
+	modem_bootimg_size = TOC_SZ;
+
+	pr_debug("Boot image addr: %p size:%d\n", shm_start,
+			modem_bootimg_size);
+
+	parentdev = &_parentdev;
+	memset(parentdev, 0, sizeof(parentdev));
+	dev_set_name(parentdev, "xshm");
+	parentdev->release = parent_release;
+	err = device_register(parentdev);
+	if (err)
+		goto error_nodev;
+
+	err = device_create_bin_file(parentdev, &modemfw_attr);
+	if (err)
+		goto error;
+	err = device_create_file(parentdev, &dev_attr_ipc_ready);
+	if (err)
+		goto error;
+	err = device_create_file(parentdev, &dev_attr_caif_ready);
+	if (err)
+		goto error;
+
+	err = genl_register_family_with_ops(&xshm_gnl_family,
+		xshm_genl_ops, ARRAY_SIZE(xshm_genl_ops));
+	if (err)
+		goto error;
+
+	gennetl_reg = 1;
+	return err;
+error:
+	pr_debug("initialization failed\n");
+	device_unregister(parentdev);
+
+error_nodev:
+	if (xshm_fake)
+		kfree(shm_start);
+	return err;
+bad_config:
+	pr_err("Bad module configuration:"
+			" xshm_base_address:%lu xshm_size:%lu err:%d\n",
+			xshm_start, xshm_size, err);
+	/* Buildin module should not return error */
+	return -EINVAL;
+}
+
+void xshm_boot_exit(void)
+{
+	device_unregister(parentdev);
+
+	if (gennetl_reg)
+		genl_unregister_family(&xshm_gnl_family);
+	gennetl_reg = 0;
+}
diff --git a/drivers/xshm/xshm_dev.c b/drivers/xshm/xshm_dev.c
new file mode 100644
index 0000000..ac812b3
--- /dev/null
+++ b/drivers/xshm/xshm_dev.c
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author: Sjur Brendeland / sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": %s :" fmt, __func__
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+#include <linux/c2c_genio.h>
+#include <linux/xshm/xshm_ipctoc.h>
+#include <linux/xshm/xshm_pdev.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sjur Brændland <sjur.brandeland@stericsson.com>");
+MODULE_DESCRIPTION("External Shared Memory - Supporting direct boot and IPC");
+MODULE_VERSION("XSHM 0.5 : " __DATE__);
+
+static int xshm_inactivity_timeout = 1000;
+module_param(xshm_inactivity_timeout, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(xshm_inactivity_timeout, "Inactivity timeout, ms.");
+
+bool ready_for_ipc;
+bool ready_for_caif;
+static spinlock_t list_lock;
+static LIST_HEAD(pdev_list);
+static spinlock_t timer_lock;
+static int inactivity_timeout;
+static struct timer_list inactivity_timer;
+static bool power_on;
+
+#if 1
+#define xdev_dbg(dev, fmt, arg...) printk(KERN_DEBUG "%s%d: %s - " fmt, \
+			dev->pdev.name, dev->pdev.id, __func__, ##arg)
+#define xdev_devl(dev, fmt, arg...) printk(KERN_DEBUG "%s%d: %s - " fmt, \
+			dev->pdev.name, dev->pdev.id, __func__, ##arg)
+#define pr_xshmstate(dev, str) \
+	pr_devel("xshm%d: %s: %s STATE: %s txch:%s(%p) rxch:%s(%p)\n",	\
+			dev->pdev.id, __func__, str,			\
+			dev->state == XSHM_DEV_OPEN ? "open" : "close", \
+			*dev->cfg.tx.state == cpu_to_le32(XSHM_OPEN) ?	\
+			"open" : "close",				\
+			dev->cfg.tx.state,				\
+			*dev->cfg.rx.state == cpu_to_le32(XSHM_OPEN) ?	\
+			"open" : "close",				\
+			dev->cfg.rx.state)
+#else
+#define xdev_dbg(...)
+#define xdev_devl(...)
+#undef pr_debug
+#undef pr_devel
+#define pr_debug(...)
+#define pr_devel(...)
+#define pr_xshmstate(...)
+#endif
+
+static void inactivity_tout(unsigned long arg)
+{
+	unsigned long flags;
+	pr_devel("enter\n");
+	spin_lock_irqsave(&timer_lock, flags);
+	/*
+	 * This is paranoia, but if timer is reactivated
+	 * before this tout function is scheduled,
+	 * we just ignore this timeout.
+	 */
+	if (timer_pending(&inactivity_timer))
+		goto out;
+
+	if (power_on) {
+		pr_devel("genio power req(off)\n");
+		genio_power_req(false);
+		power_on = false;
+	}
+out:
+	spin_unlock_irqrestore(&timer_lock, flags);
+}
+
+static void activity(void)
+{
+	unsigned long flags;
+	pr_devel("enter\n");
+	spin_lock_irqsave(&timer_lock, flags);
+	if (!power_on) {
+		pr_devel("genio power req(on)\n");
+		genio_power_req(true);
+		power_on = true;
+	}
+	mod_timer(&inactivity_timer,
+			jiffies + inactivity_timeout);
+	spin_unlock_irqrestore(&timer_lock, flags);
+}
+
+static void reset_activity_tout(void)
+{
+	unsigned long flags;
+	pr_devel("enter\n");
+	spin_lock_irqsave(&timer_lock, flags);
+	if (power_on) {
+		genio_power_req(false);
+		power_on = false;
+	}
+	del_timer_sync(&inactivity_timer);
+	spin_unlock_irqrestore(&timer_lock, flags);
+}
+
+static int xshmdev_ipc_tx(struct xshm_dev *dev)
+{
+	xdev_devl(dev, "call genio_set_bit(%d)\n", dev->cfg.tx.xfer_bit);
+	activity();
+	return genio_set_bit(dev->cfg.tx.xfer_bit);
+}
+
+static int xshmdev_ipc_rx_release(struct xshm_dev *dev, bool more)
+{
+	xdev_devl(dev, "call genio_set_bit(%d)\n", dev->cfg.tx.xfer_bit);
+	activity();
+	return genio_set_bit(dev->cfg.rx.xfer_done_bit);
+}
+
+static int do_open(struct xshm_dev *dev)
+{
+	int err;
+
+	pr_xshmstate(dev, "enter");
+	err = dev->open_cb(dev->driver_data);
+	if (err < 0) {
+		xdev_dbg(dev, "Error - open_cb failed\n");
+
+		/* Make sure ring-buffer is empty i RX and TX direction */
+		*dev->cfg.rx.read = *dev->cfg.rx.write;
+		*dev->cfg.tx.write = *dev->cfg.tx.read;
+		*dev->cfg.tx.state = cpu_to_le32(XSHM_CLOSED);
+		xdev_devl(dev, "set state = XSHM_DEV_CLOSED\n");
+		dev->state = XSHM_DEV_CLOSED;
+		return err;
+	}
+
+	/* Check is we already have any data in the pipe */
+	if (*dev->cfg.rx.write != *dev->cfg.rx.read) {
+		pr_devel("Received data during opening\n");
+		dev->ipc_rx_cb(dev->driver_data);
+	}
+
+	return err;
+}
+
+static void genio_rx_cb(void *data)
+{
+	struct xshm_dev *dev = data;
+
+	pr_xshmstate(dev, "Enter");
+
+	if (likely(dev->state == XSHM_DEV_OPEN)) {
+		if (unlikely(!ready_for_ipc)) {
+			xdev_devl(dev, "ready_for_ipc is not yet set\n");
+			return;
+		}
+
+		if (dev->ipc_rx_cb) {
+			int err = dev->ipc_rx_cb(dev->driver_data);
+			if (unlikely(err < 0))
+				goto remote_close;
+		}
+
+	} else if (*dev->cfg.rx.state == cpu_to_le32(XSHM_OPEN)) {
+		pr_xshmstate(dev, "");
+		dev->state = XSHM_DEV_OPEN;
+		if (!ready_for_ipc) {
+			xdev_devl(dev, "ready_for_ipc is not yet set\n");
+			return;
+		}
+		if (do_open(dev) < 0)
+			goto open_fail;
+	}
+	return;
+open_fail:
+	pr_xshmstate(dev, "exit open failed");
+	/* Make sure ring-buffer is empty i RX and TX direction */
+	*dev->cfg.rx.read = *dev->cfg.rx.write;
+	*dev->cfg.tx.write = *dev->cfg.tx.read;
+remote_close:
+	*dev->cfg.tx.state = cpu_to_le32(XSHM_CLOSED);
+	dev->state = XSHM_DEV_CLOSED;
+	dev->close_cb(dev->driver_data);
+}
+
+static void genio_tx_release_cb(void *data)
+{
+	struct xshm_dev *dev = data;
+
+	pr_xshmstate(dev, "Enter");
+	if (!ready_for_ipc) {
+		xdev_devl(dev, "not ready_for_ipc\n");
+		return;
+	}
+	if (dev->ipc_tx_release_cb)
+		dev->ipc_tx_release_cb(dev->driver_data);
+}
+
+static int xshmdev_open(struct xshm_dev *dev)
+{
+	int err = -EINVAL;
+	struct list_head *node;
+	struct list_head *n;
+
+	pr_xshmstate(dev, "Enter");
+	if (WARN_ON(dev->ipc_rx_cb == NULL) ||
+			WARN_ON(dev->ipc_tx_release_cb == NULL) ||
+			WARN_ON(dev->open_cb == NULL) ||
+			WARN_ON(dev->close_cb == NULL))
+		goto err;
+
+	list_for_each_safe(node, n, &pdev_list) {
+		struct xshm_dev *dev2;
+		dev2 = list_entry(node, struct xshm_dev, node);
+		if (dev2 == dev)
+			continue;
+
+		if (dev2->state == XSHM_DEV_OPEN &&
+				dev2->cfg.excl_group != dev->cfg.excl_group) {
+			xdev_dbg(dev, "Exclusive group "
+					"prohibits device open\n");
+			err = -EPERM;
+			goto err;
+		}
+	}
+	pr_devel("call genio_subscribe(%d)\n", dev->cfg.rx.xfer_bit);
+	err = genio_subscribe(dev->cfg.rx.xfer_bit, genio_rx_cb, dev);
+	if (err)
+		goto err;
+
+	pr_devel("call genio_subscribe(%d)\n", dev->cfg.tx.xfer_done_bit);
+	err = genio_subscribe(dev->cfg.tx.xfer_done_bit,
+			genio_tx_release_cb, dev);
+	if (err)
+		goto err;
+
+	/* Indicate that our side is open and ready for action */
+	*dev->cfg.rx.read = *dev->cfg.rx.write;
+	*dev->cfg.tx.write = *dev->cfg.tx.read;
+	*dev->cfg.tx.state = cpu_to_le32(XSHM_OPEN);
+
+	if (ready_for_ipc)
+		err = xshmdev_ipc_tx(dev);
+
+	if (err < 0) {
+		xdev_dbg(dev, "can't update geno\n");
+		goto err;
+	}
+	/* If other side is ready as well we're ready to role */
+	if (*dev->cfg.rx.state == cpu_to_le32(XSHM_OPEN) && ready_for_ipc) {
+		if (do_open(dev) < 0)
+			goto err;
+		dev->state = XSHM_DEV_OPEN;
+	}
+
+	return 0;
+err:
+	pr_xshmstate(dev, "exit error");
+	*dev->cfg.rx.read = *dev->cfg.rx.write;
+	*dev->cfg.tx.write = *dev->cfg.tx.read;
+	*dev->cfg.tx.state = cpu_to_le32(XSHM_CLOSED);
+	return err;
+}
+
+static void xshmdev_close(struct xshm_dev *dev)
+{
+	pr_xshmstate(dev, "enter");
+
+	dev->state = XSHM_DEV_CLOSED;
+	*dev->cfg.rx.read = *dev->cfg.rx.write;
+	*dev->cfg.tx.state = cpu_to_le32(XSHM_CLOSED);
+	xshmdev_ipc_tx(dev);
+	if (dev->close_cb)
+		dev->close_cb(dev->driver_data);
+
+	pr_devel("call genio_unsubscribe(%d)\n", dev->cfg.rx.xfer_bit);
+	genio_unsubscribe(dev->cfg.rx.xfer_bit);
+	pr_devel("call genio_unsubscribe(%d)\n", dev->cfg.tx.xfer_done_bit);
+	genio_unsubscribe(dev->cfg.tx.xfer_done_bit);
+}
+
+int xshm_register_dev(struct xshm_dev *dev)
+{
+	int err;
+	unsigned long flags;
+
+	dev->state = XSHM_DEV_CLOSED;
+	dev->open = xshmdev_open;
+	dev->close = xshmdev_close;
+	dev->ipc_rx_release = xshmdev_ipc_rx_release;
+	dev->ipc_tx = xshmdev_ipc_tx;
+	/* Driver should only use this when platform_data is set */
+	dev->pdev.dev.platform_data = dev;
+	xdev_devl(dev, "re-register SHM platform device %s\n", dev->pdev.name);
+	err = platform_device_register(&dev->pdev);
+	if (err) {
+		xdev_dbg(dev, "registration failed (%d)\n", err);
+		goto clean;
+	}
+	spin_lock_irqsave(&list_lock, flags);
+	list_add_tail(&dev->node, &pdev_list);
+	spin_unlock_irqrestore(&list_lock, flags);
+
+	return err;
+clean:
+	kfree(dev);
+	return err;
+}
+
+static void genio_caif_ready_cb(bool ready)
+{
+	pr_devel("enter\n");
+	/* Set global variable ready_for_caif true */
+	if (ready_for_caif != ready) {
+		ready_for_caif = ready;
+		xshm_caif_ready();
+	}
+}
+
+static void genio_errhandler(int errno)
+{
+	/* Fake CAIF_READY low to trigger modem restart */
+	pr_warn("Driver reported error:%d\n", errno);
+	ready_for_caif = 0;
+	xshm_caif_ready();
+}
+
+void genio_ipc_ready_cb(void)
+{
+	struct xshm_dev *dev, *tmp;
+	unsigned long flags;
+	int err;
+	u32 getter = 0;
+	u32 setter = 0;
+
+	pr_devel("enter\n");
+	/* Set global variable ready_for_ipc true */
+#ifdef DEBUG
+	/*
+	 * In real life read_for_ipc doesn't change, but it's
+	 * convenient for testing.
+	 */
+	ready_for_ipc = !ready_for_ipc;
+#else
+	ready_for_ipc = true;
+#endif
+
+	xshm_ipc_ready();
+
+	genio_register_errhandler(genio_errhandler);
+
+	pr_devel("call genio_subscribe_caif_ready()\n");
+	err = genio_subscribe_caif_ready(genio_caif_ready_cb);
+	if (err < 0)
+		pr_debug("genio_subscribe_caif_ready failed:%d\n", err);
+
+	/* Take a refcount to the device so it doesn't go away */
+	spin_lock_irqsave(&list_lock, flags);
+	list_for_each_entry_safe(dev, tmp, &pdev_list, node)
+		get_device(&dev->pdev.dev);
+	spin_unlock_irqrestore(&list_lock, flags);
+
+	/* Collect the bit-mask for GENIO bits */
+	list_for_each_entry_safe(dev, tmp, &pdev_list, node) {
+		setter |= 1 << dev->cfg.tx.xfer_bit;
+		setter |= 1 << dev->cfg.rx.xfer_done_bit;
+		getter |= 1 << dev->cfg.rx.xfer_bit;
+		getter |= 1 << dev->cfg.tx.xfer_done_bit;
+	}
+	pr_devel("call genio_bit_alloc(%x,%x)\n", setter, getter);
+	err = genio_bit_alloc(setter, getter);
+	if (err < 0)
+		pr_debug("genio_bit_alloc failed:%d\n", err);
+
+	list_for_each_entry_safe(dev, tmp, &pdev_list, node) {
+		if (dev->cfg.rx.state != NULL && dev->cfg.tx.state != NULL &&
+				*dev->cfg.rx.state == cpu_to_le32(XSHM_OPEN) &&
+				*dev->cfg.tx.state == cpu_to_le32(XSHM_OPEN)) {
+			dev->state = XSHM_DEV_OPEN;
+			do_open(dev);
+		}
+		put_device(&dev->pdev.dev);
+	}
+}
+
+static int __init xshm_init(void)
+{
+	int err;
+
+	pr_devel("Initializing\n");
+
+	/* Pre-calculate inactivity timeout. */
+	if (xshm_inactivity_timeout != -1) {
+		inactivity_timeout =
+				xshm_inactivity_timeout * HZ / 1000;
+		if (inactivity_timeout == 0)
+			inactivity_timeout = 1;
+		else if (inactivity_timeout > NEXT_TIMER_MAX_DELTA)
+			inactivity_timeout = NEXT_TIMER_MAX_DELTA;
+	} else
+		inactivity_timeout = NEXT_TIMER_MAX_DELTA;
+
+	spin_lock_init(&list_lock);
+	INIT_LIST_HEAD(&pdev_list);
+
+	spin_lock_init(&timer_lock);
+	init_timer(&inactivity_timer);
+	inactivity_timer.data = 0L;
+	inactivity_timer.function = inactivity_tout;
+
+	pr_devel("call genio_init()\n");
+
+	err = xshm_boot_init();
+	if (err)
+		goto err;
+
+	return err;
+err:
+	pr_devel("call genio_exit()\n");
+	return err;
+}
+
+void close_devices(void)
+{
+	struct xshm_dev *dev, *tmp;
+
+	list_for_each_entry_safe(dev, tmp, &pdev_list, node)
+		if (dev->close_cb)
+			dev->close_cb(dev->driver_data);
+}
+
+void xshm_reset(void)
+{
+	struct xshm_dev *dev, *tmp;
+	unsigned long flags;
+
+	list_for_each_entry_safe(dev, tmp, &pdev_list, node) {
+		get_device(&dev->pdev.dev);
+		if (dev->close_cb)
+			dev->close_cb(dev->driver_data);
+		platform_device_unregister(&dev->pdev);
+		spin_lock_irqsave(&list_lock, flags);
+		dev->pdev.dev.platform_data = NULL;
+		list_del(&dev->node);
+		spin_unlock_irqrestore(&list_lock, flags);
+		put_device(&dev->pdev.dev);
+	}
+
+	reset_activity_tout();
+	genio_reset();
+}
+
+static void __exit xshm_exit(void)
+{
+	xshm_reset();
+	genio_unsubscribe(READY_FOR_IPC_BIT);
+	genio_unsubscribe(READY_FOR_CAIF_BIT);
+	xshm_boot_exit();
+}
+
+module_init(xshm_init);
+module_exit(xshm_exit);
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 7/9] xshm: Character device for XSHM channel access.
  2011-12-07  9:27 [PATCH 0/9] XSHM: Shared Memory Driver for ST-E Thor M7400 LTE modem Sjur Brændeland
                   ` (5 preceding siblings ...)
  2011-12-07  9:28 ` [PATCH 6/9] xshm: Platform device for XSHM Sjur Brændeland
@ 2011-12-07  9:28 ` Sjur Brændeland
  2011-12-07  9:28 ` [PATCH 8/9] xshm: Makefile and Kconfig for M7400 Shared Memory Drivers Sjur Brændeland
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 26+ messages in thread
From: Sjur Brændeland @ 2011-12-07  9:28 UTC (permalink / raw)
  To: linux-kernel; +Cc: Linus Walleij, Paul Bolle, Sjur Brændeland

This patch introduces a character interface to the XSHM channels.
A misc device is created, providing asynchronous IO and management.

The character device implements ring-buffer handling, copying
data directly from the Shared Memory area into user buffers.

Signed-off-by: Sjur Brændeland <sjur.brandeland@stericsson.com>
---
 drivers/xshm/xshm_chr.c | 1269 +++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1269 insertions(+), 0 deletions(-)
 create mode 100644 drivers/xshm/xshm_chr.c

diff --git a/drivers/xshm/xshm_chr.c b/drivers/xshm/xshm_chr.c
new file mode 100644
index 0000000..8593367
--- /dev/null
+++ b/drivers/xshm/xshm_chr.c
@@ -0,0 +1,1269 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author:	Per Sigmond / Per.Sigmond@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ":%s :" fmt, __func__
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+#include <linux/xshm/xshm_pdev.h>
+#include <linux/err.h>
+#include <linux/uaccess.h>
+
+MODULE_LICENSE("GPL");
+static LIST_HEAD(xshmchr_chrdev_list);
+static spinlock_t list_lock;
+
+#define xdev_dbg(dev, fmt, arg...) printk(KERN_DEBUG "%s: %s - " fmt, \
+			dev ? dev->misc.name : "?", __func__, ##arg)
+#define xdev_devl(dev, fmt, arg...) printk(KERN_DEBUG "%s: %s - " fmt, \
+			dev ? dev->misc.name : "?" , __func__, ##arg)
+#define pr_xchrstate(dev, str) \
+	xdev_devl(dev, "State: %s %s %s\n", str,	\
+		STATE_IS_PENDING(dev) ? "pending" : "", \
+		STATE_IS_OPEN(dev) ? "open" : "close")
+
+#define OPEN_TOUT			(25 * HZ)
+#define CONN_STATE_OPEN_BIT		0
+#define CONN_STATE_PENDING_BIT		1
+#define CONN_REMOTE_TEARDOWN_BIT	2
+#define CONN_EOF_BIT			4
+
+#define STATE_IS_OPEN(dev) test_bit(CONN_STATE_OPEN_BIT, \
+					(void *) &(dev)->conn_state)
+#define STATE_IS_REMOTE_TEARDOWN(dev) test_bit(CONN_REMOTE_TEARDOWN_BIT, \
+					(void *) &(dev)->conn_state)
+#define STATE_IS_PENDING(dev) test_bit(CONN_STATE_PENDING_BIT, \
+					(void *) &(dev)->conn_state)
+#define SET_STATE_OPEN(dev) (set_bit(CONN_STATE_OPEN_BIT,	\
+			(void *) &(dev)->conn_state), \
+			pr_devel("SET_STATE_OPEN:%d\n", dev->conn_state))
+#define SET_STATE_CLOSED(dev) (clear_bit(CONN_STATE_OPEN_BIT,	\
+			(void *) &(dev)->conn_state), \
+			pr_devel("SET_STATE_CLOSED:%d\n", dev->conn_state))
+#define SET_PENDING_ON(dev) (set_bit(CONN_STATE_PENDING_BIT,	\
+			(void *) &(dev)->conn_state), \
+			pr_devel("SET_PENDING_ON:%d\n", dev->conn_state))
+#define SET_PENDING_OFF(dev) (clear_bit(CONN_STATE_PENDING_BIT, \
+			(void *) &(dev)->conn_state), \
+			pr_devel("SET_PENDING_OFF:%d\n", dev->conn_state))
+#define SET_REMOTE_TEARDOWN(dev) (set_bit(CONN_REMOTE_TEARDOWN_BIT,	\
+			(void *) &(dev)->conn_state), \
+			pr_devel("SET_REMOTE_TEARDOWN:%d\n", dev->conn_state))
+#define CLEAR_REMOTE_TEARDOWN(dev) (clear_bit(CONN_REMOTE_TEARDOWN_BIT, \
+			(void *) &(dev)->conn_state), \
+			pr_devel("CLEAR_REMOTE_TEARDOWN:%d\n", dev->conn_state))
+#define SET_EOF(dev) (set_bit(CONN_EOF_BIT,	\
+			(void *) &(dev)->conn_state), \
+			pr_devel("SET_EOF:%d\n", dev->conn_state))
+#define CLEAR_EOF(dev) (clear_bit(CONN_EOF_BIT, \
+			(void *) &(dev)->conn_state), \
+			pr_devel("CLEAR_EOF:%d\n", dev->conn_state))
+#define STATE_IS_EOF(dev) test_bit(CONN_EOF_BIT, \
+					(void *) &(dev)->conn_state)
+
+#define CHR_READ_FLAG 0x01
+#define CHR_WRITE_FLAG 0x02
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfsdir;
+#include <linux/debugfs.h>
+#define	dbfs_atomic_inc(a) atomic_inc(a)
+#define	dbfs_atomic_add(v, a) atomic_add_return(v, a)
+#else
+#define	dbfs_atomic_inc(a) 0
+#define	dbfs_atomic_add(v, a) 0
+#endif
+
+struct ringbuf {
+	__le32 *ri;	/* Pointer to read-index in shared memory.*/
+	__le32 *wi;	/* Pointer to write-index in shared memory */
+	unsigned int size;/* Size of buffer */
+	void *data;	/* Buffer data in shared memory */
+};
+
+struct xshmchr_char_dev {
+	struct xshm_dev *xshm;
+	struct kref kref;
+	struct ringbuf rx, tx;
+	u32 conn_state;
+	char name[256];
+	struct miscdevice misc;
+	int file_mode;
+
+	/* Access to this struct and below layers */
+	struct mutex mutex;
+	wait_queue_head_t mgmt_wq;
+	/* List of misc test devices */
+	struct list_head list_field;
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *debugfs_device_dir;
+	atomic_t num_open;
+	atomic_t num_close;
+	atomic_t num_read;
+	atomic_t num_read_block;
+	atomic_t num_read_bytes;
+
+	atomic_t num_write;
+	atomic_t num_write_block;
+	atomic_t num_write_bytes;
+
+	atomic_t num_init;
+	atomic_t num_init_resp;
+	atomic_t num_deinit;
+	atomic_t num_deinit_resp;
+	atomic_t num_remote_teardown_ind;
+
+#endif
+};
+
+static void xshm_release(struct kref *kref)
+{
+	struct xshmchr_char_dev *dev;
+	dev = container_of(kref, struct xshmchr_char_dev, kref);
+	xdev_devl(dev, "Freeing device\n");
+	kfree(dev);
+}
+
+static void xshmchr_get(struct xshmchr_char_dev *dev)
+{
+	kref_get(&dev->kref);
+}
+
+static void xshmchr_put(struct xshmchr_char_dev *dev)
+{
+	kref_put(&dev->kref, xshm_release);
+}
+
+static inline unsigned int ringbuf_empty(struct ringbuf *rb)
+{
+	return *rb->wi == *rb->ri;
+}
+
+static inline unsigned int ringbuf_full(struct ringbuf *rb)
+{
+	return (le32_to_cpu(*rb->wi) + 1) % rb->size == le32_to_cpu(*rb->ri);
+}
+
+static int insert_ringbuf(struct ringbuf *rb, const char __user *from,
+		u32 len)
+{
+	u32 wi = le32_to_cpu(*rb->wi);
+	u32 ri = le32_to_cpu(*rb->ri);
+	u32 cpylen, cpylen2 = 0, notcpy;
+
+	pr_devel("insert: wi:%d ri:%d len:%d\n", wi, ri, len);
+	if (wi >= ri) {
+		len = min(len, rb->size - 1 - wi + ri);
+		cpylen = min(rb->size, wi + len) - wi;
+
+		/* Write is ahead of read, copy 'cpylen' data from 'wi' */
+		notcpy = copy_from_user(rb->data + wi, from, cpylen);
+		if (cpylen > 0 && notcpy == cpylen)
+			return -EIO;
+
+		if (cpylen < len && notcpy == 0) {
+			cpylen2 = min(ri - 1 , len - cpylen);
+
+			/* We have wrapped copy 'cpylen2' from start */
+			notcpy = copy_from_user(rb->data, from + cpylen,
+					cpylen2);
+		}
+	} else {
+		cpylen = min(ri - 1 - wi , len);
+
+		/* Read is ahead of write, copy from wi to (ri - 1) */
+		notcpy = copy_from_user(rb->data + wi, from, cpylen);
+		if (cpylen > 0 && notcpy == cpylen)
+			return -EIO;
+
+	}
+	/* Do write barrier before updating index */
+	smp_wmb();
+	*rb->wi = cpu_to_le32((wi + cpylen + cpylen2 - notcpy) % rb->size);
+	pr_devel("write ringbuf: wi: %d->%d l:%d\n",
+			wi, le32_to_cpu(*rb->wi), cpylen + cpylen2);
+	return cpylen + cpylen2 - notcpy;
+}
+
+static int extract_ringbuf(struct ringbuf *rb, void __user *to, u32 len)
+{
+	u32 wi = le32_to_cpu(*rb->wi);
+	u32 ri = le32_to_cpu(*rb->ri);
+	u32 cpylen = 0, cpylen2 = 0, notcpy;
+
+	pr_devel("extract: wi:%d ri:%d len:%d\n", wi, ri, len);
+	if (ri <= wi) {
+		len = min(wi - ri, len);
+
+		/* Read is ahead of write, copy 'len' data from 'ri' */
+		notcpy = copy_to_user(to, rb->data + ri, len);
+		if (len > 0 && notcpy == len)
+			return -EIO;
+
+		/* Do write barrier before updating index */
+		smp_wmb();
+		*rb->ri = cpu_to_le32(ri + len - notcpy);
+		pr_devel("read ringbuf: ri: %d->%d len:%d\n",
+			ri, le32_to_cpu(*rb->ri), len - notcpy);
+
+		return len - notcpy;
+	} else {
+		/* wr >= ri */
+		cpylen = min(rb->size - ri, len);
+
+		/* Write is ahead, copy 'cpylen' data from ri until end */
+		notcpy = copy_to_user(to, rb->data + ri, cpylen);
+		if (cpylen > 0 && notcpy == cpylen)
+			return -EIO;
+		if (cpylen < len && notcpy == 0) {
+			cpylen2 = min(wi , len - cpylen);
+			/* we have wrapped copy from [0 .. cpylen2] */
+			notcpy = copy_to_user(to + cpylen, rb->data, cpylen2);
+		}
+		/* Do write barrier before updating index */
+		smp_wmb();
+
+		*rb->ri = cpu_to_le32((ri + cpylen + cpylen2 - notcpy)
+						% rb->size);
+		pr_devel("read ringbuf: ri: %d->%d cpylen:%d\n",
+			ri, le32_to_cpu(*rb->ri), cpylen + cpylen2 - notcpy);
+
+		return cpylen + cpylen2 - notcpy;
+	}
+}
+
+static void drain_ringbuf(struct xshmchr_char_dev *dev)
+{
+	/* Empty the ringbuf. */
+	*dev->xshm->cfg.rx.read = *dev->xshm->cfg.rx.write;
+	*dev->xshm->cfg.tx.write = *dev->xshm->cfg.tx.read;
+}
+
+static int open_cb(void *drv)
+{
+	struct xshmchr_char_dev *dev = drv;
+
+	pr_xchrstate(dev, "enter");
+	dbfs_atomic_inc(&dev->num_init_resp);
+	/* Signal reader that data is available. */
+	WARN_ON(!STATE_IS_OPEN(dev));
+	SET_PENDING_OFF(dev);
+	wake_up_interruptible_all(&dev->mgmt_wq);
+	pr_xchrstate(dev, "exit");
+	return 0;
+}
+
+static void close_cb(void *drv)
+{
+	struct xshmchr_char_dev *dev = drv;
+
+	pr_xchrstate(dev, "enter");
+	dbfs_atomic_inc(&dev->num_remote_teardown_ind);
+	if (STATE_IS_PENDING(dev) && !STATE_IS_OPEN(dev)) {
+		/* Normal close sequence */
+		SET_PENDING_OFF(dev);
+		CLEAR_REMOTE_TEARDOWN(dev);
+		SET_EOF(dev);
+		drain_ringbuf(dev);
+		dev->file_mode = 0;
+	} else {
+		/* Remote teardown, close should be called from user-space */
+		SET_REMOTE_TEARDOWN(dev);
+		SET_PENDING_OFF(dev);
+	}
+
+	wake_up_interruptible_all(&dev->mgmt_wq);
+	pr_xchrstate(dev, "exit");
+}
+
+static int ipc_rx_cb(void *drv)
+{
+	struct xshmchr_char_dev *dev = drv;
+
+	xdev_devl(dev, "Enter\n");
+
+	if (unlikely(*dev->xshm->cfg.rx.state == cpu_to_le32(XSHM_CLOSED)))
+		return -ESHUTDOWN;
+
+	/*
+	 * Performance could perhaps be improved by having a WAIT
+	 * flag, similar to SOCK_ASYNC_WAITDATA, and only do wake up
+	 * when it's actually needed.
+	 */
+	wake_up_interruptible_all(&dev->mgmt_wq);
+	return 0;
+}
+
+static int ipc_tx_release_cb(void *drv)
+{
+	struct xshmchr_char_dev *dev = drv;
+
+	xdev_devl(dev, "Enter\n");
+	wake_up_interruptible_all(&dev->mgmt_wq);
+	return 0;
+}
+
+/* Device Read function called from Linux kernel */
+static ssize_t xshmchr_chrread(struct file *filp, char __user *buf,
+	 size_t count, loff_t *f_pos)
+{
+	unsigned int len = 0;
+	int result;
+	struct xshmchr_char_dev *dev = filp->private_data;
+	ssize_t ret = -EIO;
+
+	if (dev == NULL) {
+		xdev_dbg(dev, "private_data not set!\n");
+		return -EBADFD;
+	}
+
+	/* I want to be alone on dev (except status and queue) */
+	if (mutex_lock_interruptible(&dev->mutex)) {
+		xdev_dbg(dev, "mutex_lock_interruptible got signalled\n");
+		return -ERESTARTSYS;
+	}
+	xshmchr_get(dev);
+
+	if (!STATE_IS_OPEN(dev)) {
+		/* Device is closed or closing. */
+		if (!STATE_IS_PENDING(dev)) {
+			xdev_dbg(dev, "device is closed (by remote)\n");
+			ret = -ECONNRESET;
+		} else {
+			xdev_dbg(dev, "device is closing...\n");
+			ret = -EBADF;
+		}
+		goto read_error;
+	}
+
+	dbfs_atomic_inc(&dev->num_read);
+
+	/* Device is open or opening. */
+	if (STATE_IS_PENDING(dev)) {
+		xdev_devl(dev, "device is opening...\n");
+
+		dbfs_atomic_inc(&dev->num_read_block);
+		if (filp->f_flags & O_NONBLOCK) {
+			/* We can't block. */
+			xdev_dbg(dev, "exit: state pending and O_NONBLOCK\n");
+			ret = -EAGAIN;
+			goto read_error;
+		}
+
+		/*
+		 * To reach here client must do blocking open,
+		 * and start read() before open completes. This is
+		 * quite quirky, but let's handle it anyway.
+		 */
+		result =
+		    wait_event_interruptible(dev->mgmt_wq,
+					!STATE_IS_PENDING(dev) ||
+				    STATE_IS_REMOTE_TEARDOWN(dev));
+
+		if (result == -ERESTARTSYS) {
+			xdev_dbg(dev, "wait_event_interruptible"
+				 " woken by a signal (1)\n");
+			ret = -ERESTARTSYS;
+			goto read_error;
+		}
+		if (STATE_IS_REMOTE_TEARDOWN(dev)) {
+			xdev_dbg(dev, "received remote_shutdown indication (1)\n");
+			ret = -ESHUTDOWN;
+			goto read_error;
+		}
+	}
+
+	/* Block if we don't have any received buffers.
+	 * The queue has its own lock.
+	 */
+	while (ringbuf_empty(&dev->rx)) {
+
+		if (filp->f_flags & O_NONBLOCK) {
+			xdev_devl(dev, "exit: O_NONBLOCK\n");
+			ret = -EAGAIN;
+			goto read_error;
+		}
+
+		/* Let writers in. */
+		mutex_unlock(&dev->mutex);
+
+		xdev_devl(dev, "%s:wait for data\n", dev->name);
+		/* Block reader until data arrives or device is closed. */
+		if (wait_event_interruptible(dev->mgmt_wq,
+				!ringbuf_empty(&dev->rx)
+				|| STATE_IS_REMOTE_TEARDOWN(dev)
+				|| !STATE_IS_OPEN(dev)) == -ERESTARTSYS) {
+			xdev_devl(dev, "event_interruptible woken by "
+				 "a signal, signal_pending(current) = %d\n",
+				signal_pending(current));
+			return -ERESTARTSYS;
+		}
+
+		xdev_devl(dev, "%s:wakeup readq\n", dev->name);
+
+		if (STATE_IS_REMOTE_TEARDOWN(dev) && ringbuf_empty(&dev->rx)) {
+			if (!STATE_IS_EOF(dev)) {
+				xdev_dbg(dev, "First EOF OK\n");
+				SET_EOF(dev);
+				ret = 0;
+				goto error_nolock;
+			}
+			xdev_dbg(dev, "2'nd EOF - remote_shutdown\n");
+			ret = -ECONNRESET;
+			goto error_nolock;
+		}
+
+		/* I want to be alone on dev (except status and queue). */
+		if (mutex_lock_interruptible(&dev->mutex)) {
+			xdev_dbg(dev, "mutex_lock_interruptible"
+					" got signalled\n");
+			return -ERESTARTSYS;
+		}
+
+		if (!STATE_IS_OPEN(dev)) {
+			/* Someone closed the link, report error. */
+			xdev_dbg(dev, "remote end shutdown!\n");
+			ret = -EBADF;
+			goto read_error;
+		}
+	}
+
+	xdev_devl(dev, "%s:copy data\n", dev->name);
+	len = extract_ringbuf(&dev->rx, buf, count);
+	if (len <= 0) {
+		xdev_dbg(dev, "Extracting from ringbuf failed\n");
+		ret = -EINVAL;
+		goto read_error;
+	}
+
+	/* Signal to modem that data is read from ringbuf */
+
+	dev->xshm->ipc_rx_release(dev->xshm, false);
+
+	dbfs_atomic_add(len, &dev->num_read_bytes);
+	/* Let the others in. */
+	mutex_unlock(&dev->mutex);
+	xshmchr_put(dev);
+	return len;
+
+read_error:
+	mutex_unlock(&dev->mutex);
+error_nolock:
+	xshmchr_put(dev);
+	return ret;
+}
+
+/* Device write function called from Linux kernel (misc device) */
+static ssize_t xshmchr_chrwrite(struct file *filp, const char __user *buf,
+		      size_t count, loff_t *f_pos)
+{
+	struct xshmchr_char_dev *dev = filp->private_data;
+	ssize_t ret = -EIO;
+	int result;
+	uint len = 0;
+
+	if (dev == NULL) {
+		xdev_dbg(dev, "private_data not set!\n");
+		ret = -EBADFD;
+		goto write_error_no_unlock;
+	}
+
+	pr_xchrstate(dev, "Enter");
+
+	/* I want to be alone on dev (except status and queue). */
+	if (mutex_lock_interruptible(&dev->mutex)) {
+		xdev_dbg(dev, "mutex_lock_interruptible got signalled\n");
+		ret = -ERESTARTSYS;
+		goto write_error_no_unlock;
+	}
+	xshmchr_get(dev);
+
+	dbfs_atomic_inc(&dev->num_write);
+	if (!STATE_IS_OPEN(dev)) {
+		/* Device is closed or closing. */
+		if (!STATE_IS_PENDING(dev)) {
+			xdev_dbg(dev, "device is closed (by remote)\n");
+			ret = -EPIPE;
+		} else {
+			xdev_dbg(dev, "device is closing...\n");
+			ret = -EBADF;
+		}
+		goto write_error;
+	}
+
+	/* Device is open or opening. */
+	if (STATE_IS_PENDING(dev)) {
+		xdev_dbg(dev, "device is opening...\n");
+
+		dbfs_atomic_inc(&dev->num_write_block);
+		if (filp->f_flags & O_NONBLOCK) {
+			/* We can't block */
+			xdev_dbg(dev, "exit: state pending and O_NONBLOCK\n");
+			ret = -EAGAIN;
+			goto write_error;
+		}
+
+		/* Blocking mode; state is pending and we need to wait
+		 * for its conclusion. (Shutdown_ind set pending off.)
+		 */
+		result =
+		    wait_event_interruptible(dev->mgmt_wq,
+					!STATE_IS_PENDING(dev) ||
+					STATE_IS_REMOTE_TEARDOWN(dev));
+		if (result == -ERESTARTSYS) {
+			xdev_dbg(dev, "wait_event_interruptible"
+				 " woken by a signal (1)\n");
+			ret = -ERESTARTSYS;
+			goto write_error;
+		}
+	}
+	if (STATE_IS_REMOTE_TEARDOWN(dev)) {
+		xdev_dbg(dev, "received remote_shutdown indication\n");
+		ret = -EPIPE;
+		goto write_error;
+	}
+
+	while (ringbuf_full(&dev->tx)) {
+		/* Flow is off. Check non-block flag. */
+		if (filp->f_flags & O_NONBLOCK) {
+			xdev_dbg(dev, "exit: O_NONBLOCK and tx flow off");
+			ret = -EAGAIN;
+			goto write_error;
+		}
+
+		/* Let readers in. */
+		mutex_unlock(&dev->mutex);
+
+		xdev_devl(dev, "wait for write space\n");
+		/* Wait until flow is on or device is closed. */
+		if (wait_event_interruptible(dev->mgmt_wq,
+					!ringbuf_full(&dev->tx)
+					|| !STATE_IS_OPEN(dev)
+					|| STATE_IS_REMOTE_TEARDOWN(dev)
+					) == -ERESTARTSYS) {
+			xdev_dbg(dev, "wait_event_interruptible"
+				 " woken by a signal (1)\n");
+			ret = -ERESTARTSYS;
+			goto write_error_no_unlock;
+		}
+
+		/* I want to be alone on dev (except status and queue). */
+		if (mutex_lock_interruptible(&dev->mutex)) {
+			xdev_dbg(dev, "mutex_lock_interruptible "
+					"got signalled\n");
+			ret = -ERESTARTSYS;
+			goto write_error_no_unlock;
+		}
+
+		xdev_devl(dev, "wakeup got write space\n");
+		if (!STATE_IS_OPEN(dev)) {
+			/* Someone closed the link, report error. */
+			xdev_dbg(dev, "remote end shutdown!\n");
+			ret = -EPIPE;
+			goto write_error;
+		}
+		if (STATE_IS_REMOTE_TEARDOWN(dev)) {
+			xdev_dbg(dev, "received remote_shutdown indication\n");
+			ret = -ESHUTDOWN;
+			goto write_error;
+		}
+	}
+	len = insert_ringbuf(&dev->tx, buf, count);
+	xdev_devl(dev, "inserted %d bytes\n", len);
+	if (len <= 0) {
+		xdev_dbg(dev, "transmit failed, error = %d\n",
+				(int) ret);
+		goto write_error;
+	}
+
+	dbfs_atomic_add(len, &dev->num_write_bytes);
+
+	/* Signal to modem that data is put in ringbuf */
+	dev->xshm->ipc_tx(dev->xshm);
+
+	mutex_unlock(&dev->mutex);
+	xshmchr_put(dev);
+	return len;
+
+write_error:
+	mutex_unlock(&dev->mutex);
+write_error_no_unlock:
+	xshmchr_put(dev);
+	return ret;
+}
+
+static unsigned int xshmchr_chrpoll(struct file *filp, poll_table *waittab)
+{
+	struct xshmchr_char_dev *dev = filp->private_data;
+	unsigned int mask = 0;
+
+	if (dev == NULL) {
+		xdev_dbg(dev, "private_data not set!\n");
+		return -EBADFD;
+	}
+
+	/* I want to be alone on dev (except status and queue). */
+	if (mutex_lock_interruptible(&dev->mutex)) {
+		xdev_dbg(dev, "mutex_lock_interruptible got signalled\n");
+		mask |= POLLERR;
+		goto out;
+	}
+	xshmchr_get(dev);
+
+	if (STATE_IS_REMOTE_TEARDOWN(dev)) {
+		xdev_dbg(dev, "not open\n");
+		mask |= POLLRDHUP | POLLHUP;
+		goto out;
+	}
+
+	xdev_devl(dev, "%s: poll wait\n", dev->name);
+	poll_wait(filp, &dev->mgmt_wq, waittab);
+
+	if (STATE_IS_OPEN(dev) && STATE_IS_PENDING(dev))
+		goto out;
+
+	if (!ringbuf_empty(&dev->rx))
+		mask |= (POLLIN | POLLRDNORM);
+
+	if (!ringbuf_full(&dev->tx))
+		mask |= (POLLOUT | POLLWRNORM);
+
+out:
+	mutex_unlock(&dev->mutex);
+	xdev_devl(dev, "poll return mask=0x%04x\n", mask);
+	xshmchr_put(dev);
+	return mask;
+}
+
+/* Usage:
+ * minor >= 0 : find from minor
+ * minor < 0 and name == name : find from name
+ * minor < 0 and name == NULL : get first
+ */
+
+static struct xshmchr_char_dev *find_device(int minor, char *name,
+					 int remove_from_list)
+{
+	struct list_head *list_node;
+	struct list_head *n;
+	struct xshmchr_char_dev *dev = NULL;
+	struct xshmchr_char_dev *tmp;
+	spin_lock(&list_lock);
+	xdev_devl(dev, "start looping \n");
+	list_for_each_safe(list_node, n, &xshmchr_chrdev_list) {
+		tmp = list_entry(list_node, struct xshmchr_char_dev,
+				list_field);
+		if (minor >= 0) {	/* find from minor */
+			if (tmp->misc.minor == minor)
+				dev = tmp;
+
+		} else if (name) {	/* find from name */
+			if (!strncmp(tmp->name, name, sizeof(tmp->name)))
+				dev = tmp;
+		} else {	/* take first */
+			dev = tmp;
+		}
+
+		if (dev) {
+			xdev_devl(dev, "match %d, %s \n",
+				      minor, name);
+			if (remove_from_list)
+				list_del(list_node);
+			break;
+		}
+	}
+	spin_unlock(&list_lock);
+	return dev;
+}
+
+static int xshmchr_chropen(struct inode *inode, struct file *filp)
+{
+	struct xshmchr_char_dev *dev = NULL;
+	int result = -1;
+	int minor = iminor(inode);
+	int mode = 0;
+	int ret = -EIO;
+
+	dev = find_device(minor, NULL, 0);
+	pr_xchrstate(dev, "ENTER");
+
+	if (dev == NULL) {
+		xdev_dbg(dev, "Could not find device\n");
+		return -EBADF;
+	}
+
+	/* I want to be alone on dev (except status and queue). */
+	if (mutex_lock_interruptible(&dev->mutex)) {
+		xdev_dbg(dev, "mutex_lock_interruptible got signalled\n");
+		return -ERESTARTSYS;
+	}
+
+	xshmchr_get(dev);
+	dbfs_atomic_inc(&dev->num_open);
+	filp->private_data = dev;
+
+	switch (filp->f_flags & O_ACCMODE) {
+	case O_RDONLY:
+		mode = CHR_READ_FLAG;
+		break;
+	case O_WRONLY:
+		mode = CHR_WRITE_FLAG;
+		break;
+	case O_RDWR:
+		mode = CHR_READ_FLAG | CHR_WRITE_FLAG;
+		break;
+	}
+
+	/* If device is not open, make sure device is in fully closed state. */
+	if (!STATE_IS_OPEN(dev)) {
+		/* Has link close response been received
+		 * (if we ever sent it)?
+		 */
+		if (STATE_IS_PENDING(dev)) {
+			/* Still waiting for close response from remote.
+			 * If opened non-blocking, report "would block".
+			 */
+			if (filp->f_flags & O_NONBLOCK) {
+				xdev_devl(dev, "%s: exit: "
+						"O_NONBLOCK && close pending\n",
+						dev->name);
+				ret = -EAGAIN;
+				goto open_error;
+			}
+
+			xdev_devl(dev, "%s:WAIT for close response"
+					"from remote\n", dev->name);
+
+			/*
+			 * Blocking mode; close is pending and we need to wait
+			 * for its conclusion. However modem may be dead,
+			 * or resureccted and alive waiting for
+			 * an open ack.
+			 * It's hard to get this rigth - if state is
+			 * pending. We have missed a state update,
+			 * let's just wait for ack, and then proceede
+			 * with watever state we have.
+			 */
+			result =
+			    wait_event_interruptible_timeout(dev->mgmt_wq,
+					    !STATE_IS_PENDING(dev) ||
+					    STATE_IS_REMOTE_TEARDOWN(dev),
+					    OPEN_TOUT);
+
+			if (result == -ERESTARTSYS) {
+				xdev_dbg(dev, "%s:wait_event_interruptible"
+					" woken by a signal (1)\n", dev->name);
+				ret = -ERESTARTSYS;
+				goto open_error;
+			}
+
+			if (result == 0) {
+				SET_PENDING_OFF(dev);
+				pr_xchrstate(dev, "Timeout -pending close;"
+						"Clear pending");
+			} else
+				pr_xchrstate(dev, "wakeup (wait for close)");
+		}
+	}
+
+	/* Device is now either closed, pending open or open */
+	if (STATE_IS_OPEN(dev) && !STATE_IS_PENDING(dev)) {
+		/* Open */
+		xdev_devl(dev, "%s:Device is already opened (dev=%p) check"
+				"access f_flags = 0x%x file_mode = 0x%x\n",
+				dev->name, dev, mode, dev->file_mode);
+
+		if (mode & dev->file_mode) {
+			xdev_devl(dev, "%s:Access mode already in use 0x%x\n",
+					dev->name, 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(dev)) {
+			/* First opening of file; do connect */
+
+			SET_STATE_OPEN(dev);
+			SET_PENDING_ON(dev);
+			CLEAR_EOF(dev);
+			/* Send "open" by resetting indexes */
+			result = dev->xshm->open(dev->xshm);
+
+			if (result < 0) {
+				xdev_dbg(dev, "%s:can't open channel\n",
+						dev->name);
+				ret = -EIO;
+				SET_STATE_CLOSED(dev);
+				SET_PENDING_OFF(dev);
+				goto open_error;
+			}
+			dbfs_atomic_inc(&dev->num_init);
+		}
+
+		/* If opened non-blocking, report "success".
+		 */
+		if (filp->f_flags & O_NONBLOCK) {
+			xdev_devl(dev, "%s: EXIT: O_NONBLOCK success\n",
+					dev->name);
+			ret = 0;
+			goto open_success;
+		}
+
+		xdev_devl(dev, "%s:WAIT for connect response\n", dev->name);
+		/*
+		 * misc_open holds a global mutex anyway so there is no
+		 * reason to release our own while waiting
+		 */
+		result =
+		    wait_event_interruptible_timeout(dev->mgmt_wq,
+				    !STATE_IS_PENDING(dev) ||
+				    STATE_IS_REMOTE_TEARDOWN(dev),
+				    OPEN_TOUT);
+		if (result == 0) {
+			xdev_dbg(dev, "%s:wait_event_interruptible "
+					"timed out (1)\n", dev->name);
+			ret = -ETIMEDOUT;
+			goto open_error;
+		}
+		if (result == -ERESTARTSYS) {
+			xdev_dbg(dev, "%s:wait_event_interruptible"
+					" woken by a signal (2)\n", dev->name);
+			ret = -ERESTARTSYS;
+			goto open_error;
+		}
+		if (STATE_IS_REMOTE_TEARDOWN(dev)) {
+			xdev_dbg(dev, "received remote_shutdown indication\n");
+			ret = -ESHUTDOWN;
+			goto open_error;
+		}
+
+		pr_xchrstate(dev, "wakeup (wait for open)");
+		if (!STATE_IS_OPEN(dev)) {
+			/* Lower layers said "no". */
+			xdev_dbg(dev, "%s:xshmchr_chropen: Closed received\n",
+					dev->name);
+			ret = -EPIPE;
+			goto open_error;
+		}
+
+		xdev_devl(dev, "%s: connect received\n", dev->name);
+	}
+open_success:
+	/* Open is OK. */
+	dev->file_mode |= mode;
+
+	xdev_devl(dev, "%s: file mode = %x\n",
+			dev->name, dev->file_mode);
+	pr_xchrstate(dev, "EXIT");
+
+	mutex_unlock(&dev->mutex);
+	xshmchr_put(dev);
+	return 0;
+
+open_error:
+	SET_STATE_CLOSED(dev);
+	SET_PENDING_OFF(dev);
+	mutex_unlock(&dev->mutex);
+	xshmchr_put(dev);
+	return ret;
+}
+
+static int xshmchr_chrrelease(struct inode *inode, struct file *filp)
+{
+	struct xshmchr_char_dev *dev = NULL;
+	int minor = iminor(inode);
+	int mode = 0;
+
+
+	dev = find_device(minor, NULL, 0);
+	if (dev == NULL) {
+		xdev_dbg(dev, "Could not find device\n");
+		return -EBADF;
+	}
+
+	pr_xchrstate(dev, "enter");
+
+	/* I want to be alone on dev (except status queue). */
+	if (mutex_lock_interruptible(&dev->mutex)) {
+		xdev_dbg(dev, "mutex_lock_interruptible got signalled\n");
+		return -ERESTARTSYS;
+	}
+
+	xshmchr_get(dev);
+	dbfs_atomic_inc(&dev->num_close);
+
+	/* Is the device open? */
+	if (!STATE_IS_OPEN(dev)) {
+		xdev_devl(dev, "Device not open (dev=%p) \n",
+			      dev);
+		mutex_unlock(&dev->mutex);
+		xshmchr_put(dev);
+		return 0;
+	}
+
+	switch (filp->f_flags & O_ACCMODE) {
+	case O_RDONLY:
+		mode = CHR_READ_FLAG;
+		break;
+	case O_WRONLY:
+		mode = CHR_WRITE_FLAG;
+		break;
+	case O_RDWR:
+		mode = CHR_READ_FLAG | CHR_WRITE_FLAG;
+		break;
+	}
+
+	dev->file_mode &= ~mode;
+	if (dev->file_mode) {
+		xdev_devl(dev, "Device is kept open by someone else, "
+			 " don't close. XSHMCHR connection - file_mode = %x\n",
+			 dev->file_mode);
+		mutex_unlock(&dev->mutex);
+		xshmchr_put(dev);
+		return 0;
+	}
+
+	/* IS_CLOSED have double meaning:
+	 * 1) Spontanous Remote Shutdown Request.
+	 * 2) Ack on a channel teardown(disconnect)
+	 * Must clear bit, in case we previously received
+	 * a remote shudown request.
+	 */
+
+	SET_STATE_CLOSED(dev);
+	SET_PENDING_ON(dev);
+	CLEAR_REMOTE_TEARDOWN(dev);
+	SET_EOF(dev);
+
+	dev->xshm->close(dev->xshm);
+
+	dbfs_atomic_inc(&dev->num_deinit);
+
+	/* Empty the ringbuf */
+	drain_ringbuf(dev);
+	dev->file_mode = 0;
+
+	mutex_unlock(&dev->mutex);
+	pr_xchrstate(dev, "exit");
+	xshmchr_put(dev);
+	return 0;
+}
+
+static const struct file_operations xshmchr_chrfops = {
+	.owner = THIS_MODULE,
+	.read = xshmchr_chrread,
+	.write = xshmchr_chrwrite,
+	.open = xshmchr_chropen,
+	.release = xshmchr_chrrelease,
+	.poll = xshmchr_chrpoll,
+};
+
+static int cfshm_probe(struct platform_device *pdev)
+
+{
+	struct xshmchr_char_dev *dev = NULL;
+	int result;
+	struct xshm_dev *xshm = pdev->dev.platform_data;
+	xdev_devl(dev, "cfshm_probe called\n");
+
+	if (xshm == NULL)
+		return 0;
+
+	/* Allocate device */
+	dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+
+	if (!dev) {
+		pr_err("kmalloc failed.\n");
+		return -ENOMEM;
+	}
+
+	memset(dev, 0, sizeof(*dev));
+	kref_init(&dev->kref);
+
+	dev->xshm = xshm;
+	mutex_init(&dev->mutex);
+	init_waitqueue_head(&dev->mgmt_wq);
+
+	/* Fill in some information concerning the misc device. */
+	dev->misc.minor = MISC_DYNAMIC_MINOR;
+	if (strlen(xshm->cfg.name) == 0) {
+		xdev_dbg(dev, "Platform device does not have a name\n");
+		return -EINVAL;
+	}
+	sprintf(dev->name, "%s", xshm->cfg.name);
+	dev->misc.name = dev->name;
+	dev->misc.fops = &xshmchr_chrfops;
+
+	dev->tx.ri = xshm->cfg.tx.read;
+	dev->tx.wi = xshm->cfg.tx.write;
+	dev->tx.data = xshm->cfg.tx.addr;
+	dev->tx.size = xshm->cfg.tx.ch_size - 1;
+
+	dev->rx.ri = xshm->cfg.rx.read;
+	dev->rx.wi = xshm->cfg.rx.write;
+	dev->rx.data = xshm->cfg.rx.addr;
+	dev->rx.size = xshm->cfg.rx.ch_size - 1;
+	if (dev->rx.size < 2 || dev->tx.size < 2) {
+		dev->rx.size = 0;
+		dev->tx.size = 0;
+		xdev_dbg(dev, "dev:%s error - channel size too small\n",
+				dev->name);
+		return -EINVAL;
+	}
+	dev->xshm->ipc_rx_cb = ipc_rx_cb;
+	dev->xshm->ipc_tx_release_cb = ipc_tx_release_cb;
+	dev->xshm->open_cb = open_cb;
+	dev->xshm->close_cb = close_cb;
+	dev->xshm->driver_data = dev;
+
+	xdev_devl(dev, "register pdev:%s chr=%s(%s) dev=%p\n", xshm->pdev.name,
+			dev->name, xshm->cfg.name, dev);
+
+	/* Register the device. */
+	dev->misc.parent = &xshm->pdev.dev;
+	result = misc_register(&dev->misc);
+
+	/* Lock in order to try to stop someone from opening the device
+	 * too early. The misc device has its own lock. We cannot take our
+	 * lock until misc_register() is finished, because in open() the
+	 * locks are taken in this order (misc first and then dev).
+	 * So anyone managing to open the device between the misc_register
+	 * and the mutex_lock will get a "device not found" error. Don't
+	 * think it can be avoided.
+	 */
+	if (mutex_lock_interruptible(&dev->mutex)) {
+		xdev_dbg(dev, "mutex_lock_interruptible got signalled\n");
+		return -ERESTARTSYS;
+	}
+
+	if (result < 0) {
+		pr_warn("XSHMCHR: chnl_chr: error - %d, can't register misc.\n",
+			      result);
+		mutex_unlock(&dev->mutex);
+		goto err_failed;
+	}
+
+	xdev_devl(dev, "XSHMCHR: dev: "
+			"Registered dev with name=%s minor=%d, dev=%p\n",
+			dev->misc.name, dev->misc.minor, dev->misc.this_device);
+
+	SET_STATE_CLOSED(dev);
+	SET_PENDING_OFF(dev);
+	CLEAR_REMOTE_TEARDOWN(dev);
+	CLEAR_EOF(dev);
+
+	/* Add the device. */
+	spin_lock(&list_lock);
+	list_add(&dev->list_field, &xshmchr_chrdev_list);
+	spin_unlock(&list_lock);
+
+#ifdef CONFIG_DEBUG_FS
+	if (debugfsdir != NULL) {
+		dev->debugfs_device_dir =
+		    debugfs_create_dir(dev->misc.name, debugfsdir);
+		debugfs_create_u32("conn_state", S_IRUSR | S_IWUSR,
+				   dev->debugfs_device_dir, &dev->conn_state);
+		debugfs_create_u32("num_open", S_IRUSR | S_IWUSR,
+				   dev->debugfs_device_dir,
+				   (u32 *) &dev->num_open);
+		debugfs_create_u32("num_close", S_IRUSR | S_IWUSR,
+				   dev->debugfs_device_dir,
+				   (u32 *) &dev->num_close);
+		debugfs_create_u32("num_init", S_IRUSR | S_IWUSR,
+				   dev->debugfs_device_dir,
+				   (u32 *) &dev->num_init);
+		debugfs_create_u32("num_init_resp", S_IRUSR | S_IWUSR,
+				   dev->debugfs_device_dir,
+				   (u32 *) &dev->num_init_resp);
+		debugfs_create_u32("num_deinit", S_IRUSR | S_IWUSR,
+				   dev->debugfs_device_dir,
+				   (u32 *) &dev->num_deinit);
+		debugfs_create_u32("num_deinit_resp", S_IRUSR | S_IWUSR,
+				   dev->debugfs_device_dir,
+				   (u32 *) &dev->num_deinit_resp);
+		debugfs_create_u32("num_remote_teardown_ind",
+				   S_IRUSR | S_IWUSR, dev->debugfs_device_dir,
+				   (u32 *) &dev->num_remote_teardown_ind);
+		debugfs_create_u32("num_read",
+				   S_IRUSR | S_IWUSR, dev->debugfs_device_dir,
+				   (u32 *) &dev->num_read);
+		debugfs_create_u32("num_read_block",
+				   S_IRUSR | S_IWUSR, dev->debugfs_device_dir,
+				   (u32 *) &dev->num_read_block);
+		debugfs_create_u32("num_read_bytes",
+				   S_IRUSR | S_IWUSR, dev->debugfs_device_dir,
+				   (u32 *) &dev->num_read_bytes);
+		debugfs_create_u32("num_write",
+				   S_IRUSR | S_IWUSR, dev->debugfs_device_dir,
+				   (u32 *) &dev->num_write);
+		debugfs_create_u32("num_write_block",
+				   S_IRUSR | S_IWUSR, dev->debugfs_device_dir,
+				   (u32 *) &dev->num_write_block);
+		debugfs_create_u32("num_write_bytes",
+				   S_IRUSR | S_IWUSR, dev->debugfs_device_dir,
+				   (u32 *) &dev->num_write_bytes);
+
+		debugfs_create_u32("rx_write_index",
+				   S_IRUSR | S_IWUSR, dev->debugfs_device_dir,
+				(u32 *) dev->xshm->cfg.rx.write);
+		debugfs_create_u32("rx_read_index",
+				   S_IRUSR | S_IWUSR, dev->debugfs_device_dir,
+				(u32 *) dev->xshm->cfg.rx.read);
+		debugfs_create_u32("rx_state",
+				   S_IRUSR | S_IWUSR, dev->debugfs_device_dir,
+				(u32 *) dev->xshm->cfg.rx.state);
+		debugfs_create_u32("tx_write_index",
+				   S_IRUSR | S_IWUSR, dev->debugfs_device_dir,
+				(u32 *) dev->xshm->cfg.tx.write);
+		debugfs_create_u32("tx_read_index",
+				   S_IRUSR | S_IWUSR, dev->debugfs_device_dir,
+				(u32 *) dev->xshm->cfg.tx.read);
+		debugfs_create_u32("tx_state",
+				   S_IRUSR | S_IWUSR, dev->debugfs_device_dir,
+				(u32 *) dev->xshm->cfg.tx.state);
+
+	}
+#endif
+	mutex_unlock(&dev->mutex);
+	return 0;
+err_failed:
+	xshmchr_put(dev);
+	return result;
+}
+
+static int chrdev_remove(struct xshmchr_char_dev *dev)
+{
+	if (!dev)
+		return -EBADF;
+
+	if (STATE_IS_OPEN(dev)) {
+		xdev_dbg(dev, "Device is opened "
+			 "(dev=%p) file_mode = 0x%x\n",
+			 dev, dev->file_mode);
+		SET_STATE_CLOSED(dev);
+		SET_PENDING_OFF(dev);
+		wake_up_interruptible_all(&dev->mgmt_wq);
+	}
+
+	if (mutex_lock_interruptible(&dev->mutex)) {
+		xdev_dbg(dev, "mutex_lock_interruptible got signalled\n");
+		xshmchr_put(dev);
+		return -ERESTARTSYS;
+	}
+
+	drain_ringbuf(dev);
+
+	misc_deregister(&dev->misc);
+
+	/* Remove from list. */
+	list_del(&dev->list_field);
+
+#ifdef CONFIG_DEBUG_FS
+	if (dev->debugfs_device_dir != NULL)
+		debugfs_remove_recursive(dev->debugfs_device_dir);
+#endif
+
+	mutex_unlock(&dev->mutex);
+	xshmchr_put(dev);
+	return 0;
+}
+
+static int cfshm_remove(struct platform_device *pdev)
+{
+	int err;
+	struct xshm_dev *xshm = pdev->dev.platform_data;
+
+	if (xshm == NULL)
+		return 0;
+	pr_devel("unregister pdev:%s chr=%s pdev=%p\n", xshm->pdev.name,
+			xshm->cfg.name, pdev);
+
+	err = chrdev_remove(xshm->driver_data);
+	if (err)
+		pr_debug("removing char-dev:%s failed.%d\n",
+					xshm->cfg.name, err);
+
+	xshm->ipc_rx_cb = NULL;
+	xshm->ipc_tx_release_cb = NULL;
+	xshm->open_cb = NULL;
+	xshm->close_cb = NULL;
+	xshm->driver_data = NULL;
+	return err;
+}
+
+static struct platform_driver cfshm_plat_drv = {
+	.probe = cfshm_probe,
+	.remove = cfshm_remove,
+	.driver = {
+		   .name = "xshms",
+		   .owner = THIS_MODULE,
+		   },
+};
+
+
+static int __init xshmchr_chrinit_module(void)
+{
+	int err;
+	pr_devel("xshm init\n");
+	spin_lock_init(&list_lock);
+
+	/* Register platform driver. */
+	err = platform_driver_register(&cfshm_plat_drv);
+	if (err) {
+		printk(KERN_ERR "Could not register platform SHM driver: %d.\n",
+			err);
+		goto err_dev_register;
+	}
+
+#ifdef CONFIG_DEBUG_FS
+	debugfsdir = debugfs_create_dir("xshm_chr", NULL);
+#endif
+
+ err_dev_register:
+	return err;
+
+}
+
+static void __exit xshmchr_chrexit_module(void)
+{
+	int result;
+	struct xshmchr_char_dev *dev = NULL;
+
+	/* Unregister platform driver. */
+	platform_driver_unregister(&cfshm_plat_drv);
+
+	do {
+		/* Remove any device (the first in the list). */
+		dev = find_device(-1, NULL, 0);
+		result = chrdev_remove(dev);
+	} while (result == 0);
+
+#ifdef CONFIG_DEBUG_FS
+	if (debugfsdir != NULL)
+		debugfs_remove_recursive(debugfsdir);
+#endif
+
+}
+
+module_init(xshmchr_chrinit_module);
+module_exit(xshmchr_chrexit_module);
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 8/9] xshm: Makefile and Kconfig for M7400 Shared Memory Drivers
  2011-12-07  9:27 [PATCH 0/9] XSHM: Shared Memory Driver for ST-E Thor M7400 LTE modem Sjur Brændeland
                   ` (6 preceding siblings ...)
  2011-12-07  9:28 ` [PATCH 7/9] xshm: Character device for XSHM channel access Sjur Brændeland
@ 2011-12-07  9:28 ` Sjur Brændeland
  2011-12-07 12:57   ` Linus Walleij
  2011-12-07  9:28 ` [PATCH 9/9] caif-xshm: Add CAIF driver for Shared memory for M7400 Sjur Brændeland
                   ` (2 subsequent siblings)
  10 siblings, 1 reply; 26+ messages in thread
From: Sjur Brændeland @ 2011-12-07  9:28 UTC (permalink / raw)
  To: linux-kernel; +Cc: Linus Walleij, Paul Bolle, Sjur Brændeland

Signed-off-by: Sjur Brændeland <sjur.brandeland@stericsson.com>
---
 drivers/Kconfig       |    2 ++
 drivers/Makefile      |    1 +
 drivers/xshm/Kconfig  |   17 +++++++++++++++++
 drivers/xshm/Makefile |    3 +++
 4 files changed, 23 insertions(+), 0 deletions(-)
 create mode 100644 drivers/xshm/Kconfig
 create mode 100644 drivers/xshm/Makefile

diff --git a/drivers/Kconfig b/drivers/Kconfig
index 041426c..742f028 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -138,4 +138,6 @@ source "drivers/virt/Kconfig"
 
 source "drivers/devfreq/Kconfig"
 
+source "drivers/xshm/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 91077ac..30eae54 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -121,6 +121,7 @@ obj-$(CONFIG_VLYNQ)		+= vlynq/
 obj-$(CONFIG_STAGING)		+= staging/
 obj-y				+= platform/
 obj-y				+= ieee802154/
+obj-$(CONFIG_XSHM)		+= xshm/
 #common clk code
 obj-y				+= clk/
 
diff --git a/drivers/xshm/Kconfig b/drivers/xshm/Kconfig
new file mode 100644
index 0000000..80daca4
--- /dev/null
+++ b/drivers/xshm/Kconfig
@@ -0,0 +1,17 @@
+# XSHM gets selected by whoever wants it.
+config XSHM
+	depends on CONFIG_C2C
+	tristate
+
+config XSHM_CHR
+	tristate "Character device for External Shared Memory (XSHM)"
+	select XSHM
+	default n
+	---help---
+	Say "Y" to use a character device for the External Shared
+	Memory (XSHM) IPC mechanism. XSHM is an IPC protocol used to
+	talk to external device such as modem over a shared memory
+	(e.g. Chip to Chip).
+	Only say "M" here if you want to test XSHM and need to load
+	and unload its module.
+	If unsure say N.
diff --git a/drivers/xshm/Makefile b/drivers/xshm/Makefile
new file mode 100644
index 0000000..954bd22
--- /dev/null
+++ b/drivers/xshm/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_XSHM) += xshm.o
+xshm-objs := xshm_boot.o xshm_dev.o
+obj-$(CONFIG_XSHM_CHR) +=  xshm_chr.o
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 9/9] caif-xshm: Add CAIF driver for Shared memory for M7400
  2011-12-07  9:27 [PATCH 0/9] XSHM: Shared Memory Driver for ST-E Thor M7400 LTE modem Sjur Brændeland
                   ` (7 preceding siblings ...)
  2011-12-07  9:28 ` [PATCH 8/9] xshm: Makefile and Kconfig for M7400 Shared Memory Drivers Sjur Brændeland
@ 2011-12-07  9:28 ` Sjur Brændeland
  2011-12-07 12:30 ` [PATCH 0/9] XSHM: Shared Memory Driver for ST-E Thor M7400 LTE modem Arnd Bergmann
  2011-12-07 12:50 ` [PATCH 0/9] XSHM: Shared Memory Driver for ST-E Thor M7400 LTE modem Linus Walleij
  10 siblings, 0 replies; 26+ messages in thread
From: Sjur Brændeland @ 2011-12-07  9:28 UTC (permalink / raw)
  To: linux-kernel
  Cc: Linus Walleij, Paul Bolle, Sjur Brændeland, David S. Miller

This patch introduces a caif shared memory link layer driver
for ST-Ericsson's Thor M7400 LTE modem.

M7400 uses a ring-buffer in shared memory for transporting data from the modem.
Each ring-buffer element contains an array of caif frames. caif_xshm calls
napi_schedule() when receiving notification about incoming data.
The napi-poll function copies data from the ring-buffer to SKBs until
ring-buffer is empty, or quota is exceeded.

If transmit ring-buffer is full, it also uses napi for scheduling transmission
of queued transmit buffer.

cc: David S. Miller <davem@davemloft.net>
Acked-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sjur Brændeland <sjur.brandeland@stericsson.com>
---
 drivers/net/caif/Kconfig     |   10 +
 drivers/net/caif/Makefile    |    1 +
 drivers/net/caif/caif_xshm.c |  935 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 946 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/caif/caif_xshm.c

diff --git a/drivers/net/caif/Kconfig b/drivers/net/caif/Kconfig
index abf4d7a..bc03700 100644
--- a/drivers/net/caif/Kconfig
+++ b/drivers/net/caif/Kconfig
@@ -47,3 +47,13 @@ config CAIF_HSI
        The caif low level driver for CAIF over HSI.
        Be aware that if you enable this then you also need to
        enable a low-level HSI driver.
+
+config CAIF_XSHM
+	tristate "CAIF external memory protocol driver"
+	depends on XSHM && CAIF
+	default n
+	---help---
+	Say "Y" if you want to support CAIF over External Shared Memory (XSHM)
+	IPC mechanism (e.g. over Chip to Chip). Only say M here if you want to
+	test CAIF over XSHM and need to load and unload its module.
+	If unsure say N.
diff --git a/drivers/net/caif/Makefile b/drivers/net/caif/Makefile
index 91dff86..9310b24 100644
--- a/drivers/net/caif/Makefile
+++ b/drivers/net/caif/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_CAIF_SPI_SLAVE) += cfspi_slave.o
 # Shared memory
 caif_shm-objs := caif_shmcore.o caif_shm_u5500.o
 obj-$(CONFIG_CAIF_SHM) += caif_shm.o
+obj-$(CONFIG_CAIF_XSHM) += caif_xshm.o
 
 # HSI interface
 obj-$(CONFIG_CAIF_HSI) += caif_hsi.o
diff --git a/drivers/net/caif/caif_xshm.c b/drivers/net/caif/caif_xshm.c
new file mode 100644
index 0000000..35cff94
--- /dev/null
+++ b/drivers/net/caif/caif_xshm.c
@@ -0,0 +1,935 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Contact: Sjur Brendeland / sjur.brandeland@stericsson.com
+ * Authors: Sjur Brendeland / sjur.brandeland@stericsson.com
+ *	   Daniel Martensson / daniel.martensson@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": %s() :" fmt, __func__
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/netdevice.h>
+#include <net/rtnetlink.h>
+#include <linux/if_arp.h>
+#include <net/caif/caif_device.h>
+#include <net/caif/caif_layer.h>
+#include <linux/xshm/xshm_pdev.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Daniel Martensson <daniel.martensson@stericsson.com>");
+MODULE_AUTHOR("Sjur Brendeland <sjur.brandeland@stericsson.com>");
+MODULE_DESCRIPTION("CAIF SHM driver");
+
+#define CONNECT_TIMEOUT (3 * HZ)
+#define CAIF_NEEDED_HEADROOM	32
+#define CAIF_FLOW_ON		1
+#define CAIF_FLOW_OFF		0
+
+#define LOW_XOFF_WATERMARK	50
+#define HIGH_XOFF_WATERMARK	70
+#define STUFF_MARK		30
+
+struct ringbuf {
+	__le32	*rip;
+	__le32	*wip;
+	u32	size;
+	__le32	*bufsize;
+};
+
+struct shm_pck_desc {
+	/* Offset from start of channel to CAIF frame. */
+	u32 offset;
+	u32 length;
+} __packed;
+
+struct shm_caif_frm {
+	/* Number of bytes of padding before the CAIF frame. */
+	u8 hdr_ofs;
+} __packed;
+
+#define SHM_HDR_LEN sizeof(struct shm_caif_frm)
+
+struct shmbuffer {
+/* Static part: */
+	u8 *addr;
+	u32 index;
+	u32 len;
+/* Dynamic part: */
+	u32 frames;
+	/* Offset from start of buffer to CAIF frame. */
+	u32 frm_ofs;
+};
+
+enum CFSHM_STATE {
+	CFSHM_CLOSED = 1,
+	CFSHM_OPENING,
+	CFSHM_OPEN
+};
+
+struct cfshm {
+	/* caif_dev_common must always be first in the structure*/
+	struct caif_dev_common cfdev;
+	struct xshm_dev *xshm;
+	struct napi_struct napi;
+	struct ringbuf tx;
+	struct sk_buff_head sk_qhead;
+	spinlock_t lock;
+	struct ringbuf rx;
+	u8 *rx_ringbuf;
+	u32 rx_frms_pr_buf;
+	u32 rx_alignment;
+	struct shmbuffer **rx_bufs;
+	struct net_device *ndev;
+
+	u32 tx_frms_pr_buf;
+	u32 tx_alignment;
+	struct shmbuffer **tx_bufs;
+	u8 *tx_ringbuf;
+	u32 tx_flow_on;
+	u32 high_xoff_water;
+	u32 low_xoff_water;
+	u32 stuff_mark;
+	atomic_t dbg_smp_rxactive;
+	enum CFSHM_STATE state;
+	struct platform_device *pdev;
+	struct list_head node;
+	wait_queue_head_t netmgmt_wq;
+};
+
+static LIST_HEAD(cfshm_list);
+static spinlock_t cfshm_list_lock;
+
+static unsigned int ringbuf_used(struct ringbuf *rb)
+{
+	if (le32_to_cpu(*rb->wip) >= le32_to_cpu(*rb->rip))
+		return le32_to_cpu(*rb->wip) - le32_to_cpu(*rb->rip);
+	else
+		return rb->size - le32_to_cpu(*rb->rip) + le32_to_cpu(*rb->wip);
+}
+
+static int ringbuf_get_writepos(struct ringbuf *rb)
+{
+	if ((le32_to_cpu(*rb->wip) + 1) % rb->size == le32_to_cpu(*rb->rip))
+		return -1;
+	else
+		return le32_to_cpu(*rb->wip);
+}
+
+static int ringbuf_get_readpos(struct ringbuf *rb)
+{
+
+	if (le32_to_cpu(*rb->wip) == le32_to_cpu(*rb->rip))
+		return -1;
+	else
+		return le32_to_cpu(*rb->rip);
+}
+
+static int ringbuf_upd_writeptr(struct ringbuf *rb)
+{
+	if (!WARN_ON((le32_to_cpu(*rb->wip) + 1) % rb->size == le32_to_cpu(*rb->rip))) {
+		*rb->wip = cpu_to_le32((le32_to_cpu(*rb->wip) + 1) % rb->size);
+		/* Do write barrier before updating index */
+		smp_wmb();
+	}
+	return le32_to_cpu(*rb->wip);
+}
+
+static void ringbuf_upd_readptr(struct ringbuf *rb)
+{
+	if (!WARN_ON(le32_to_cpu(*rb->wip) == le32_to_cpu(*rb->rip))) {
+		*rb->rip = cpu_to_le32((le32_to_cpu(*rb->rip) + 1) % rb->size);
+		/* Do write barrier before updating index */
+		smp_wmb();
+	}
+}
+
+
+
+static struct shmbuffer *get_rx_buf(struct cfshm *cfshm)
+{
+	struct shmbuffer *pbuf = NULL;
+	int idx = ringbuf_get_readpos(&cfshm->rx);
+
+	if (idx < 0)
+		goto out;
+	pbuf = cfshm->rx_bufs[idx];
+out:
+	return pbuf;
+}
+
+static struct shmbuffer *new_rx_buf(struct cfshm *cfshm)
+{
+	struct shmbuffer *pbuf = get_rx_buf(cfshm);
+
+	WARN_ON(!spin_is_locked(&cfshm->lock));
+	if (pbuf)
+		pbuf->frames = 0;
+
+	return pbuf;
+}
+
+static struct shmbuffer *get_tx_buf(struct cfshm *cfshm)
+{
+	int idx = ringbuf_get_writepos(&cfshm->tx);
+
+	if (idx < 0)
+		return NULL;
+	return cfshm->tx_bufs[idx];
+}
+
+inline struct shmbuffer *tx_bump_buf(struct cfshm *cfshm,
+			struct shmbuffer *pbuf)
+{
+	u32 desc_size;
+	struct shmbuffer *newpbuf = pbuf;
+
+	WARN_ON(!spin_is_locked(&cfshm->lock));
+	if (pbuf) {
+		cfshm->xshm->cfg.tx.buf_size[pbuf->index] =
+			cpu_to_le32(pbuf->frm_ofs);
+		ringbuf_upd_writeptr(&cfshm->tx);
+		newpbuf = get_tx_buf(cfshm);
+		/* Reset buffer parameters. */
+		desc_size = (cfshm->tx_frms_pr_buf + 1) *
+			sizeof(struct shm_pck_desc);
+		pbuf->frm_ofs = desc_size + (desc_size % cfshm->rx_alignment);
+		pbuf->frames = 0;
+
+	}
+	return newpbuf;
+}
+
+static struct shmbuffer *shm_rx_func(struct cfshm *cfshm, int quota)
+{
+	struct shmbuffer *pbuf;
+	struct sk_buff *skb;
+	int ret;
+	unsigned long flags;
+
+	pbuf = get_rx_buf(cfshm);
+	while (pbuf) {
+		/* Retrieve pointer to start of the packet descriptor area. */
+		struct shm_pck_desc *pck_desc =
+			((struct shm_pck_desc *) pbuf->addr) + pbuf->frames;
+		u32 offset;
+
+		/* Loop until descriptor contains zero offset */
+		while ((offset = pck_desc->offset)) {
+			unsigned int caif_len;
+			struct shm_caif_frm *frm;
+			u32 length = pck_desc->length;
+			u8 hdr_ofs;
+			frm = (struct shm_caif_frm *)(pbuf->addr + offset);
+			hdr_ofs = frm->hdr_ofs;
+			caif_len =
+				length - SHM_HDR_LEN -
+				hdr_ofs;
+
+			pr_devel("copy data buf:%d frm:%d offs:%d @%x len:%d\n",
+					pbuf->index, pbuf->frames, offset,
+					(u32) (SHM_HDR_LEN + hdr_ofs + offset +
+						pbuf->addr - cfshm->rx_ringbuf),
+					length);
+
+			/* Check whether number of frames is below limit */
+			if (pbuf->frames > cfshm->rx_frms_pr_buf) {
+				pr_warn("Too many frames in buffer.\n");
+				++cfshm->ndev->stats.rx_frame_errors;
+				goto desc_err;
+			}
+
+			/* Check whether offset is below low limits */
+			if (pbuf->addr + offset
+					<= (u8 *)(pck_desc + 1)) {
+				pr_warn("Offset in desc. below buffer area.\n");
+				++cfshm->ndev->stats.rx_frame_errors;
+				goto desc_err;
+			}
+
+			/* Check whether offset above upper limit */
+			if (offset + length > pbuf->len) {
+				pr_warn("Offset outside buffer area:\n");
+				++cfshm->ndev->stats.rx_frame_errors;
+				goto desc_err;
+			}
+
+			skb = netdev_alloc_skb(cfshm->ndev,
+							caif_len + 1);
+			if (skb == NULL) {
+				pr_debug("Couldn't allocate SKB\n");
+				++cfshm->ndev->stats.rx_dropped;
+				goto out;
+			}
+
+			memcpy(skb_put(skb, caif_len),
+					SHM_HDR_LEN + hdr_ofs +
+					offset + pbuf->addr,
+					caif_len);
+
+			skb->protocol = htons(ETH_P_CAIF);
+			skb_reset_mac_header(skb);
+			skb->dev = cfshm->ndev;
+
+			/* Push received packet up the stack. */
+			ret = netif_receive_skb(skb);
+
+			if (!ret) {
+				cfshm->ndev->stats.rx_packets++;
+				cfshm->ndev->stats.rx_bytes +=
+					length;
+			} else
+				++cfshm->ndev->stats.rx_dropped;
+			/* Move to next packet descriptor. */
+			pck_desc++;
+
+			pbuf->frames++;
+			if (--quota <= 0) {
+				pr_devel("Quota exeeded (pbuf:%p)\n", pbuf);
+				goto out;
+			}
+		}
+desc_err:
+		pbuf->frames = 0;
+
+		spin_lock_irqsave(&cfshm->lock, flags);
+		ringbuf_upd_readptr(&cfshm->rx);
+		pbuf = new_rx_buf(cfshm);
+		spin_unlock_irqrestore(&cfshm->lock, flags);
+
+	}
+	cfshm->xshm->ipc_rx_release(cfshm->xshm, false);
+out:
+	return pbuf;
+}
+
+static int insert_skb_in_buf(struct cfshm *cfshm, struct sk_buff *skb,
+					struct shmbuffer *pbuf)
+{
+	struct shm_pck_desc *pck_desc;
+	unsigned int frmlen;
+	struct shm_caif_frm *frm;
+	u8 hdr_ofs;
+	struct caif_payload_info *info = (struct caif_payload_info *)&skb->cb;
+
+	WARN_ON(!spin_is_locked(&cfshm->lock));
+
+	if (unlikely(pbuf->frames >= cfshm->tx_frms_pr_buf)) {
+		pr_devel("-ENOSPC exeeded frames: %d >= %d\n",
+				pbuf->frames, cfshm->tx_frms_pr_buf);
+		return -ENOSPC;
+	}
+
+	/*
+	 * Align the address of the entire CAIF frame (incl padding),
+	 * so the modem can do efficient DMA of this frame
+	 * FIXME: Alignment is power of to, so it could use binary ops.
+	 */
+	pbuf->frm_ofs = roundup(pbuf->frm_ofs, cfshm->tx_alignment);
+
+
+	/* Make the payload (IP packet) inside the frame aligned */
+	hdr_ofs = (unsigned long) &pbuf->frm_ofs;
+	hdr_ofs = roundup(hdr_ofs + SHM_HDR_LEN + info->hdr_len,
+			cfshm->tx_alignment);
+
+	frm = (struct shm_caif_frm *)
+		(pbuf->addr + pbuf->frm_ofs);
+
+	frmlen = SHM_HDR_LEN + hdr_ofs + skb->len;
+
+	/*
+	 * Verify that packet, header and additional padding
+	 * can fit within the buffer frame area.
+	 */
+	if (pbuf->len < pbuf->frm_ofs + frmlen) {
+		pr_devel("-ENOSPC exeeded offset %d < %d\n",
+				pbuf->len, pbuf->frm_ofs + frmlen);
+		return -ENOSPC;
+	}
+
+	/* Copy in CAIF frame. */
+	frm->hdr_ofs = hdr_ofs;
+	skb_copy_bits(skb, 0, pbuf->addr +
+			pbuf->frm_ofs + SHM_HDR_LEN +
+			hdr_ofs, skb->len);
+
+	pr_devel("copy data buf:%d frm:%d offs:%d @%d len:%d\n",
+			pbuf->index, pbuf->frames,
+			pbuf->frm_ofs,
+			(u32) (pbuf->addr + pbuf->frm_ofs +
+				SHM_HDR_LEN + hdr_ofs - cfshm->tx_ringbuf),
+			skb->len);
+
+	cfshm->ndev->stats.tx_packets++;
+	cfshm->ndev->stats.tx_bytes += frmlen;
+	/* Fill in the shared memory packet descriptor area. */
+	pck_desc = (struct shm_pck_desc *) (pbuf->addr);
+	/* Forward to current frame. */
+	pck_desc += pbuf->frames;
+	pck_desc->offset = pbuf->frm_ofs;
+	pck_desc->length = frmlen;
+	/* Terminate packet descriptor area. */
+	pck_desc++;
+	pck_desc->offset = 0;
+	pck_desc->length = 0;
+	/* Update buffer parameters. */
+	pbuf->frames++;
+	pbuf->frm_ofs += frmlen;
+
+	return 0;
+}
+
+static struct shmbuffer *queue_to_ringbuf(struct cfshm *cfshm, int *new_bufs)
+{
+	struct shmbuffer *pbuf;
+	struct sk_buff *skb;
+	int err;
+
+	WARN_ON(!spin_is_locked(&cfshm->lock));
+
+	pbuf = get_tx_buf(cfshm);
+	while (pbuf != NULL) {
+		skb = skb_peek(&cfshm->sk_qhead);
+		if (skb == NULL)
+			break;
+		err = insert_skb_in_buf(cfshm, skb, pbuf);
+		if (unlikely(err == -ENOSPC)) {
+			pr_devel("No more space in buffer\n");
+			++(*new_bufs);
+			pbuf = tx_bump_buf(cfshm, pbuf);
+			continue;
+		}
+		skb = skb_dequeue(&cfshm->sk_qhead);
+		/* We're always in NET_*_SOFTIRQ */
+		dev_kfree_skb(skb);
+	}
+	return pbuf;
+}
+
+static int shm_netdev_open(struct net_device *netdev)
+{
+	struct cfshm *cfshm = netdev_priv(netdev);
+	int ret, err = 0;
+
+	cfshm->state = CFSHM_OPENING;
+	if (cfshm->xshm != NULL && cfshm->xshm->open != NULL)
+		err = cfshm->xshm->open(cfshm->xshm);
+	if (err)
+		goto error;
+
+	rtnl_unlock();  /* Release RTNL lock during connect wait */
+	ret = wait_event_interruptible_timeout(cfshm->netmgmt_wq,
+			cfshm->state != CFSHM_OPENING,
+			CONNECT_TIMEOUT);
+	rtnl_lock();
+
+	if (ret == 0) {
+		pr_debug("connect timeout\n");
+		err = -ETIMEDOUT;
+		goto error;
+	}
+
+	if (cfshm->state !=  CFSHM_OPEN) {
+		pr_debug("connect failed\n");
+		err = -ECONNREFUSED;
+		goto error;
+	}
+
+	napi_enable(&cfshm->napi);
+	return 0;
+error:
+	if (cfshm->xshm != NULL && cfshm->xshm->close != NULL)
+		cfshm->xshm->close(cfshm->xshm);
+	return err;
+}
+
+static int shm_netdev_close(struct net_device *netdev)
+{
+	struct cfshm *cfshm = netdev_priv(netdev);
+
+	napi_disable(&cfshm->napi);
+
+	if (cfshm->xshm != NULL && cfshm->xshm->close != NULL)
+		cfshm->xshm->close(cfshm->xshm);
+
+	return 0;
+}
+
+static int open_cb(void *drv)
+{
+	struct cfshm *cfshm = drv;
+
+	cfshm->state = CFSHM_OPEN;
+	netif_carrier_on(cfshm->ndev);
+	wake_up_interruptible(&cfshm->netmgmt_wq);
+	return 0;
+}
+
+static void close_cb(void *drv)
+{
+	struct cfshm *cfshm = drv;
+
+	cfshm->state = CFSHM_CLOSED;
+	netif_carrier_off(cfshm->ndev);
+	wake_up_interruptible(&cfshm->netmgmt_wq);
+}
+
+static int caif_shmdrv_rx_cb(void *drv)
+{
+	struct cfshm *cfshm = drv;
+
+	if (unlikely(*cfshm->xshm->cfg.rx.state == cpu_to_le32(XSHM_CLOSED)))
+		return -ESHUTDOWN;
+
+	napi_schedule(&cfshm->napi);
+	return 0;
+}
+
+static int send_pending_txbufs(struct cfshm *cfshm, int usedbufs)
+{
+	unsigned long flags;
+
+	/* Send the started buffer if used buffers are low enough */
+	WARN_ON(!spin_is_locked(&cfshm->lock));
+	if (likely(usedbufs < cfshm->stuff_mark)) {
+		struct shmbuffer *pbuf = get_tx_buf(cfshm);
+		if (unlikely(pbuf->frames > 0)) {
+			if (spin_trylock_irqsave(&cfshm->lock, flags)) {
+				WARN_ON(!spin_is_locked(&cfshm->lock));
+				pbuf = get_tx_buf(cfshm);
+				tx_bump_buf(cfshm, pbuf);
+				spin_unlock_irqrestore(&cfshm->lock, flags);
+				cfshm->xshm->ipc_tx(cfshm->xshm);
+				return 0;
+			} else {
+				return -EBUSY;
+			}
+		}
+	}
+	return 0;
+}
+
+static int caif_shmdrv_tx_release_cb(void *drv)
+{
+	struct cfshm *cfshm = drv;
+	int usedbufs;
+
+	usedbufs = ringbuf_used(&cfshm->tx);
+
+	/* Send flow-on if we have sent flow-off and get below low-water */
+	if (usedbufs <= cfshm->low_xoff_water && !cfshm->tx_flow_on) {
+		pr_debug("Flow on\n");
+		cfshm->tx_flow_on = true;
+		cfshm->cfdev.flowctrl(cfshm->ndev, CAIF_FLOW_ON);
+	}
+
+	/* If ringbuf is full, schedule NAPI to start sending */
+	if (skb_peek(&cfshm->sk_qhead) != NULL) {
+		pr_debug("Schedule NAPI to empty queue\n");
+		napi_schedule(&cfshm->napi);
+		return 0;
+	}
+
+	/* Send the started buffer if used buffers are low enough */
+	if (usedbufs < cfshm->stuff_mark) {
+		struct shmbuffer *pbuf = get_tx_buf(cfshm);
+		if (pbuf != NULL && pbuf->frames > 0)
+			napi_schedule(&cfshm->napi);
+	}
+	return 0;
+}
+
+static int shm_rx_poll(struct napi_struct *napi, int quota)
+{
+	struct cfshm *cfshm = container_of(napi, struct cfshm, napi);
+	int new_bufs;
+	struct shmbuffer *pbuf;
+	int usedbufs;
+	unsigned long flags;
+
+	/* Simply return if rx_poll is already called on other CPU */
+	if (atomic_read(&cfshm->dbg_smp_rxactive) > 0)
+		return quota;
+
+	WARN_ON(atomic_inc_return(&cfshm->dbg_smp_rxactive) > 1);
+
+	pbuf = shm_rx_func(cfshm, quota);
+
+	usedbufs = ringbuf_used(&cfshm->tx);
+
+	if (spin_trylock_irqsave(&cfshm->lock, flags)) {
+
+		/* Check if we're below "Stuff" limit, and send pending data */
+		send_pending_txbufs(cfshm, usedbufs);
+
+		/* Check if we have queued packets */
+		if (unlikely(skb_peek(&cfshm->sk_qhead) != NULL)) {
+			struct shmbuffer *txbuf;
+			WARN_ON(!spin_is_locked(&cfshm->lock));
+			pr_debug("Try to empty tx-queue\n");
+			new_bufs = 0;
+			txbuf = queue_to_ringbuf(cfshm, &new_bufs);
+
+			/* Bump out if we are configured with few buffers */
+			if (txbuf && cfshm->xshm->cfg.tx.buffers < 3) {
+				tx_bump_buf(cfshm, txbuf);
+
+				spin_unlock_irqrestore(&cfshm->lock, flags);
+				cfshm->xshm->ipc_tx(cfshm->xshm);
+				goto txdone;
+			}
+		}
+		spin_unlock_irqrestore(&cfshm->lock, flags);
+	}
+txdone:
+
+	if (pbuf == NULL)
+		napi_complete(&cfshm->napi);
+
+	atomic_dec(&cfshm->dbg_smp_rxactive);
+	return 0;
+}
+
+static int shm_netdev_tx(struct sk_buff *skb, struct net_device *shm_netdev)
+{
+	struct shmbuffer *pbuf = NULL;
+	int usedbufs;
+	int new_bufs = 0;
+	struct cfshm *cfshm = netdev_priv(shm_netdev);
+	unsigned long flags;
+
+	/*
+	 * If we have packets in queue, keep queueing to avoid
+	 * out-of-order delivery
+	 */
+	spin_lock_irqsave(&cfshm->lock, flags);
+
+	skb_queue_tail(&cfshm->sk_qhead, skb);
+	pbuf = queue_to_ringbuf(cfshm, &new_bufs);
+
+	usedbufs = ringbuf_used(&cfshm->tx);
+
+	if (usedbufs > cfshm->high_xoff_water && cfshm->tx_flow_on) {
+		pr_debug("Flow off\n");
+		cfshm->tx_flow_on = false;
+		spin_unlock_irqrestore(&cfshm->lock, flags);
+		cfshm->cfdev.flowctrl(cfshm->ndev, CAIF_FLOW_OFF);
+		return 0;
+	}
+
+	/* Check if we should accumulate more packets */
+	if (new_bufs == 0 && usedbufs > cfshm->stuff_mark) {
+		spin_unlock_irqrestore(&cfshm->lock, flags);
+		return 0;
+	}
+	tx_bump_buf(cfshm, pbuf);
+	spin_unlock_irqrestore(&cfshm->lock, flags);
+	cfshm->xshm->ipc_tx(cfshm->xshm);
+	return 0;
+}
+
+static const struct net_device_ops netdev_ops = {
+	.ndo_open = shm_netdev_open,
+	.ndo_stop = shm_netdev_close,
+	.ndo_start_xmit = shm_netdev_tx,
+};
+
+static void shm_netdev_setup(struct net_device *pshm_netdev)
+{
+	struct cfshm *cfshm;
+
+	cfshm = netdev_priv(pshm_netdev);
+	pshm_netdev->netdev_ops = &netdev_ops;
+	pshm_netdev->type = ARPHRD_CAIF;
+	pshm_netdev->hard_header_len = CAIF_NEEDED_HEADROOM;
+	pshm_netdev->tx_queue_len = 0;
+	pshm_netdev->destructor = free_netdev;
+
+	/* Initialize structures in a clean state. */
+	memset(cfshm, 0, sizeof(struct cfshm));
+}
+
+static void deinit_bufs(struct cfshm *cfshm)
+{
+	int j;
+
+	if (cfshm == NULL)
+		return;
+
+	for (j = 0; j < cfshm->xshm->cfg.rx.buffers; j++)
+		kfree(cfshm->rx_bufs[j]);
+	kfree(cfshm->rx_bufs);
+
+	for (j = 0; j < cfshm->xshm->cfg.tx.buffers; j++)
+		kfree(cfshm->tx_bufs[j]);
+	kfree(cfshm->tx_bufs);
+}
+
+static int cfshm_probe(struct platform_device *pdev)
+{
+	int err, j;
+	struct xshm_dev *xshm = pdev->dev.platform_data;
+	struct cfshm *cfshm = NULL;
+	struct net_device *netdev;
+	u32 buf_size;
+	unsigned long flags;
+
+	if (xshm == NULL)
+		return -EINVAL;
+	if (xshm->cfg.tx.addr == NULL || xshm->cfg.rx.addr == NULL) {
+		pr_debug("Shared Memory are not configured\n");
+		return -EINVAL;
+	}
+
+	if (xshm->cfg.tx.ch_size / xshm->cfg.tx.buffers <
+			xshm->cfg.tx.packets * sizeof(struct shm_pck_desc) +
+				xshm->cfg.tx.mtu) {
+		pr_warn("Bad packet TX-channel size");
+		return -EINVAL;
+	}
+
+	if (xshm->cfg.rx.ch_size / xshm->cfg.rx.buffers <
+			sizeof(struct shm_pck_desc) + xshm->cfg.rx.mtu) {
+		pr_warn("Bad packet RX-channel size");
+		return -EINVAL;
+	}
+
+	if (xshm->cfg.rx.buffers < 2 || xshm->cfg.tx.buffers < 2) {
+		pr_warn("Too few buffers in channel");
+		return -EINVAL;
+	}
+
+	err = -ENOMEM;
+	netdev = alloc_netdev(sizeof(struct cfshm), xshm->cfg.name,
+			shm_netdev_setup);
+
+	if (netdev == NULL)
+		goto error;
+
+	cfshm = netdev_priv(netdev);
+	cfshm->state = CFSHM_CLOSED;
+	init_waitqueue_head(&cfshm->netmgmt_wq);
+
+	cfshm->xshm = xshm;
+	xshm->driver_data = cfshm;
+	cfshm->ndev = netdev;
+	netdev->mtu = xshm->cfg.tx.mtu;
+	cfshm->high_xoff_water =
+		(xshm->cfg.rx.buffers * HIGH_XOFF_WATERMARK) / 100;
+	cfshm->low_xoff_water =
+		(xshm->cfg.rx.buffers * LOW_XOFF_WATERMARK) / 100;
+	cfshm->stuff_mark = (xshm->cfg.rx.buffers * STUFF_MARK) / 100;
+
+	cfshm->tx_frms_pr_buf = xshm->cfg.tx.packets;
+	cfshm->rx_frms_pr_buf = xshm->cfg.rx.packets;
+	cfshm->rx_alignment = xshm->cfg.rx.alignment;
+	cfshm->tx_alignment = xshm->cfg.tx.alignment;
+
+	if (xshm->cfg.latency)
+		cfshm->cfdev.link_select = CAIF_LINK_LOW_LATENCY;
+	else
+		cfshm->cfdev.link_select = CAIF_LINK_HIGH_BANDW;
+
+	cfshm->tx.rip = xshm->cfg.tx.read;
+	cfshm->tx.wip = xshm->cfg.tx.write;
+	cfshm->tx.bufsize = xshm->cfg.tx.buf_size;
+	cfshm->tx.size = xshm->cfg.tx.buffers;
+
+	cfshm->rx.rip = xshm->cfg.rx.read;
+	cfshm->rx.wip = xshm->cfg.rx.write;
+	cfshm->rx.bufsize = xshm->cfg.rx.buf_size;
+	cfshm->rx.size = xshm->cfg.rx.buffers;
+	pr_devel("RX ri:%d wi:%d size:%d\n",
+		le32_to_cpu(*cfshm->rx.rip),
+			le32_to_cpu(*cfshm->rx.wip), cfshm->rx.size);
+	pr_devel("TX ri:%d wi:%d size:%d\n",
+		le32_to_cpu(*cfshm->tx.rip),
+			le32_to_cpu(*cfshm->tx.wip), cfshm->rx.size);
+	pr_devel("frms_pr_buf:%d %d\n", cfshm->rx_frms_pr_buf,
+			cfshm->tx_frms_pr_buf);
+
+	spin_lock_init(&cfshm->lock);
+	netif_carrier_off(netdev);
+	skb_queue_head_init(&cfshm->sk_qhead);
+
+	pr_devel("SHM DEVICE[%p] PROBED BY DRIVER, NEW SHM DRIVER"
+			" INSTANCE AT cfshm =0x%p\n",
+			cfshm->xshm, cfshm);
+
+	cfshm->tx_ringbuf = xshm->cfg.tx.addr;
+	cfshm->rx_ringbuf = xshm->cfg.rx.addr;
+
+	pr_devel("TX-BASE:%p RX-BASE:%p\n",
+			cfshm->tx_ringbuf,
+			cfshm->rx_ringbuf);
+
+	cfshm->tx_bufs = kzalloc(sizeof(struct shmbuffer *) *
+			xshm->cfg.tx.buffers, GFP_KERNEL);
+	if (cfshm->tx_bufs == NULL)
+		goto error;
+	buf_size = xshm->cfg.tx.ch_size / xshm->cfg.tx.buffers;
+
+	pr_devel("TX: buffers:%d buf_size:%d frms:%d mtu:%d\n",
+			xshm->cfg.tx.buffers, buf_size,
+			cfshm->tx_frms_pr_buf, netdev->mtu);
+
+	for (j = 0; j < xshm->cfg.tx.buffers; j++) {
+		u32 desc_size;
+		struct shmbuffer *tx_buf =
+				kzalloc(sizeof(struct shmbuffer), GFP_KERNEL);
+
+		if (tx_buf == NULL) {
+			pr_warn("ERROR, Could not"
+					" allocate dynamic mem. for tx_buf, "
+					" Bailing out ...\n");
+			goto error;
+		}
+
+		tx_buf->index = j;
+
+		tx_buf->addr = cfshm->tx_ringbuf + (buf_size * j);
+		tx_buf->len = buf_size;
+		tx_buf->frames = 0;
+		desc_size = (cfshm->tx_frms_pr_buf + 1) *
+				sizeof(struct shm_pck_desc);
+
+		tx_buf->frm_ofs = desc_size + (desc_size % cfshm->tx_alignment);
+
+		cfshm->tx_bufs[j] = tx_buf;
+
+		pr_devel("tx_buf[%d] addr:%p len:%d\n",
+				tx_buf->index,
+				tx_buf->addr,
+				tx_buf->len);
+	}
+
+	cfshm->rx_bufs = kzalloc(sizeof(struct shmbuffer *) *
+				xshm->cfg.rx.buffers, GFP_KERNEL);
+	if (cfshm->rx_bufs == NULL)
+		goto error;
+	buf_size = xshm->cfg.tx.ch_size / xshm->cfg.tx.buffers;
+	pr_devel("RX: buffers:%d buf_size:%d frms:%d mtu:%d\n",
+			xshm->cfg.rx.buffers, buf_size,
+			cfshm->rx_frms_pr_buf, netdev->mtu);
+
+	for (j = 0; j < xshm->cfg.rx.buffers; j++) {
+		struct shmbuffer *rx_buf =
+				kzalloc(sizeof(struct shmbuffer), GFP_KERNEL);
+
+		if (rx_buf == NULL) {
+			pr_warn("ERROR, Could not"
+					" allocate dynamic mem.for rx_buf, "
+					" Bailing out ...\n");
+			goto error;
+		}
+
+		rx_buf->index = j;
+
+		rx_buf->addr = cfshm->rx_ringbuf + (buf_size * j);
+		rx_buf->len = buf_size;
+		cfshm->rx_bufs[j] = rx_buf;
+		pr_devel("rx_buf[%d] addr:%p len:%d\n",
+				rx_buf->index,
+				rx_buf->addr,
+				rx_buf->len);
+	}
+
+	cfshm->tx_flow_on = 1;
+	cfshm->xshm->ipc_rx_cb = caif_shmdrv_rx_cb;
+	cfshm->xshm->ipc_tx_release_cb = caif_shmdrv_tx_release_cb;
+	cfshm->xshm->open_cb = open_cb;
+	cfshm->xshm->close_cb = close_cb;
+
+	spin_lock_irqsave(&cfshm->lock, flags);
+	get_tx_buf(cfshm);
+	new_rx_buf(cfshm);
+	spin_unlock_irqrestore(&cfshm->lock, flags);
+
+	netif_napi_add(netdev, &cfshm->napi, shm_rx_poll,
+			2 * cfshm->rx_frms_pr_buf);
+
+	err = register_netdev(netdev);
+	if (err) {
+		pr_warn("ERROR[%d], SHM could not, "
+			"register with NW FRMWK Bailing out ...\n", err);
+		goto error;
+	}
+
+	/* Add CAIF SHM device to list. */
+	spin_lock(&cfshm_list_lock);
+	list_add_tail(&cfshm->node, &cfshm_list);
+	spin_unlock(&cfshm_list_lock);
+
+	return err;
+error:
+	deinit_bufs(cfshm);
+	free_netdev(netdev);
+	return err;
+}
+
+static int cfshm_remove(struct platform_device *pdev)
+{
+	struct xshm_dev *xshm;
+	struct cfshm *cfshm;
+
+	xshm = pdev->dev.platform_data;
+
+	if (xshm == NULL || xshm->driver_data == NULL)
+		return 0;
+
+	cfshm = xshm->driver_data;
+
+	spin_lock(&cfshm_list_lock);
+	list_del(&cfshm->node);
+	spin_unlock(&cfshm_list_lock);
+
+	deinit_bufs(cfshm);
+
+	unregister_netdev(cfshm->ndev);
+
+	xshm->ipc_rx_cb = NULL;
+	xshm->ipc_tx_release_cb = NULL;
+	xshm->open_cb = NULL;
+	xshm->close_cb = NULL;
+	xshm->driver_data = NULL;
+
+	return 0;
+}
+
+static struct platform_driver cfshm_plat_drv = {
+	.probe = cfshm_probe,
+	.remove = cfshm_remove,
+	.driver = {
+		.name = "xshmp",
+		.owner = THIS_MODULE,
+	},
+};
+
+static void __exit cfshm_exit_module(void)
+{
+	platform_driver_unregister(&cfshm_plat_drv);
+}
+
+static int __init cfshm_init_module(void)
+{
+	int err;
+
+	spin_lock_init(&cfshm_list_lock);
+
+	err = platform_driver_register(&cfshm_plat_drv);
+	if (err) {
+		printk(KERN_ERR "Could not register platform SHM driver: %d.\n",
+			err);
+		goto err_dev_register;
+	}
+	return err;
+
+ err_dev_register:
+	return err;
+}
+
+module_init(cfshm_init_module);
+module_exit(cfshm_exit_module);
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCH 0/9] XSHM: Shared Memory Driver for ST-E Thor M7400 LTE modem
  2011-12-07  9:27 [PATCH 0/9] XSHM: Shared Memory Driver for ST-E Thor M7400 LTE modem Sjur Brændeland
                   ` (8 preceding siblings ...)
  2011-12-07  9:28 ` [PATCH 9/9] caif-xshm: Add CAIF driver for Shared memory for M7400 Sjur Brændeland
@ 2011-12-07 12:30 ` Arnd Bergmann
  2011-12-07 15:44   ` Sjur BRENDELAND
  2011-12-07 12:50 ` [PATCH 0/9] XSHM: Shared Memory Driver for ST-E Thor M7400 LTE modem Linus Walleij
  10 siblings, 1 reply; 26+ messages in thread
From: Arnd Bergmann @ 2011-12-07 12:30 UTC (permalink / raw)
  To: Sjur Brændeland; +Cc: linux-kernel, Linus Walleij, Paul Bolle

Hi Sjur,

I've tried to do a review of your code, but I really need to understand
more of the background first, to be able to tell if it's using the
right high-level approach. There are a lot of details I could
comment on, but let's first look at the overall design.

On Wednesday 07 December 2011, Sjur Brændeland wrote:
> Introduction:
> ~~~~~~~~~~~~~
> This patch-set introduces the Shared Memory Driver for ST-Ericsson's
> Thor M7400 LTE modem.
> The shared memory model is implemented for Chip-to-chip and
> uses a reserved memory area and a number of bi-directional
> channels. Each channel has it's own designated data area where payload
> data is copied into.

Can you explain what the scope of this framework is? What I don't
understand from the code is whether this is meant to be very
broadly applicable to a lot of devices and competing with e.g.
rpmsg from Ohad Ben-Cohen, or whether this is really speciific
to one hardware implementation for caif.

Also, to what degree is the protocol design flexible? Is the modem
side fixed, so you have to live with any shortcomings of the interface,
or are you at this point able to improve both sides when something
is found to be lacking?

> Two different channel types are defined, one stream channel which is
> implemented as a traditional ring-buffer, and a packet channel which
> is a ring-buffer of fix sized buffers where each buffer contains
> an array of CAIF frames.
> 
> The notification of read and write index updates are handled in a
> separate driver called c2c_genio. This driver will be contributed separately,
> but the API is included in this patch-set.
> 
> The channel configuration is stored in shared memory, each channel
> has a designated area in shared memory for configuration data,
> read and write indexes and a data area.

How many channels will there be on of each type in a typical system,
and respectively in the maximum you can realistically expect over
the next 10 years?

> Stream data
> ~~~~~~~~~~~
> The driver for the stream channel is implemented as a character device
> interface to user space. The character device implements non-blocking open
> and non-blocking IO in general. The character device is implementing
> a traditional circular buffer directly in the shared memory region for
> the channel.

It seems unusual to have both a socket interface and a character device
interface. What is the motivation behind this? Why can't you use the
socket for non-blocking I/O?

> Driver model
> ~~~~~~~~~~~~~~
> Current implementation is using the platform bus for XSHM devices.
> The packet channels are named "xshmp", and stream channel "xshms".
>
> /sys/devices:
> |-- platform
> |   |-- uevent
> |   `-- xshm
> |       |-- bootimg
> |       |-- caif_ready
> |       |-- ipc_ready
> |       |-- subsystem -> ../../../bus/platform
> |       |-- xshmp.1
> |       |   |-- driver -> ../../../../bus/platform/drivers/xshmp
> |       |   |-- subsystem -> ../../../../bus/platform
> |       `-- xshms.0
> |           |-- driver -> ../../../../bus/platform/drivers/xshms
> |           |-- misc
> |           |   `-- xshm0
> |           |       |-- device -> ../../../xshms.0
> |           |       |-- subsystem -> ../../../../../../class/misc
> `-- virtual
>     |-- net
>     |   |-- cfshm0

Why are the channels /platform/ devices? With all the infrastructure
you have in the xshm driver, I would think that you should be
able to probe the available channels yourself instead of relying the
board to define them. If you cannot, that is something that I think
needs to be fixed in the interface to the modem. If you can, you
probably should not be using the platform_bus but instead create
your own bus_type.

	Arnd

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 0/9] XSHM: Shared Memory Driver for ST-E Thor M7400 LTE modem
  2011-12-07  9:27 [PATCH 0/9] XSHM: Shared Memory Driver for ST-E Thor M7400 LTE modem Sjur Brændeland
                   ` (9 preceding siblings ...)
  2011-12-07 12:30 ` [PATCH 0/9] XSHM: Shared Memory Driver for ST-E Thor M7400 LTE modem Arnd Bergmann
@ 2011-12-07 12:50 ` Linus Walleij
  10 siblings, 0 replies; 26+ messages in thread
From: Linus Walleij @ 2011-12-07 12:50 UTC (permalink / raw)
  To: Sjur Brændeland; +Cc: linux-kernel

In order not to cause build troubles I had to apply this patch:

>From 429471bfdc25b4773e157bb2aca02cb0033c824b Mon Sep 17 00:00:00 2001
From: Linus Walleij <linus.walleij@linaro.org>
Date: Wed, 7 Dec 2011 13:45:52 +0100
Subject: [PATCH] Fixup build by default-building dummy genio

Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
 drivers/xshm/Makefile |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/xshm/Makefile b/drivers/xshm/Makefile
index 954bd22..9670960 100644
--- a/drivers/xshm/Makefile
+++ b/drivers/xshm/Makefile
@@ -1,3 +1,3 @@
 obj-$(CONFIG_XSHM) += xshm.o
-xshm-objs := xshm_boot.o xshm_dev.o
+xshm-objs := xshm_boot.o xshm_dev.o genio_dummy.o
 obj-$(CONFIG_XSHM_CHR) +=  xshm_chr.o
-- 
1.7.3.2

I think that you need to teach the driver to dynamically look
for a genio driver so you don't get compilation errors,
then it will find any genio driver or the dummy driver if that
is compiled in.

At the least you need a Kconfig and Makefile entry for the
dummy_genio driver...

Yours,
Linus Walleij

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCH 8/9] xshm: Makefile and Kconfig for M7400 Shared Memory Drivers
  2011-12-07  9:28 ` [PATCH 8/9] xshm: Makefile and Kconfig for M7400 Shared Memory Drivers Sjur Brændeland
@ 2011-12-07 12:57   ` Linus Walleij
  0 siblings, 0 replies; 26+ messages in thread
From: Linus Walleij @ 2011-12-07 12:57 UTC (permalink / raw)
  To: Sjur Brændeland; +Cc: linux-kernel, Paul Bolle

2011/12/7 Sjur Brændeland <sjur.brandeland@stericsson.com>:

Some commit blurb here pls.

> Signed-off-by: Sjur Brændeland <sjur.brandeland@stericsson.com>
> ---
(...)
> diff --git a/drivers/xshm/Kconfig b/drivers/xshm/Kconfig
> new file mode 100644
> index 0000000..80daca4
> --- /dev/null
> +++ b/drivers/xshm/Kconfig
> @@ -0,0 +1,17 @@
> +# XSHM gets selected by whoever wants it.
> +config XSHM
> +       depends on CONFIG_C2C
> +       tristate
> +
> +config XSHM_CHR
> +       tristate "Character device for External Shared Memory (XSHM)"
> +       select XSHM
> +       default n
> +       ---help---
> +       Say "Y" to use a character device for the External Shared
> +       Memory (XSHM) IPC mechanism. XSHM is an IPC protocol used to
> +       talk to external device such as modem over a shared memory
> +       (e.g. Chip to Chip).
> +       Only say "M" here if you want to test XSHM and need to load
> +       and unload its module.
> +       If unsure say N.

As mentioned this needs some Kconfig entry to enable the
dummy driver.

This will appear right in the root menu of the driver config,
and I think it is more apropriate to hide this from systems that
do not have xshm, so check for how I did in drivers/pinctrl for
example like this:

config XSHM
        bool
        depends on EXPERIMENTAL

if XSHM

menu "XSHM drivers and interfaces"
        depends on XSHM

... your previous entries here ...

endmenu

endif

This will hide everything until an arch issues
select XSHM in it's say arch/arm/mach-foo/Kconfig.

> diff --git a/drivers/xshm/Makefile b/drivers/xshm/Makefile
> new file mode 100644
> index 0000000..954bd22
> --- /dev/null
> +++ b/drivers/xshm/Makefile
> @@ -0,0 +1,3 @@
> +obj-$(CONFIG_XSHM) += xshm.o
> +xshm-objs := xshm_boot.o xshm_dev.o
> +obj-$(CONFIG_XSHM_CHR) +=  xshm_chr.o

Also include a build rule for the dummy driver here.

Thanks,
Linus Walleij

^ permalink raw reply	[flat|nested] 26+ messages in thread

* RE: [PATCH 0/9] XSHM: Shared Memory Driver for ST-E Thor M7400 LTE modem
  2011-12-07 12:30 ` [PATCH 0/9] XSHM: Shared Memory Driver for ST-E Thor M7400 LTE modem Arnd Bergmann
@ 2011-12-07 15:44   ` Sjur BRENDELAND
  2011-12-09 14:42     ` Arnd Bergmann
  0 siblings, 1 reply; 26+ messages in thread
From: Sjur BRENDELAND @ 2011-12-07 15:44 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linux-kernel, Linus Walleij, Paul Bolle

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="utf-8", Size: 6540 bytes --]

Hi Arnd,

> I've tried to do a review of your code, but I really need to understand
> more of the background first, to be able to tell if it's using the
> right high-level approach. There are a lot of details I could
> comment on, but let's first look at the overall design.

Ok great, looking forward to hearing your feedback.
 
> > Introduction:
> > ~~~~~~~~~~~~~
> > This patch-set introduces the Shared Memory Driver for ST-Ericsson's
> > Thor M7400 LTE modem.
> > The shared memory model is implemented for Chip-to-chip and
> > uses a reserved memory area and a number of bi-directional
> > channels. Each channel has it's own designated data area where
> payload
> > data is copied into.
> 
> Can you explain what the scope of this framework is? What I don't
> understand from the code is whether this is meant to be very
> broadly applicable to a lot of devices and competing with e.g.
> rpmsg from Ohad Ben-Cohen, or whether this is really speciific
> to one hardware implementation for caif.

This is primarily designed for supporting the ST-Ericsson's M7400 product
and future modem platforms supporting shared memory.
There are some parts with a rather tight coupling to the modem such as
synchronization of the startup phases "ipc_ready" and "caif_ready" and
how we store the 1st stage boot images in shared memory.
However there are other concepts and parts of the implementation 
that probably is possible to reuse.

> Also, to what degree is the protocol design flexible? Is the modem
> side fixed, so you have to live with any shortcomings of the interface,
> or are you at this point able to improve both sides when something
> is found to be lacking?

As I see it, this interface to the modem is pretty much set, and isn't
going to change a lot for the M7400 product.

However for the long term perspective: we expect this interface to evolve
for future products, so suggestions and input for improvements is welcome.
rpmsg or at least the use of virtio-ring combined with a true end-to-end
zero copy is something we definitely are interested to look into for the
future.

> > Two different channel types are defined, one stream channel which is
> > implemented as a traditional ring-buffer, and a packet channel which
> > is a ring-buffer of fix sized buffers where each buffer contains
> > an array of CAIF frames.
> >
> > The notification of read and write index updates are handled in a
> > separate driver called c2c_genio. This driver will be contributed
> separately,
> > but the API is included in this patch-set.
> >
> > The channel configuration is stored in shared memory, each channel
> > has a designated area in shared memory for configuration data,
> > read and write indexes and a data area.
> 
> How many channels will there be on of each type in a typical system,
> and respectively in the maximum you can realistically expect over
> the next 10 years?

Currently we have defined the following channels:

Ch#	Type		Used for				RX size	TX size
------------------------------------------------------------------
1	Stream	Modem Crash dump			64 kB		4 kB
2	Stream	Flash-less Boot Protocol	4 kB		256 kB
3	Stream	Boot Logging			8 kB		4kB
4	Packet	CAIF high speed 			8*16 kB	8*16kB
5	Packet	CAIF low latency			4*1kB		4*1kB

The modem is doing a multi stage boot, where it initially reads
boot images stored in shared memory. This will boot-strap the
2nd boot stage where the Flash-less Boot Protocol is used.
In this phase channel #2 and #3 is used. At this stage only a
very basic OS-less runtime system is available, so CAIF protocol
cannot be running on the modem.

When the 2nd boot stage is complete, channel #4 and #5 will be used.
CAIF is a multiplexing protocol allowing multiple connections,
so the reason for two channels is to be able to separate high
throughput traffic such as IP + Logging from traffic with low-latency
requirements such as control (e.g. AT commands) and audio.

In case of modem crash-dump the modem is running in OS-less mode, and 
transfers dump over channel #1.

Initially I have implemented separate memory areas for
the different channels and the initial boot images, however
the channel specification and netlink interface supports
overlapping channels. This is a possible future improvement.

> > Stream data
> > ~~~~~~~~~~~
> > The driver for the stream channel is implemented as a character device
> > interface to user space. The character device implements non-blocking open
> > and non-blocking IO in general. The character device is implementing
> > a traditional circular buffer directly in the shared memory region for
> > the channel.
> 
> It seems unusual to have both a socket interface and a character device
> interface. What is the motivation behind this? Why can't you use the
> socket for non-blocking I/O?

As mentioned above, the modem is in different run modes, stream device
is only used when modem runs in OS-less mode, during boot or crash-dump.
Except for boot and crash-dump the CAIF stack is used.
A similar approach is used for HSI, where we use a bare
HSI channel in OS-less mode, and CAIF over HSI otherwise.

> > Driver model
> > ~~~~~~~~~~~~~~
> > Current implementation is using the platform bus for XSHM devices.
> > The packet channels are named "xshmp", and stream channel "xshms".
> >
> > /sys/devices:
> > |-- platform
> > |   |-- uevent
> > |   `-- xshm
> > |       |-- bootimg
> > |       |-- caif_ready
> > |       |-- ipc_ready
> > |       |-- subsystem -> ../../../bus/platform
> > |       |-- xshmp.1
> > |       |   |-- driver -> ../../../../bus/platform/drivers/xshmp
> > |       |   |-- subsystem -> ../../../../bus/platform
> > |       `-- xshms.0
> > |           |-- driver -> ../../../../bus/platform/drivers/xshms
> > |           |-- misc
> > |           |   `-- xshm0
> > |           |       |-- device -> ../../../xshms.0
> > |           |       |-- subsystem -> ../../../../../../class/misc
> > `-- virtual
> >     |-- net
> >     |   |-- cfshm0
> 
> Why are the channels /platform/ devices? With all the infrastructure
> you have in the xshm driver, I would think that you should be
> able to probe the available channels yourself instead of relying the
> board to define them.

OK point taken. Using the platform bus, was just share laziness as it
provides a simple way of creating device and drivers. I will look into
creating a separate bus.

Regards,
Sjur

ÿôèº{.nÇ+‰·Ÿ®‰­†+%ŠËÿ±éݶ\x17¥Šwÿº{.nÇ+‰·¥Š{±þG«éÿŠ{ayº\x1dʇڙë,j\a­¢f£¢·hšïêÿ‘êçz_è®\x03(­éšŽŠÝ¢j"ú\x1a¶^[m§ÿÿ¾\a«þG«éÿ¢¸?™¨è­Ú&£ø§~á¶iO•æ¬z·švØ^\x14\x04\x1a¶^[m§ÿÿÃ\fÿ¶ìÿ¢¸?–I¥

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 0/9] XSHM: Shared Memory Driver for ST-E Thor M7400 LTE modem
  2011-12-07 15:44   ` Sjur BRENDELAND
@ 2011-12-09 14:42     ` Arnd Bergmann
  2011-12-12  9:05       ` Sjur BRENDELAND
  2012-03-22 14:31       ` Sjur BRENDELAND
  0 siblings, 2 replies; 26+ messages in thread
From: Arnd Bergmann @ 2011-12-09 14:42 UTC (permalink / raw)
  To: Sjur BRENDELAND; +Cc: linux-kernel, Linus Walleij, Paul Bolle

On Wednesday 07 December 2011, Sjur BRENDELAND wrote:

> > > Introduction:
> > > ~~~~~~~~~~~~~
> > > This patch-set introduces the Shared Memory Driver for ST-Ericsson's
> > > Thor M7400 LTE modem.
> > > The shared memory model is implemented for Chip-to-chip and
> > > uses a reserved memory area and a number of bi-directional
> > > channels. Each channel has it's own designated data area where
> > payload
> > > data is copied into.
> > 
> > Can you explain what the scope of this framework is? What I don't
> > understand from the code is whether this is meant to be very
> > broadly applicable to a lot of devices and competing with e.g.
> > rpmsg from Ohad Ben-Cohen, or whether this is really speciific
> > to one hardware implementation for caif.
> 
> This is primarily designed for supporting the ST-Ericsson's M7400 product
> and future modem platforms supporting shared memory.
> There are some parts with a rather tight coupling to the modem such as
> synchronization of the startup phases "ipc_ready" and "caif_ready" and
> how we store the 1st stage boot images in shared memory.
> However there are other concepts and parts of the implementation 
> that probably is possible to reuse.

Ok.

> > Also, to what degree is the protocol design flexible? Is the modem
> > side fixed, so you have to live with any shortcomings of the interface,
> > or are you at this point able to improve both sides when something
> > is found to be lacking?
> 
> As I see it, this interface to the modem is pretty much set, and isn't
> going to change a lot for the M7400 product.
> 
> However for the long term perspective: we expect this interface to evolve
> for future products, so suggestions and input for improvements is welcome.
> rpmsg or at least the use of virtio-ring combined with a true end-to-end
> zero copy is something we definitely are interested to look into for the
> future.

Is the rpmsg support something that can be done with the current low-level
protocol? It would be really nice if you could at least share some code
for the general infrastructure, even if both the underlying transport
and the high-level protocols are different.

> > > Two different channel types are defined, one stream channel which is
> > > implemented as a traditional ring-buffer, and a packet channel which
> > > is a ring-buffer of fix sized buffers where each buffer contains
> > > an array of CAIF frames.
> > >
> > > The notification of read and write index updates are handled in a
> > > separate driver called c2c_genio. This driver will be contributed
> > separately,
> > > but the API is included in this patch-set.
> > >
> > > The channel configuration is stored in shared memory, each channel
> > > has a designated area in shared memory for configuration data,
> > > read and write indexes and a data area.
> > 
> > How many channels will there be on of each type in a typical system,
> > and respectively in the maximum you can realistically expect over
> > the next 10 years?
> 
> Currently we have defined the following channels:
> 
> Ch#	Type		Used for				RX size	TX size
> ------------------------------------------------------------------
> 1	Stream	Modem Crash dump			64 kB		4 kB
> 2	Stream	Flash-less Boot Protocol	4 kB		256 kB
> 3	Stream	Boot Logging			8 kB		4kB
> 4	Packet	CAIF high speed 			8*16 kB	8*16kB
> 5	Packet	CAIF low latency			4*1kB		4*1kB
> 
> The modem is doing a multi stage boot, where it initially reads
> boot images stored in shared memory. This will boot-strap the
> 2nd boot stage where the Flash-less Boot Protocol is used.
> In this phase channel #2 and #3 is used. At this stage only a
> very basic OS-less runtime system is available, so CAIF protocol
> cannot be running on the modem.
> 
> When the 2nd boot stage is complete, channel #4 and #5 will be used.
> CAIF is a multiplexing protocol allowing multiple connections,
> so the reason for two channels is to be able to separate high
> throughput traffic such as IP + Logging from traffic with low-latency
> requirements such as control (e.g. AT commands) and audio.
> 
> In case of modem crash-dump the modem is running in OS-less mode, and 
> transfers dump over channel #1.
> 
> Initially I have implemented separate memory areas for
> the different channels and the initial boot images, however
> the channel specification and netlink interface supports
> overlapping channels. This is a possible future improvement.

ok

> > > Stream data
> > > ~~~~~~~~~~~
> > > The driver for the stream channel is implemented as a character device
> > > interface to user space. The character device implements non-blocking open
> > > and non-blocking IO in general. The character device is implementing
> > > a traditional circular buffer directly in the shared memory region for
> > > the channel.
> > 
> > It seems unusual to have both a socket interface and a character device
> > interface. What is the motivation behind this? Why can't you use the
> > socket for non-blocking I/O?
> 
> As mentioned above, the modem is in different run modes, stream device
> is only used when modem runs in OS-less mode, during boot or crash-dump.
> Except for boot and crash-dump the CAIF stack is used.
> A similar approach is used for HSI, where we use a bare
> HSI channel in OS-less mode, and CAIF over HSI otherwise.

My feeling is that a character device is not the ideal implementation here,
but I'm not sure what is. One option I can see is to declare the stream
interface part of the configuration logic and do everything through netlink,
packetizing the stream data in netlink frames. Alternatively, a tty port
device might be more appropriate than a plain chardev. Both of these
are likely a bit slower than what you have today, but my impression is that
performance is not the main design goal for the stream interface. If it is,
the best option would probably be to allow user space to mmap the buffer and
do the implementation in an application outside of the kernel.

	Arnd

^ permalink raw reply	[flat|nested] 26+ messages in thread

* RE: [PATCH 0/9] XSHM: Shared Memory Driver for ST-E Thor M7400 LTE modem
  2011-12-09 14:42     ` Arnd Bergmann
@ 2011-12-12  9:05       ` Sjur BRENDELAND
  2012-03-22 14:31       ` Sjur BRENDELAND
  1 sibling, 0 replies; 26+ messages in thread
From: Sjur BRENDELAND @ 2011-12-12  9:05 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linux-kernel, Linus Walleij, Paul Bolle

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="utf-8", Size: 3438 bytes --]

Hi Arnd,

>> However for the long term perspective: we expect this interface to evolve
>> for future products, so suggestions and input for improvements is welcome.
>> rpmsg or at least the use of virtio-ring combined with a true end-to-end
>> zero copy is something we definitely are interested to look into for the
>> future.
> 
> Is the rpmsg support something that can be done with the current low-level
> protocol? It would be really nice if you could at least share some code
> for the general infrastructure, even if both the underlying transport
> and the high-level protocols are different.

I have already been challenged in this area by Linux Walleij :-),
but I'm afraid I don't see how this could be done in a sensible way.

The modem doesn't understand the virtual-ring format, and implements
a different "protocol" - so the virtual-ring format cannot be used to
talk to the modem. And using virtqueue as a packet elevator between
Kernel internal modules doesn't seem like the right thing either.

If the virtqueue_ops structure hadn't been removed, we could have used
this as an abstraction. But this structure was killed off.

I might be missing something here, but currently I don't see how I 
could reuse any of the virtio architecture for the current implementation.
However it is definitely something we want to look into for the future. 

>> As mentioned above, the modem is in different run modes, stream device
>> is only used when modem runs in OS-less mode, during boot or crash-dump.
>> Except for boot and crash-dump the CAIF stack is used.
>> A similar approach is used for HSI, where we use a bare
>> HSI channel in OS-less mode, and CAIF over HSI otherwise.
> 
> My feeling is that a character device is not the ideal implementation here,
> but I'm not sure what is. One option I can see is to declare the stream
> interface part of the configuration logic and do everything through netlink,
> packetizing the stream data in netlink frames. Alternatively, a tty port
> device might be more appropriate than a plain chardev. Both of these
> are likely a bit slower than what you have today, but my impression is that
> performance is not the main design goal for the stream interface. If it is,
> the best option would probably be to allow user space to mmap the buffer and
> do the implementation in an application outside of the kernel.

The M7400 modem implements other physical interfaces such as HSI and USB. We
have a daemon in user space controlling the run modes and "life-cycle" of the
modem (boot, shutdown, upgrade, etc) and we are trying to keep the interface
to the different physical interfaces aligned. So for both HSI and USB we're
using a stream interface (tty or char device) for the flash-less boot protocol.
Due to this I'd prefer to stick to a plain char/tty interface and not use netlink
or mmap interfaces.

The overall modem boot time is certainly important. But I don't have performance
figures of how much of the overall boot time is used for transferring the flash-
less boot image in front of me. So it's hard to make a judgment of exactly how
performance critical this is. With the current implementation there is *one* copy
from user-space to the shared memory area so it should be reasonably efficient. 

Regards,
Sjur
ÿôèº{.nÇ+‰·Ÿ®‰­†+%ŠËÿ±éݶ\x17¥Šwÿº{.nÇ+‰·¥Š{±þG«éÿŠ{ayº\x1dʇڙë,j\a­¢f£¢·hšïêÿ‘êçz_è®\x03(­éšŽŠÝ¢j"ú\x1a¶^[m§ÿÿ¾\a«þG«éÿ¢¸?™¨è­Ú&£ø§~á¶iO•æ¬z·švØ^\x14\x04\x1a¶^[m§ÿÿÃ\fÿ¶ìÿ¢¸?–I¥

^ permalink raw reply	[flat|nested] 26+ messages in thread

* RE: [PATCH 0/9] XSHM: Shared Memory Driver for ST-E Thor M7400 LTE modem
  2011-12-09 14:42     ` Arnd Bergmann
  2011-12-12  9:05       ` Sjur BRENDELAND
@ 2012-03-22 14:31       ` Sjur BRENDELAND
  2012-03-22 16:57         ` Arnd Bergmann
  1 sibling, 1 reply; 26+ messages in thread
From: Sjur BRENDELAND @ 2012-03-22 14:31 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linux-kernel, Linus Walleij, sjurbren

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="utf-8", Size: 3264 bytes --]

Hi Arnd,

I've got some updates since my last reply from December...

[Arnd]
>>> Also, to what degree is the protocol design flexible? Is the modem
>>> side fixed, so you have to live with any shortcomings of the interface,
>>> or are you at this point able to improve both sides when something
>>> is found to be lacking?

I have started working on the next generation shared memory interface for
ST-Ericsson modems. So the interface design is now flexible! This means that
I can be open to input and new ideas, at least for the next month or two.
I'm hoping (if time allows) to prototype and post some patches while working
on the specification.

The primary goal for the new interface design, besides getting it accepted
in the mainline kernel, is to implement zero-copy for IP traffic on the modem
side. Zero-copy on host side would be the next step.

[Sjur]
>> However for the long term perspective: we expect this interface to evolve
>> for future products, so suggestions and input for improvements is welcome.
>> rpmsg or at least the use of virtio-ring combined with a true end-to-end
>> zero copy is something we definitely are interested to look into for the
>> future.

My current idea for the new interface design is to use Virtio channels for
transporting CAIF frames. The CAIF interface could be implemented as a
virtio-driver using separate RX and TX rings.

For uplink traffic (TX) we could (initially) copy SKB into a buffer located in
the shared-memory area, and add the buffer to the Virtio-ring. We will need
to manage a fixed size buffer pool of uplink data buffers. (I don't think we
will be able to access the kernel memory from the modem, so I cannot put SKB
content directly on the virtio-ring as virtio-net does)

For Downlink payload (RX) I'm planning on using a reversed virtio-ring.
The modem has its own sophisticated memory allocator for payload and
implements mechanisms for efficient buffer handling inside the modem.
The modem could add the payload buffer on the virtual-ring without
copying and kick the host.

[Sjur:]
>>>> The driver for the stream channel is implemented as a character device
...
[Arnd:]
> My feeling is that a character device is not the ideal implementation here,
> but I'm not sure what is. One option I can see is to declare the stream
> interface part of the configuration logic and do everything through netlink,
> packetizing the stream data in netlink frames. Alternatively, a tty port
> device might be more appropriate than a plain chardev. Both of these
> are likely a bit slower than what you have today, but my impression is that
> performance is not the main design goal for the stream interface. If it is,
> the best option would probably be to allow user space to mmap the buffer and
> do the implementation in an application outside of the kernel.

For the stream interface it's tempting to reuse the ring buffer interface
to the modem from last time. But perhaps the Virtio-console could be as the
user-space interface, with a slim virtual device underneath feeding data
from the ring-buffer into a virtual-ring...?

Regards,
Sjur
ÿôèº{.nÇ+‰·Ÿ®‰­†+%ŠËÿ±éݶ\x17¥Šwÿº{.nÇ+‰·¥Š{±þG«éÿŠ{ayº\x1dʇڙë,j\a­¢f£¢·hšïêÿ‘êçz_è®\x03(­éšŽŠÝ¢j"ú\x1a¶^[m§ÿÿ¾\a«þG«éÿ¢¸?™¨è­Ú&£ø§~á¶iO•æ¬z·švØ^\x14\x04\x1a¶^[m§ÿÿÃ\fÿ¶ìÿ¢¸?–I¥

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 0/9] XSHM: Shared Memory Driver for ST-E Thor M7400 LTE modem
  2012-03-22 14:31       ` Sjur BRENDELAND
@ 2012-03-22 16:57         ` Arnd Bergmann
  2012-03-27 13:15           ` Using remoteproc with ST-Ericsson modem Sjur BRENDELAND
  0 siblings, 1 reply; 26+ messages in thread
From: Arnd Bergmann @ 2012-03-22 16:57 UTC (permalink / raw)
  To: Sjur BRENDELAND; +Cc: linux-kernel, Linus Walleij, sjurbren, Ohad Ben-Cohen

On Thursday 22 March 2012, Sjur BRENDELAND wrote:
> Hi Arnd,
> 
> I've got some updates since my last reply from December...
> 
> [Arnd]
> >>> Also, to what degree is the protocol design flexible? Is the modem
> >>> side fixed, so you have to live with any shortcomings of the interface,
> >>> or are you at this point able to improve both sides when something
> >>> is found to be lacking?
> 
> I have started working on the next generation shared memory interface for
> ST-Ericsson modems. So the interface design is now flexible! This means that
> I can be open to input and new ideas, at least for the next month or two.
> I'm hoping (if time allows) to prototype and post some patches while working
> on the specification.

Ok, very good.

> [Sjur]
> >> However for the long term perspective: we expect this interface to evolve
> >> for future products, so suggestions and input for improvements is welcome.
> >> rpmsg or at least the use of virtio-ring combined with a true end-to-end
> >> zero copy is something we definitely are interested to look into for the
> >> future.
> 
> My current idea for the new interface design is to use Virtio channels for
> transporting CAIF frames. The CAIF interface could be implemented as a
> virtio-driver using separate RX and TX rings.
> 
> For uplink traffic (TX) we could (initially) copy SKB into a buffer located in
> the shared-memory area, and add the buffer to the Virtio-ring. We will need
> to manage a fixed size buffer pool of uplink data buffers. (I don't think we
> will be able to access the kernel memory from the modem, so I cannot put SKB
> content directly on the virtio-ring as virtio-net does)
> 
> For Downlink payload (RX) I'm planning on using a reversed virtio-ring.
> The modem has its own sophisticated memory allocator for payload and
> implements mechanisms for efficient buffer handling inside the modem.
> The modem could add the payload buffer on the virtual-ring without
> copying and kick the host.

Remoteproc and rpmsg are now in the arm-soc tree and will be merged
upstream for v3.4, I suggest you discuss with Ohad how to best hook in
there.

What is the limitation for the addressing here, i.e. why can't the
modem access all of the host memory?

> [Sjur:]
> >>>> The driver for the stream channel is implemented as a character device
> ...
> [Arnd:]
> > My feeling is that a character device is not the ideal implementation here,
> > but I'm not sure what is. One option I can see is to declare the stream
> > interface part of the configuration logic and do everything through netlink,
> > packetizing the stream data in netlink frames. Alternatively, a tty port
> > device might be more appropriate than a plain chardev. Both of these
> > are likely a bit slower than what you have today, but my impression is that
> > performance is not the main design goal for the stream interface. If it is,
> > the best option would probably be to allow user space to mmap the buffer and
> > do the implementation in an application outside of the kernel.
> 
> For the stream interface it's tempting to reuse the ring buffer interface
> to the modem from last time. But perhaps the Virtio-console could be as the
> user-space interface, with a slim virtual device underneath feeding data
> from the ring-buffer into a virtual-ring...?

sounds doable, but again Ohad may have better suggestions as well.

	Arnd

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Using remoteproc with ST-Ericsson modem.
  2012-03-22 16:57         ` Arnd Bergmann
@ 2012-03-27 13:15           ` Sjur BRENDELAND
  2012-03-27 13:56             ` Ohad Ben-Cohen
  0 siblings, 1 reply; 26+ messages in thread
From: Sjur BRENDELAND @ 2012-03-27 13:15 UTC (permalink / raw)
  To: Arnd Bergmann, Ohad Ben-Cohen; +Cc: linux-kernel, Linus Walleij, sjurbren

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="utf-8", Size: 2475 bytes --]

Hi Ohad,

[Arnd]
>Remoteproc and rpmsg are now in the arm-soc tree and will be merged
>upstream for v3.4, I suggest you discuss with Ohad how to best hook in
>there.

I would like to discuss with how to use remoteproc for the ST-Ericsson
modem. For a while, I have been working on a shared memory interface
for ST-Ericsson modems. I already have one version in linux-next, but
I'm planning on redoing parts of the implementation. As part of this, I
want to investigate if I can use remoteproc for handling the start-up
of our modem. One solution could be to make firmware and resource handling
pluggable.

The principles for remoteproc and modem_shm are not so different.
 a) We have a number of channels in shared memory, and the shm-address is
   predefined.
 b) Boot images are copied into shared memory before start.

However, we have a couple of requirements that are different.
- We don't use a ELF binary for resource definition. We have resource
  definitions in a binary format outside the binary. (I'm afraid I'm
  not able to change this).
- We need user-space to access sequential data streams.
- We use CAIF (a muxing protocol) implemented in the networking subsystem.

In order to use remoteproc, I think it could be a good idea to make the
firmware, resource and possible Virtio handling pluggable. E.g:

static struct rproc_ops rproc_ops = {
      ...
       .fw_sanity_check = fw_sanity_check,
       .fw_resource_handler = fw_resource_handler,
       .fw_load = fw_load,
       .fw_config_virtio = fw_config_virtio,
};

I think we need to be able to launch other Virtio device types than
VIRTIO_ID_RPMSG as well. Most likely, I need to make a CAIF specific
VIRTIO type due to the fact that I need RX direction to be reversed
(modem must be able to post it's payload buffer to Virtio ring).

[Sjur:]
>> For the stream interface it's tempting to reuse the ring buffer interface
>> to the modem from last time. But perhaps the Virtio-console could be as the
>> user-space interface...
[Arnd:]
>sounds doable, but again Ohad may have better suggestions as well.

We also define stream channels. As mentioned, I'm considering the possibility
of reusing Virtio console.

So, I'd really like to get your viewpoint and ideas for how we could use
remoteproc to manage the ST-Ericsson modem.

Regards,
Sjur
ÿôèº{.nÇ+‰·Ÿ®‰­†+%ŠËÿ±éݶ\x17¥Šwÿº{.nÇ+‰·¥Š{±þG«éÿŠ{ayº\x1dʇڙë,j\a­¢f£¢·hšïêÿ‘êçz_è®\x03(­éšŽŠÝ¢j"ú\x1a¶^[m§ÿÿ¾\a«þG«éÿ¢¸?™¨è­Ú&£ø§~á¶iO•æ¬z·švØ^\x14\x04\x1a¶^[m§ÿÿÃ\fÿ¶ìÿ¢¸?–I¥

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Using remoteproc with ST-Ericsson modem.
  2012-03-27 13:15           ` Using remoteproc with ST-Ericsson modem Sjur BRENDELAND
@ 2012-03-27 13:56             ` Ohad Ben-Cohen
  2012-03-28  9:33               ` Sjur BRENDELAND
  0 siblings, 1 reply; 26+ messages in thread
From: Ohad Ben-Cohen @ 2012-03-27 13:56 UTC (permalink / raw)
  To: Sjur BRENDELAND
  Cc: Arnd Bergmann, linux-kernel, Linus Walleij, sjurbren,
	Loic PALLARDY, Ludovic BARRE

Hi Sjur,

On Tue, Mar 27, 2012 at 3:15 PM, Sjur BRENDELAND
<sjur.brandeland@stericsson.com> wrote:
> However, we have a couple of requirements that are different.
> - We don't use a ELF binary for resource definition. We have resource
>  definitions in a binary format outside the binary. (I'm afraid I'm
>  not able to change this).

That's ok:

We coupled the resource table with the firmware binary in order to
reflect the strong ties it has with the firmware's software and to
prevent issues of loading the wrong table with a given firmware, but
supporting an external resource table will help others as well so it's
definitely in the plans (feel free to take a stab at it if you want).

> In order to use remoteproc, I think it could be a good idea to make the
> firmware, resource and possible Virtio handling pluggable. E.g:
>
> static struct rproc_ops rproc_ops = {
>      ...
>       .fw_sanity_check = fw_sanity_check,
>       .fw_resource_handler = fw_resource_handler,
>       .fw_load = fw_load,
>       .fw_config_virtio = fw_config_virtio,
> };

Can you please elaborate why would you want that ? Let's go
requirement by requirement.

I strongly prefer to extend the generic code to support additional
requirements rather than to allow platform-specific implementations to
override it (the motivation is to prevent code duplication).

> I think we need to be able to launch other Virtio device types than
> VIRTIO_ID_RPMSG as well.

Sure. Please note that we already support that today: a firmware can
indicate any type of (and number of) virito devices it supports, and
remoteproc will create those device as needed.

> Most likely, I need to make a CAIF specific
> VIRTIO type

Have you considered making this an rpmsg driver ? it might make your
implementation simpler.

> due to the fact that I need RX direction to be reversed
> (modem must be able to post it's payload buffer to Virtio ring).

It sounds like something that Loic and Ludovic (cc'ed) have been
dealing with recently too. To solve it, they have created a symmetric
variation of vrings - you might want to take a look at their
implementation.

> We also define stream channels. As mentioned, I'm considering the possibility
> of reusing Virtio console.

Sounds nice. We tried virtio console with a remote processor and it
worked quite nicely (there're still a few details that need some work
in that front, mainly if your rproc is behind an IOMMU, but otherwise
it should be quite smooth).

Thanks,
Ohad.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* RE: Using remoteproc with ST-Ericsson modem.
  2012-03-27 13:56             ` Ohad Ben-Cohen
@ 2012-03-28  9:33               ` Sjur BRENDELAND
  2012-03-28 15:55                 ` Ohad Ben-Cohen
  0 siblings, 1 reply; 26+ messages in thread
From: Sjur BRENDELAND @ 2012-03-28  9:33 UTC (permalink / raw)
  To: Ohad Ben-Cohen
  Cc: Arnd Bergmann, linux-kernel, Linus Walleij, sjurbren,
	Loic PALLARDY, Ludovic BARRE

Hi Ohad,

> Can you please elaborate why would you want that ? Let's go
> requirement by requirement.

1) Resource descriptors and parameters such as size of vring,
size of the "carved-out" shared memory, as well as proprietary
(CAIF specific) parameters, should be supported. These parameters
must be available to the Virtio device-drivers (CAIF interface),
and to the modem. The resource/parameter configuration has to be
stored in shared memory before booting the modem.

2a) The resource descriptors and configuration parameters are pre-
formatted in the proprietary binary format. Remoteproc (or it's plugin)
must then be able to parse this proprietary format, extract
configuration parameters, and load the binary image into shared memory.
OR 
2b) Configuration parameters and firmware can be provided separately.
The remoteproc (or it's plugin) must be able to format the provided
configuration parameters in a proprietary format understood by the
modem boot-loader and store the firmware and configuration in
shared memory. A user-space API for configuration (e.g. netlink)
must be supported.
OR
2c) As in 2a, the image in proprietary format containing both firmware 
and parameters could be provided. In addition configuration parameters
could be provided to remoteproc separately. The binary-image and
configuration parameters will in this case hold identical configuration
information. A user-space API for configuration (e.g. netlink)
must be supported.

I had a quick look at the patch from Loic and Ludovic and they seem to
provide resource-table and firmware separately.

In my case, the best solution seems to be 2a). I.e to parse parameters
from the provided firmware, and avoid any extra configuration parameters
provided from user-space. It seems to me we could do this by adding a
callback function to remoteproc that parses the firmware and returns
a resource table.

>> I think we need to be able to launch other Virtio device types than
>> VIRTIO_ID_RPMSG as well.
> 
> Sure. Please note that we already support that today: a firmware can
> indicate any type of (and number of) virito devices it supports, and
> remoteproc will create those device as needed.

Excellent.

>> Most likely, I need to make a CAIF specific VIRTIO type
> 
> Have you considered making this an rpmsg driver ? it might make your
> implementation simpler.

Hm, there are some issue we need to handle in that case: last time I
looked rpmsg did blocking calls, but CAIF may be running in soft-irq context.
As mentioned before I need rpmsg to do "reversed/symmetric Vrings" for
the RX direction. I also need performance specific tweaks such as
disabling kicks and go into poll-mode at upon high load.

However there might be new requirements we have in common such as:
buffer pools with different fixed sized buffers, zero-copy handling of
SKBs (TX), and DMA for (RX). Even if I end up not using rpmsg we should
definitely look for opportunities for common code. I think we will be
trying to solve the same type of problems.

>> due to the fact that I need RX direction to be reversed
>> (modem must be able to post its payload buffer to Virtio ring).
> 
> It sounds like something that Loic and Ludovic (cc'ed) have been
> dealing with recently too. To solve it, they have created a symmetric
> variation of vrings - you might want to take a look at their
> implementation.

Thanks for info, I've already started talking to them :-)

>> We also define stream channels. As mentioned, I'm considering the possibility
>> of reusing Virtio console.
> 
> Sounds nice. We tried virtio console with a remote processor and it
> worked quite nicely.

OK, good to hear. I'll definitely look into this more closely.

Regards,
Sjur

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Using remoteproc with ST-Ericsson modem.
  2012-03-28  9:33               ` Sjur BRENDELAND
@ 2012-03-28 15:55                 ` Ohad Ben-Cohen
  2012-03-28 17:31                   ` Sjur BRENDELAND
  0 siblings, 1 reply; 26+ messages in thread
From: Ohad Ben-Cohen @ 2012-03-28 15:55 UTC (permalink / raw)
  To: Sjur BRENDELAND
  Cc: Arnd Bergmann, linux-kernel, Linus Walleij, sjurbren,
	Loic PALLARDY, Ludovic BARRE

Hi Sjur,

On Wed, Mar 28, 2012 at 11:33 AM, Sjur BRENDELAND
<sjur.brandeland@stericsson.com> wrote:
> 1) Resource descriptors and parameters such as size of vring,
> size of the "carved-out" shared memory

We have that already today - the firmware controls these kind of
parameters (and more).

> as well as proprietary (CAIF specific) parameters

Not sure what exactly do you mean here - can you pls elaborate ?

> should be supported. These parameters
> must be available to the Virtio device-drivers

Virtio configuration is done via the virtio config space, which we
should already be supporting (untested though, because we didn't need
this yet).

> (CAIF interface),
> and to the modem. The resource/parameter configuration has to be
> stored in shared memory before booting the modem.

Yeah, this is what we do today with the resource table.

> 2a) The resource descriptors and configuration parameters are pre-
> formatted in the proprietary binary format.

What does mandate this proprietary binary format ? Can you just
directly use remoteproc's resource table format instead (i.e. an
extensible collection of type-value pairs) ?

> Remoteproc (or it's plugin)
> must then be able to parse this proprietary format, extract
> configuration parameters, and load the binary image into shared memory.
> OR
> 2b) Configuration parameters and firmware can be provided separately.
> The remoteproc (or it's plugin) must be able to format the provided
> configuration parameters in a proprietary format understood by the
> modem boot-loader and store the firmware and configuration in
> shared memory. A user-space API for configuration (e.g. netlink)
> must be supported.
> OR
> 2c) As in 2a, the image in proprietary format containing both firmware
> and parameters could be provided. In addition configuration parameters
> could be provided to remoteproc separately. The binary-image and
> configuration parameters will in this case hold identical configuration
> information. A user-space API for configuration (e.g. netlink)
> must be supported.
>
> In my case, the best solution seems to be 2a). I.e to parse parameters
> from the provided firmware, and avoid any extra configuration parameters
> provided from user-space. It seems to me we could do this by adding a
> callback function to remoteproc that parses the firmware and returns
> a resource table.

You have suggested several possible solutions, but I'd really prefer
to understand the problem first please :)

Can you please explain how do things work for you today ? binary
formats, image/configuration, how things boot/load/get-configured,
etc..

If I'll understand your requirements (hardware, relevant firmware code
which can't be changed and may impose the design, etc..) it will help
me find with you a suitable solution.

> However there might be new requirements we have in common such as:
> buffer pools with different fixed sized buffers, zero-copy handling of
> SKBs (TX), and DMA for (RX). Even if I end up not using rpmsg we should
> definitely look for opportunities for common code. I think we will be
> trying to solve the same type of problems.

The main thing that rpmsg provides over virtio is the multiplexing of
several channels/drivers over the same set of vrings and a simple API
for doing TX/RX.

If you think you will have to implement similar plumbing, then please
consider using rpmsg - it will save you time and effort (any other gap
that rpmsg does not yet provide can be easily solved - I wouldn't
worry about it).

OTOH, if you don't need that aforementioned plumbing, then directly
using virtio does have its merit of course.

Thanks,
Ohad.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* RE: Using remoteproc with ST-Ericsson modem.
  2012-03-28 15:55                 ` Ohad Ben-Cohen
@ 2012-03-28 17:31                   ` Sjur BRENDELAND
  2012-03-31 19:04                     ` Ohad Ben-Cohen
  0 siblings, 1 reply; 26+ messages in thread
From: Sjur BRENDELAND @ 2012-03-28 17:31 UTC (permalink / raw)
  To: Ohad Ben-Cohen
  Cc: Arnd Bergmann, linux-kernel, Linus Walleij, sjurbren,
	Loic PALLARDY, Ludovic BARRE

Hi Ohad,

> We have that already today - the firmware controls these kind of
> parameters (and more).
> 
> > as well as proprietary (CAIF specific) parameters
> 
> Not sure what exactly do you mean here - can you pls elaborate ?

Yes, we store MTU and alignment information. We also use a special
register for generating interrupts/kicks in each rx/tx direction.
The modem needs to know what bit is assigned to each channel.
See struct shm_ipctoc_channel below.

> Virtio configuration is done via the virtio config space, which we
> should already be supporting (untested though, because we didn't need
> this yet).

Hm, my impression was that Virtio only supported single bits for
configuration information.

> > (CAIF interface),
> > and to the modem. The resource/parameter configuration has to be
> > stored in shared memory before booting the modem.
> 
> Yeah, this is what we do today with the resource table.
>
> > 2a) The resource descriptors and configuration parameters are pre-
> > formatted in the proprietary binary format.
> 
> What does mandate this proprietary binary format ? Can you just
> directly use remoteproc's resource table format instead (i.e. an
> extensible collection of type-value pairs) ?

I'm afraid not. I cannot change the layout for the modem boot loader.
The boot image start with a "table of contents" defining the content
of the image. At the start of the shared memory are there needs to
be structure like this: (see https://lkml.org/lkml/2012/1/11/165)
[snip]
/**
 * struct toc_entry - Points out the boot images
 *
 * @start: Offset counting from start of memory area to the image data.
 * @size:  Size of the images in bytes.
 * @flags: Use 0 if no flags are in use.
 * @entry: Where to jump to start exeuting. Only applicable
 *		when using SDRAM. Set to 0xffffffff if unused.
 * @load_addr: Location in SDRAM to move image. Set to 0xffffffff if
 *		not applicable.
 * @name: Name of image.
 */
struct toc_entry {
	__le32 start;
	__le32 size;
	__le32 flags;
	__le32 entry_point;
	__le32 load_addr;
	char name[12];
};

Currently this will be a table with two entries, one entry pointing at the
binary image, and one entry pointing at the IPC-TOC.

[snip]
/**
 * struct shm_ipctoc_channel - Channel descriptors.
 *
 * @offset: Relative address to channel data area.
 * @size: Total size of a SHM channel area partition.
 * @mode: Mode of channel: Packet mode=1, Stream mode (mode = 2).
 * @buffers: Number of buffers for the channel.
 * @ipc: Offset to IPC message location (of type struct shm_bufidx).
 * @read_bit: GENI/O bit used to indicate update of the read pointer for
 *	this channel (at offset ipc).
 * @write_bit: GENI/O bit used to indicate update of the write pointer for
 *	this channel (at offset ipc).
 * @alignment: Protocol specific options for the protocol,
 *	e.g. packet alignment.
 * @packets: Maximum Number of packets in a buffer (packet mode).
 * @mtu: Maximum Transfer Unit for packets in a buffer (packet mode).
 *
 * This struct defines the channel configuration for a single direction.
 *
 * This structure is pointed out by the &shm_toc and is written by
 * host during start-up and read by modem at firmware boot.
 */
struct shm_ipctoc_channel {
	__le32 offset;
	__le32 size;
	__u8 mode;
	__le32 buffers;
	__le32 ipc;
	__le16 read_bit;
	__le16 write_bit;
	__u8 alignment;
	__u8 packets;
	__u16 mtu;
};

Part of this shm_ipctoc_shannel will have to change when we move from using
ring-buffers to virtio-ring for communication. So this part is not set in
stone. If needed we could do some layout changes here.

> You have suggested several possible solutions, but I'd really prefer
> to understand the problem first please :)
> 
> Can you please explain how do things work for you today ? binary
> formats, image/configuration, how things boot/load/get-configured,
> etc..
> If I'll understand your requirements (hardware, relevant firmware code
> which can't be changed and may impose the design, etc..) it will help
> me find with you a suitable solution.

Sure, I'll try to explain the current solution 
(see https://lkml.org/lkml/2012/1/11/161):

We have a user-space component controlling most of the modem boot sequence.
This component keeps track of the firmware and corresponding
configuration. It also manages life-cycle start/stop/restart, and
different run-modes etc.

The startup sequence run something like this (simplified).
a) configuration data related to channels are sent from user-space
   to the kernel module modem_shm via gen-netlink 
   (see https://lkml.org/lkml/2012/1/11/162)
b) The firmware image (containing TOC and the executable) is sent to
   kernel via firmware framework.
c) when all channels have been sent to modem_shm, configuration data
   for each channel is processed and location of each channel in
   shared memory is calculated. The "kick" bits are also assigned
   to each channel.
d) The modem-image starts with a table (TOC). A new pointer to a 
   IPC-TOC containing channel configuration is added to the TOC.
   The executable and channel configuration needed by the modem
   is stored in shared memory.
   (See https://lkml.org/lkml/2012/1/11/167)
e) devices for the different channels are instantiated. Each device
   is given similar configuration information to what is store in
   shared memory. (See https://lkml.org/lkml/2012/1/11/163)
f) User space turns the modem ON, and boots using the initial 
   firmware from shared-memory.
g) modem's boot stage-2 starts, additional modem binaries are loaded
   using stream channels.
h) when boot stage-2 is complete, the CAIF device is enabled.


The current memory layout is something like this:
+------------+
|	TOC	 |----+
|            |--+ |
+------------+  | |
| Boot IMG	 |<-+ |
+------------+    |
| RX Data	 |    |
+------------+    |
| IPC TOC	 |<---+
+------------+
| RX Ch Decr |
+------------+
| TX Ch Decr |
+------------+
| TX Data	 |
+------------+

(sorry for this figure, my MSFT mail usually mess up things badly...)

 
> > However there might be new requirements we have in common such as:
> > buffer pools with different fixed sized buffers, zero-copy handling
> of
> > SKBs (TX), and DMA for (RX). Even if I end up not using rpmsg we
> should
> > definitely look for opportunities for common code. I think we will be
> > trying to solve the same type of problems.
> 
> The main thing that rpmsg provides over virtio is the multiplexing of
> several channels/drivers over the same set of vrings and a simple API
> for doing TX/RX.

This is the same thing CAIF does, so multiplexing is not something I'm
looking for.

> If you think you will have to implement similar plumbing, then please
> consider using rpmsg - it will save you time and effort (any other gap
> that rpmsg does not yet provide can be easily solved - I wouldn't
> worry about it).

I like the attitude :-), what new features have you planned for rpmsg?

> OTOH, if you don't need that aforementioned plumbing, then directly
> using virtio does have its merit of course.

Yes, I tend to think I should use virtio directly, but I have not made up
mind. I'll probably come back to this later.

Regards,
Sjur

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Using remoteproc with ST-Ericsson modem.
  2012-03-28 17:31                   ` Sjur BRENDELAND
@ 2012-03-31 19:04                     ` Ohad Ben-Cohen
  2012-03-31 21:25                       ` Sjur Brændeland
  0 siblings, 1 reply; 26+ messages in thread
From: Ohad Ben-Cohen @ 2012-03-31 19:04 UTC (permalink / raw)
  To: Sjur BRENDELAND
  Cc: Arnd Bergmann, linux-kernel, Linus Walleij, sjurbren,
	Loic PALLARDY, Ludovic BARRE

Hi Sjur,

On Wed, Mar 28, 2012 at 7:31 PM, Sjur BRENDELAND
<sjur.brandeland@stericsson.com> wrote:
> Hm, my impression was that Virtio only supported single bits for
> configuration information.

You probably refer to virtio's features bitmap, which is how virtio
peers synchronize the features they both support.

Virtio also supports device-specific variable-length configuration
fields - take a look at struct virtio_config_ops (get/set in
particular).

> I'm afraid not. I cannot change the layout for the modem boot loader.
> The boot image start with a "table of contents" defining the content
> of the image.

What binary format is the boot image itself ? ELF ? something else ?

> The startup sequence run something like this (simplified).
> a) configuration data related to channels are sent from user-space
>   to the kernel module modem_shm via gen-netlink
>   (see https://lkml.org/lkml/2012/1/11/162)
> b) The firmware image (containing TOC and the executable) is sent to
>   kernel via firmware framework.
> c) when all channels have been sent to modem_shm, configuration data
>   for each channel is processed and location of each channel in
>   shared memory is calculated. The "kick" bits are also assigned
>   to each channel.
> d) The modem-image starts with a table (TOC). A new pointer to a
>   IPC-TOC containing channel configuration is added to the TOC.
>   The executable and channel configuration needed by the modem
>   is stored in shared memory.
>   (See https://lkml.org/lkml/2012/1/11/167)
> e) devices for the different channels are instantiated. Each device
>   is given similar configuration information to what is store in
>   shared memory. (See https://lkml.org/lkml/2012/1/11/163)
> f) User space turns the modem ON, and boots using the initial
>   firmware from shared-memory.
> g) modem's boot stage-2 starts, additional modem binaries are loaded
>   using stream channels.
> h) when boot stage-2 is complete, the CAIF device is enabled.

Cool, thanks for the details!

> The current memory layout is something like this:
> +------------+
> |       TOC      |----+
> |            |--+ |
> +------------+  | |
> | Boot IMG       |<-+ |
> +------------+    |
> | RX Data        |    |
> +------------+    |
> | IPC TOC        |<---+
> +------------+
> | RX Ch Decr |
> +------------+
> | TX Ch Decr |
> +------------+
> | TX Data        |
> +------------+

Which parts of this are set in stone ? only the TOC or additional
regions as well ?

> This is the same thing CAIF does, so multiplexing is not something I'm
> looking for.

I saw you created an SHM bus, which matches SHM drivers to SHM devices
(i.e. channels). I guess you did that because you have some code in
common which all SHM drivers/devices use ? maybe some low level
transport layer ?

This sounds similar to rpmsg in hindsight, but I couldn't find yet the
relevant code to really tell: Most of shm_bus.c looks like very
minimal boilerplate bus code. Maybe the genio_* API is the shared code
(though I could only find a dummy version of that API in linux next) ?

> I like the attitude :-), what new features have you planned for rpmsg?

Anything you need :)

Some of the obvious stuff might be zero copy I/O, user space API,
static channel configuration, runtime PM, recovery, etc.. (there's
more). Sometimes it's a pure rpmsg feature, and sometimes it's
involved with remoteproc.

But we really just evolve according to requirements - we don't have a
rigid roadmap.

> Yes, I tend to think I should use virtio directly, but I have not made up
> mind. I'll probably come back to this later.

What's the vrings layout you will be using (off the top of your head)
? Separate vrings per driver/device or shared vrings ?

Thanks,
Ohad.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Using remoteproc with ST-Ericsson modem.
  2012-03-31 19:04                     ` Ohad Ben-Cohen
@ 2012-03-31 21:25                       ` Sjur Brændeland
  2012-04-01  6:15                         ` Ohad Ben-Cohen
  0 siblings, 1 reply; 26+ messages in thread
From: Sjur Brændeland @ 2012-03-31 21:25 UTC (permalink / raw)
  To: Ohad Ben-Cohen; +Cc: Arnd Bergmann, linux-kernel, Linus Walleij

Hi Ohad,

> You probably refer to virtio's features bitmap, which is how virtio
> peers synchronize the features they both support.
>
> Virtio also supports device-specific variable-length configuration
> fields - take a look at struct virtio_config_ops (get/set in
> particular).

Ok, that's good.

>> I'm afraid not. I cannot change the layout for the modem boot loader.
>> The boot image start with a "table of contents" defining the content
>> of the image.
>
> What binary format is the boot image itself ? ELF ? something else ?

Not sure, I have to check and come back to you on that...

>> The current memory layout is something like this:
>> +------------+
>> |       TOC      |----+
>> |            |--+ |
>> +------------+  | |
>> | Boot IMG       |<-+ |
>> +------------+    |
>> | RX Data        |    |
>> +------------+    |
>> | IPC TOC        |<---+
>> +------------+
>> | RX Ch Decr |
>> +------------+
>> | TX Ch Decr |
>> +------------+
>> | TX Data        |
>> +------------+
>
> Which parts of this are set in stone ? only the TOC or additional
> regions as well ?

The TOC and executable Boot image is set in stone.
The rest of this could be changed. Some of this must be
changed due to new zero-copy features and use of virtio-rings
for CAIF anyway.

>> This is the same thing CAIF does, so multiplexing is not something I'm
>> looking for.
>
> I saw you created an SHM bus, which matches SHM drivers to SHM devices
> (i.e. channels). I guess you did that because you have some code in
> common which all SHM drivers/devices use ? maybe some low level
> transport layer ?

Yes, On the "device" level, the code in shm_dev.c and genio_* is common
This code deals with startup synchronisation, generating kicks for RX/TX,
and also the PM related inactivity timer in order to power down interfaces
when not in use. If/When I start using the remoteproc framework, I will
probably move some of the functionality from shm_dev.c in on the
remoteproc device level (e.g. new file: modem_shm_remoteproc.c)

The code in shm_boot.c is mainly dealing with configuration, it terminates
the gen-netlink socket and calculates and writes configuration data in memory,
and creates the devices (to some extent similar functionality to remoteproc).
It would be nice to find the best way to use remoteproc framework for some
of this functionality currently implemented in shm_boot.c.

> This sounds similar to rpmsg in hindsight, but I couldn't find yet the
> relevant code to really tell: Most of shm_bus.c looks like very
> minimal boilerplate bus code. Maybe the genio_* API is the shared code
> (though I could only find a dummy version of that API in linux next) ?

genio_* is the interface towards the underlying C2C HW driver. We have
unfortunately not yet started contribution of this component.

>> I like the attitude :-), what new features have you planned for rpmsg?
>
> Anything you need :)
>
> Some of the obvious stuff might be zero copy I/O, user space API,
> static channel configuration, runtime PM, recovery, etc.. (there's
> more). Sometimes it's a pure rpmsg feature, and sometimes it's
> involved with remoteproc.
>
> But we really just evolve according to requirements - we don't have a
> rigid roadmap.

Good stuff. One question from top of my mind: Have you considered
option of using rpmsg without the protocol, eg something like
RPMSG_RAW, where I can take advantage of rpmsg transport features
without needing the muxing feature. Then I could use CAIF muxing layer
without the rpmsg protocol and naming services....?

>> Yes, I tend to think I should use virtio directly, but I have not made up
>> mind. I'll probably come back to this later.
>
> What's the vrings layout you will be using (off the top of your head)
> ? Separate vrings per driver/device or shared vrings ?

In normal operation after boot, we will only use CAIF link layer device.
CAIF will handle the multiplexing. I was thinking of using one vring in
each direction RX/TX and reversing the vring for RX direction.

For the stream channels we use three pairs of ring-buffers at the
moment. I really haven't made up my mind on how to do this. But doing
the transport with plain ringbuffers is really quite straight forward, so one
option is to keep this as is, but use tty instead of the misc device framework.
Another option is using virtio_console, but I realise virtio_console is doing
normal kmalloc so memory handling in virtio_console would have to change
then. (I have a memory addressing issue, so modem cannot access all
kernel memory).

Or I could look into feeding data from the existing ringbuffers into the
vrings of virtio_console. Then I could reuse both modem implementation of
ringbuffers and virtion_console without changes.

Yet another option is using rpmsg. In that case I think one pair of
virings would be sufficient for the three streams.


The current channel allocation is like this:
Ch# Type Used for RX size TX size
------------------------------------------------------------------
1 Stream Modem Crash dump 64 kB 4 kB
2 Stream Flash-less Boot Protocol 4 kB 256 kB
3 Stream Boot Logging 8 kB 4kB
4 Packet CAIF high speed 8*16 kB 8*16kB
[5 Packet CAIF low latency 4*1kB 4*1kB]

#5 is probably going to be removed.

Regards,
Sjur

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Using remoteproc with ST-Ericsson modem.
  2012-03-31 21:25                       ` Sjur Brændeland
@ 2012-04-01  6:15                         ` Ohad Ben-Cohen
  0 siblings, 0 replies; 26+ messages in thread
From: Ohad Ben-Cohen @ 2012-04-01  6:15 UTC (permalink / raw)
  To: Sjur Brændeland; +Cc: Arnd Bergmann, linux-kernel, Linus Walleij

Hi Sjur,

On Sun, Apr 1, 2012 at 12:25 AM, Sjur Brændeland <sjurbren@gmail.com> wrote:
> Good stuff. One question from top of my mind: Have you considered
> option of using rpmsg without the protocol, eg something like
> RPMSG_RAW, where I can take advantage of rpmsg transport features
> without needing the muxing feature. Then I could use CAIF muxing layer
> without the rpmsg protocol and naming services....?

I think you may already be able to do so: the naming services are only
enabled when VIRTIO_RPMSG_F_NS is present. About the rest of the rpmsg
protocol - I think you can largely ignore it. Rpmsg drivers shouldn't
really care about it, they just need to use the send/receive API and
everything would just work.

> Another option is using virtio_console, but I realise virtio_console is doing
> normal kmalloc so memory handling in virtio_console would have to change
> then. (I have a memory addressing issue, so modem cannot access all
> kernel memory).

What's the limitation you have? only a certain range of addresses is
visible to the modem?

OMAP4 has a related problem: the memory that virtio_console allocates
isn't immediately visible to the OMAP4's remote processors, because it
isn't mapped in the relevant IOMMU. To fix this, we may need
virtio_console to start allocating its memory with the DMA API (but
we'll only start discussing it when the IOMMU-based DMA API will be
merged). I guess that something along these lines would work out for
you too ?

Thanks,
Ohad.

^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2012-04-01  6:16 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-12-07  9:27 [PATCH 0/9] XSHM: Shared Memory Driver for ST-E Thor M7400 LTE modem Sjur Brændeland
2011-12-07  9:28 ` [PATCH 1/9] xshm: Shared Memory layout for ST-E M7400 driver Sjur Brændeland
2011-12-07  9:28 ` [PATCH 2/9] xshm: Channel config definitions " Sjur Brændeland
2011-12-07  9:28 ` [PATCH 3/9] xshm: Config data use for platform devices Sjur Brændeland
2011-12-07  9:28 ` [PATCH 4/9] xshm: geni/geno driver interface Sjur Brændeland
2011-12-07  9:28 ` [PATCH 5/9] xshm: genio dummy driver Sjur Brændeland
2011-12-07  9:28 ` [PATCH 6/9] xshm: Platform device for XSHM Sjur Brændeland
2011-12-07  9:28 ` [PATCH 7/9] xshm: Character device for XSHM channel access Sjur Brændeland
2011-12-07  9:28 ` [PATCH 8/9] xshm: Makefile and Kconfig for M7400 Shared Memory Drivers Sjur Brændeland
2011-12-07 12:57   ` Linus Walleij
2011-12-07  9:28 ` [PATCH 9/9] caif-xshm: Add CAIF driver for Shared memory for M7400 Sjur Brændeland
2011-12-07 12:30 ` [PATCH 0/9] XSHM: Shared Memory Driver for ST-E Thor M7400 LTE modem Arnd Bergmann
2011-12-07 15:44   ` Sjur BRENDELAND
2011-12-09 14:42     ` Arnd Bergmann
2011-12-12  9:05       ` Sjur BRENDELAND
2012-03-22 14:31       ` Sjur BRENDELAND
2012-03-22 16:57         ` Arnd Bergmann
2012-03-27 13:15           ` Using remoteproc with ST-Ericsson modem Sjur BRENDELAND
2012-03-27 13:56             ` Ohad Ben-Cohen
2012-03-28  9:33               ` Sjur BRENDELAND
2012-03-28 15:55                 ` Ohad Ben-Cohen
2012-03-28 17:31                   ` Sjur BRENDELAND
2012-03-31 19:04                     ` Ohad Ben-Cohen
2012-03-31 21:25                       ` Sjur Brændeland
2012-04-01  6:15                         ` Ohad Ben-Cohen
2011-12-07 12:50 ` [PATCH 0/9] XSHM: Shared Memory Driver for ST-E Thor M7400 LTE modem Linus Walleij

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.