summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Blair <chris.blair@stericsson.com>2011-11-19 08:51:15 +0000
committerPhilippe Langlais <philippe.langlais@stericsson.com>2012-06-05 10:40:22 +0200
commit8a374a1c9fe6aaa00c514e3ce77f31c405ce8b8e (patch)
tree35872b10d7631abb092b4243def34566bb5873b7
parente754d13c1713e247b884af5f210d1c6d62948e85 (diff)
modem: Add M6718 IPC SPI driver netlink support
Adds netlink support to the modem driver. Userspace can send requests for status on the link and they can subscribe for broadcasrs on the link. ST-Ericsson ID: 369397 ST-Ericsson FOSS-OUT ID: STETL-FOSS-OUT-12224 ST-Ericsson Linux next: NA Change-Id: I9159080c7337498368da759ca2b20106dd41381c Signed-off-by: Chris Blair <chris.blair@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36499 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
-rw-r--r--drivers/modem/m6718_spi/Makefile3
-rw-r--r--drivers/modem/m6718_spi/modem_netlink.h20
-rw-r--r--drivers/modem/m6718_spi/netlink.c182
-rw-r--r--drivers/modem/m6718_spi/protocol.c2
4 files changed, 206 insertions, 1 deletions
diff --git a/drivers/modem/m6718_spi/Makefile b/drivers/modem/m6718_spi/Makefile
index b3898c62662..5cb265b00ac 100644
--- a/drivers/modem/m6718_spi/Makefile
+++ b/drivers/modem/m6718_spi/Makefile
@@ -5,7 +5,8 @@ ifeq ($(CONFIG_MODEM_M6718_SPI_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
endif
-m6718_modem_spi-objs := modem_driver.o protocol.o util.o queue.o debug.o
+m6718_modem_spi-objs := modem_driver.o protocol.o util.o queue.o debug.o \
+ netlink.o
ifeq ($(CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE),y)
m6718_modem_spi-objs += modem_state.o
diff --git a/drivers/modem/m6718_spi/modem_netlink.h b/drivers/modem/m6718_spi/modem_netlink.h
new file mode 100644
index 00000000000..19e123d9b12
--- /dev/null
+++ b/drivers/modem/m6718_spi/modem_netlink.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Chris Blair <chris.blair@stericsson.com> for ST-Ericsson
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ * Modem IPC driver protocol interface header:
+ * netlink related functionality.
+ */
+#ifndef _MODEM_NETLINK_H_
+#define _MODEM_NETLINK_H_
+
+#include "modem_protocol.h"
+
+bool ipc_create_netlink_socket(struct ipc_link_context *context);
+void ipc_broadcast_modem_online(struct ipc_link_context *context);
+void ipc_broadcast_modem_reset(struct ipc_link_context *context);
+
+#endif /* _MODEM_NETLINK_H_ */
diff --git a/drivers/modem/m6718_spi/netlink.c b/drivers/modem/m6718_spi/netlink.c
new file mode 100644
index 00000000000..253b19162b1
--- /dev/null
+++ b/drivers/modem/m6718_spi/netlink.c
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010,2011
+ *
+ * Author: Chris Blair <chris.blair@stericsson.com> for ST-Ericsson
+ * based on shrm_protocol.c
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ * U9500 <-> M6718 IPC protocol implementation using SPI:
+ * netlink related functionality
+ */
+#include <linux/netlink.h>
+#include <linux/spi/spi.h>
+#include <linux/modem/m6718_spi/modem_net.h>
+#include <linux/modem/m6718_spi/modem_char.h>
+#include "modem_protocol.h"
+#include "modem_private.h"
+#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE
+#include "modem_state.h"
+#endif
+
+static struct sock *netlink_sk;
+struct modem_spi_dev *modem_dev;
+
+#define MAX_PAYLOAD 1024
+
+/*
+ * Netlink broadcast message values: this must correspond to those values
+ * expected by userspace for the appropriate message.
+ */
+enum netlink_msg_id {
+ NETLINK_MODEM_RESET = 1,
+ NETLINK_MODEM_QUERY_STATE,
+ NETLINK_USER_REQUEST_MODEM_RESET,
+ NETLINK_MODEM_STATUS_ONLINE,
+ NETLINK_MODEM_STATUS_OFFLINE
+};
+
+static void netlink_multicast_tasklet(unsigned long data)
+{
+ struct sk_buff *skb;
+ struct nlmsghdr *nlh;
+ enum netlink_msg_id nlmsg = (enum netlink_msg_id)data;
+
+ if (netlink_sk == NULL) {
+ pr_err("could not send multicast, no socket\n");
+ return;
+ }
+
+ /* prepare netlink message */
+ skb = alloc_skb(NLMSG_SPACE(MAX_PAYLOAD), GFP_ATOMIC);
+ if (!skb) {
+ pr_err("failed to allocate socket buffer\n");
+ return;
+ }
+
+ if (nlmsg == NETLINK_MODEM_RESET)
+ modem_isa_reset(modem_dev);
+
+ nlh = (struct nlmsghdr *)skb->data;
+ nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
+ nlh->nlmsg_pid = 0; /* from kernel */
+ nlh->nlmsg_flags = 0;
+ *(int *)NLMSG_DATA(nlh) = nlmsg;
+ skb_put(skb, MAX_PAYLOAD);
+ /* sender is in group 1<<0 */
+ NETLINK_CB(skb).pid = 0; /* from kernel */
+ /* to mcast group 1<<0 */
+ NETLINK_CB(skb).dst_group = 1;
+
+ /* multicast the message to all listening processes */
+ pr_debug("sending netlink multicast message %d\n", nlmsg);
+ netlink_broadcast(netlink_sk, skb, 0, 1, GFP_ATOMIC);
+
+}
+
+static void send_unicast(int dst_pid)
+{
+ struct sk_buff *skb;
+ struct nlmsghdr *nlh;
+
+ if (netlink_sk == NULL) {
+ pr_err("could not send unicast, no socket\n");
+ return;
+ }
+
+ /* prepare the message for unicast */
+ skb = alloc_skb(NLMSG_SPACE(MAX_PAYLOAD), GFP_KERNEL);
+ if (!skb) {
+ pr_err("failed to allocate socket buffer\n");
+ return;
+ }
+
+ nlh = (struct nlmsghdr *)skb->data;
+ nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
+ nlh->nlmsg_pid = 0; /* from kernel */
+ nlh->nlmsg_flags = 0;
+
+ if (modem_m6718_spi_is_boot_done()) {
+ pr_debug("sending netlink unicast message %d\n",
+ NETLINK_MODEM_STATUS_ONLINE);
+ *(int *)NLMSG_DATA(nlh) = NETLINK_MODEM_STATUS_ONLINE;
+ } else {
+ pr_debug("sending netlink unicast message %d\n",
+ NETLINK_MODEM_STATUS_OFFLINE);
+ *(int *)NLMSG_DATA(nlh) = NETLINK_MODEM_STATUS_OFFLINE;
+ }
+
+ skb_put(skb, MAX_PAYLOAD);
+ /* sender is in group 1<<0 */
+ NETLINK_CB(skb).pid = 0; /* from kernel */
+ NETLINK_CB(skb).dst_group = 0;
+
+ /* unicast the message to the querying process */
+ netlink_unicast(netlink_sk, skb, dst_pid, MSG_DONTWAIT);
+}
+
+static void netlink_receive(struct sk_buff *skb)
+{
+ struct nlmsghdr *nlh = NULL;
+ int msg;
+
+ nlh = (struct nlmsghdr *)skb->data;
+ msg = *((int *)(NLMSG_DATA(nlh)));
+ switch (msg) {
+ case NETLINK_MODEM_QUERY_STATE:
+ send_unicast(nlh->nlmsg_pid);
+ break;
+ case NETLINK_USER_REQUEST_MODEM_RESET:
+ pr_info("user requested modem reset!\n");
+#ifdef CONFIG_DEBUG_FS
+ if (l1_context.msr_disable) {
+ pr_info("MSR is disabled, ignoring reset request\n");
+ break;
+ }
+#endif
+#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE
+ modem_state_force_reset();
+#else
+ pr_err("modestate integration is not enabled in IPC, "
+ "unable to reset modem\n");
+#endif
+ break;
+ default:
+ pr_debug("ignoring invalid netlink message\n");
+ break;
+ }
+}
+
+bool ipc_create_netlink_socket(struct ipc_link_context *context)
+{
+ if (netlink_sk != NULL)
+ return true;
+
+ netlink_sk = netlink_kernel_create(NULL, NETLINK_MODEM, 1,
+ netlink_receive, NULL, THIS_MODULE);
+ if (netlink_sk == NULL) {
+ dev_err(&context->sdev->dev,
+ "failed to create netlink socket\n");
+ return false;
+ }
+ modem_dev = spi_get_drvdata(context->sdev);
+ return true;
+}
+
+DECLARE_TASKLET(modem_online_tasklet, netlink_multicast_tasklet,
+ NETLINK_MODEM_STATUS_ONLINE);
+DECLARE_TASKLET(modem_reset_tasklet, netlink_multicast_tasklet,
+ NETLINK_MODEM_RESET);
+
+void ipc_broadcast_modem_online(struct ipc_link_context *context)
+{
+ dev_info(&context->sdev->dev, "broadcast modem online event!\n");
+ tasklet_schedule(&modem_online_tasklet);
+}
+
+void ipc_broadcast_modem_reset(struct ipc_link_context *context)
+{
+ dev_info(&context->sdev->dev, "broadcast modem reset event!\n");
+ tasklet_schedule(&modem_reset_tasklet);
+}
+
diff --git a/drivers/modem/m6718_spi/protocol.c b/drivers/modem/m6718_spi/protocol.c
index a8e3ce42389..86b124120ce 100644
--- a/drivers/modem/m6718_spi/protocol.c
+++ b/drivers/modem/m6718_spi/protocol.c
@@ -13,6 +13,7 @@
#include "modem_util.h"
#include "modem_queue.h"
#include "modem_debug.h"
+#include "modem_netlink.h"
#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE
#include <linux/workqueue.h>
@@ -326,6 +327,7 @@ int modem_protocol_probe(struct spi_device *sdev)
ipc_queue_init(context);
ipc_dbg_debugfs_link_init(context);
ipc_dbg_throughput_link_init(context);
+ ipc_create_netlink_socket(context);
/*
* For link0 (the handshake link) we force a state transition now so