Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.13.7.
  1/*
  2 * Intel MIC Platform Software Stack (MPSS)
  3 *
  4 * Copyright(c) 2015 Intel Corporation.
  5 *
  6 * This program is free software; you can redistribute it and/or modify
  7 * it under the terms of the GNU General Public License, version 2, as
  8 * published by the Free Software Foundation.
  9 *
 10 * This program is distributed in the hope that it will be useful, but
 11 * WITHOUT ANY WARRANTY; without even the implied warranty of
 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 13 * General Public License for more details.
 14 *
 15 * The full GNU General Public License is included in this distribution in
 16 * the file called "COPYING".
 17 *
 18 * Intel MIC COSM Client Driver
 19 *
 20 */
 21#include <linux/module.h>
 22#include <linux/delay.h>
 23#include <linux/reboot.h>
 24#include <linux/kthread.h>
 25#include <linux/sched/signal.h>
 26
 27#include "../cosm/cosm_main.h"
 28
 29#define COSM_SCIF_MAX_RETRIES 10
 30#define COSM_HEARTBEAT_SEND_MSEC (COSM_HEARTBEAT_SEND_SEC * MSEC_PER_SEC)
 31
 32static struct task_struct *client_thread;
 33static scif_epd_t client_epd;
 34static struct scif_peer_dev *client_spdev;
 35
 36/*
 37 * Reboot notifier: receives shutdown status from the OS and communicates it
 38 * back to the COSM process on the host
 39 */
 40static int cosm_reboot_event(struct notifier_block *this, unsigned long event,
 41			     void *ptr)
 42{
 43	struct cosm_msg msg = { .id = COSM_MSG_SHUTDOWN_STATUS };
 44	int rc;
 45
 46	event = (event == SYS_RESTART) ? SYSTEM_RESTART : event;
 47	dev_info(&client_spdev->dev, "%s %d received event %ld\n",
 48		 __func__, __LINE__, event);
 49
 50	msg.shutdown_status = event;
 51	rc = scif_send(client_epd, &msg, sizeof(msg), SCIF_SEND_BLOCK);
 52	if (rc < 0)
 53		dev_err(&client_spdev->dev, "%s %d scif_send rc %d\n",
 54			__func__, __LINE__, rc);
 55
 56	return NOTIFY_DONE;
 57}
 58
 59static struct notifier_block cosm_reboot = {
 60	.notifier_call  = cosm_reboot_event,
 61};
 62
 63/* Set system time from timespec value received from the host */
 64static void cosm_set_time(struct cosm_msg *msg)
 65{
 66	int rc = do_settimeofday64(&msg->timespec);
 67
 68	if (rc)
 69		dev_err(&client_spdev->dev, "%s: %d settimeofday rc %d\n",
 70			__func__, __LINE__, rc);
 71}
 72
 73/* COSM client receive message processing */
 74static void cosm_client_recv(void)
 75{
 76	struct cosm_msg msg;
 77	int rc;
 78
 79	while (1) {
 80		rc = scif_recv(client_epd, &msg, sizeof(msg), 0);
 81		if (!rc) {
 82			return;
 83		} else if (rc < 0) {
 84			dev_err(&client_spdev->dev, "%s: %d rc %d\n",
 85				__func__, __LINE__, rc);
 86			return;
 87		}
 88
 89		dev_dbg(&client_spdev->dev, "%s: %d rc %d id 0x%llx\n",
 90			__func__, __LINE__, rc, msg.id);
 91
 92		switch (msg.id) {
 93		case COSM_MSG_SYNC_TIME:
 94			cosm_set_time(&msg);
 95			break;
 96		case COSM_MSG_SHUTDOWN:
 97			orderly_poweroff(true);
 98			break;
 99		default:
100			dev_err(&client_spdev->dev, "%s: %d unknown id %lld\n",
101				__func__, __LINE__, msg.id);
102			break;
103		}
104	}
105}
106
107/* Initiate connection to the COSM server on the host */
108static int cosm_scif_connect(void)
109{
110	struct scif_port_id port_id;
111	int i, rc;
112
113	client_epd = scif_open();
114	if (!client_epd) {
115		dev_err(&client_spdev->dev, "%s %d scif_open failed\n",
116			__func__, __LINE__);
117		return -ENOMEM;
118	}
119
120	port_id.node = 0;
121	port_id.port = SCIF_COSM_LISTEN_PORT;
122
123	for (i = 0; i < COSM_SCIF_MAX_RETRIES; i++) {
124		rc = scif_connect(client_epd, &port_id);
125		if (rc < 0)
126			msleep(1000);
127		else
128			break;
129	}
130
131	if (rc < 0) {
132		dev_err(&client_spdev->dev, "%s %d scif_connect rc %d\n",
133			__func__, __LINE__, rc);
134		scif_close(client_epd);
135		client_epd = NULL;
136	}
137	return rc < 0 ? rc : 0;
138}
139
140/* Close host SCIF connection */
141static void cosm_scif_connect_exit(void)
142{
143	if (client_epd) {
144		scif_close(client_epd);
145		client_epd = NULL;
146	}
147}
148
149/*
150 * COSM SCIF client thread function: waits for messages from the host and sends
151 * a heartbeat to the host
152 */
153static int cosm_scif_client(void *unused)
154{
155	struct cosm_msg msg = { .id = COSM_MSG_HEARTBEAT };
156	struct scif_pollepd pollepd;
157	int rc;
158
159	allow_signal(SIGKILL);
160
161	while (!kthread_should_stop()) {
162		pollepd.epd = client_epd;
163		pollepd.events = EPOLLIN;
164
165		rc = scif_poll(&pollepd, 1, COSM_HEARTBEAT_SEND_MSEC);
166		if (rc < 0) {
167			if (-EINTR != rc)
168				dev_err(&client_spdev->dev,
169					"%s %d scif_poll rc %d\n",
170					__func__, __LINE__, rc);
171			continue;
172		}
173
174		if (pollepd.revents & EPOLLIN)
175			cosm_client_recv();
176
177		msg.id = COSM_MSG_HEARTBEAT;
178		rc = scif_send(client_epd, &msg, sizeof(msg), SCIF_SEND_BLOCK);
179		if (rc < 0)
180			dev_err(&client_spdev->dev, "%s %d scif_send rc %d\n",
181				__func__, __LINE__, rc);
182	}
183
184	dev_dbg(&client_spdev->dev, "%s %d Client thread stopped\n",
185		__func__, __LINE__);
186	return 0;
187}
188
189static void cosm_scif_probe(struct scif_peer_dev *spdev)
190{
191	int rc;
192
193	dev_dbg(&spdev->dev, "%s %d: dnode %d\n",
194		__func__, __LINE__, spdev->dnode);
195
196	/* We are only interested in the host with spdev->dnode == 0 */
197	if (spdev->dnode)
198		return;
199
200	client_spdev = spdev;
201	rc = cosm_scif_connect();
202	if (rc)
203		goto exit;
204
205	rc = register_reboot_notifier(&cosm_reboot);
206	if (rc) {
207		dev_err(&spdev->dev,
208			"reboot notifier registration failed rc %d\n", rc);
209		goto connect_exit;
210	}
211
212	client_thread = kthread_run(cosm_scif_client, NULL, "cosm_client");
213	if (IS_ERR(client_thread)) {
214		rc = PTR_ERR(client_thread);
215		dev_err(&spdev->dev, "%s %d kthread_run rc %d\n",
216			__func__, __LINE__, rc);
217		goto unreg_reboot;
218	}
219	return;
220unreg_reboot:
221	unregister_reboot_notifier(&cosm_reboot);
222connect_exit:
223	cosm_scif_connect_exit();
224exit:
225	client_spdev = NULL;
226}
227
228static void cosm_scif_remove(struct scif_peer_dev *spdev)
229{
230	int rc;
231
232	dev_dbg(&spdev->dev, "%s %d: dnode %d\n",
233		__func__, __LINE__, spdev->dnode);
234
235	if (spdev->dnode)
236		return;
237
238	if (!IS_ERR_OR_NULL(client_thread)) {
239		rc = send_sig(SIGKILL, client_thread, 0);
240		if (rc) {
241			pr_err("%s %d send_sig rc %d\n",
242			       __func__, __LINE__, rc);
243			return;
244		}
245		kthread_stop(client_thread);
246	}
247	unregister_reboot_notifier(&cosm_reboot);
248	cosm_scif_connect_exit();
249	client_spdev = NULL;
250}
251
252static struct scif_client scif_client_cosm = {
253	.name = KBUILD_MODNAME,
254	.probe = cosm_scif_probe,
255	.remove = cosm_scif_remove,
256};
257
258static int __init cosm_client_init(void)
259{
260	int rc = scif_client_register(&scif_client_cosm);
261
262	if (rc)
263		pr_err("scif_client_register failed rc %d\n", rc);
264	return rc;
265}
266
267static void __exit cosm_client_exit(void)
268{
269	scif_client_unregister(&scif_client_cosm);
270}
271
272module_init(cosm_client_init);
273module_exit(cosm_client_exit);
274
275MODULE_AUTHOR("Intel Corporation");
276MODULE_DESCRIPTION("Intel(R) MIC card OS state management client driver");
277MODULE_LICENSE("GPL v2");