[RAMEN9610-20413][9610] wlbt: SCSC Driver version 10.6.1.0
[GitHub/MotorolaMobilityLLC/kernel-slsi.git] / drivers / misc / samsung / scsc / mxlog_transport.c
1 /****************************************************************************
2 *
3 * Copyright (c) 2014 - 2017 Samsung Electronics Co., Ltd. All rights reserved
4 *
5 ****************************************************************************/
6 /** Uses */
7 #include <linux/module.h>
8 #include <linux/slab.h>
9 #include <linux/sched.h>
10 #include <linux/sched/task.h>
11 #include <scsc/scsc_logring.h>
12 #include "scsc_mif_abs.h"
13 #include "mifintrbit.h"
14 /** Implements */
15 #include "mxlog_transport.h"
16
17 #define MXLOG_TRANSPORT_BUF_LENGTH (16 * 1024)
18 #define MXLOG_TRANSPORT_PACKET_SIZE (4)
19
20 /* Flag that an error has occurred so the I/O thread processing should stop */
21 void mxlog_transport_set_error(struct mxlog_transport *mxlog_transport)
22 {
23 SCSC_TAG_WARNING(MXLOG_TRANS, "I/O thread processing is suspended\n");
24
25 mxlog_transport->mxlog_thread.block_thread = 1;
26 }
27
28 static void input_irq_handler(int irq, void *data)
29 {
30 struct mxlog_transport *mxlog_transport = (struct mxlog_transport *)data;
31 struct mxlog_thread *th = &mxlog_transport->mxlog_thread;
32 struct scsc_mif_abs *mif_abs;
33
34 SCSC_TAG_DEBUG(MXLOG_TRANS, "mxlog intr\n");
35 /* Clear the interrupt first to ensure we can't possibly miss one */
36 mif_abs = scsc_mx_get_mif_abs(mxlog_transport->mx);
37 mif_abs->irq_bit_clear(mif_abs, irq);
38
39 /* The the other side wrote some data to the input stream,
40 * wake up the thread that deals with this.
41 */
42 if (th->task == NULL) {
43 SCSC_TAG_ERR(MXLOG_TRANS, "mxlog_thread is NOT running\n");
44 return;
45 }
46 /*
47 * If an error has occured, we discard silently all messages from
48 * the stream until the error has been processed and the system has
49 * been reinitialised.
50 */
51 if (th->block_thread == 1) {
52 SCSC_TAG_DEBUG(MXLOG_TRANS, "discard message.\n");
53 /*
54 * Do not try to acknowledge a pending interrupt here.
55 * This function is called by a function which in turn can be
56 * running in an atomic or 'disabled irq' level.
57 */
58 return;
59 }
60 th->wakeup_flag = 1;
61
62 /* wake up I/O thread */
63 wake_up_interruptible(&th->wakeup_q);
64 }
65
66 static void thread_wait_until_stopped(struct mxlog_transport *mxlog_transport)
67 {
68 struct mxlog_thread *th = &mxlog_transport->mxlog_thread;
69
70 /*
71 * kthread_stop() cannot handle the th exiting while
72 * kthread_should_stop() is false, so sleep until kthread_stop()
73 * wakes us up.
74 */
75 SCSC_TAG_INFO(MXLOG_TRANS, "%s waiting for the stop signal.\n", th->name);
76 set_current_state(TASK_INTERRUPTIBLE);
77 if (!kthread_should_stop()) {
78 SCSC_TAG_DEBUG(MXLOG_TRANS, "%s schedule....\n", th->name);
79 schedule();
80 }
81
82 /**
83 * Caller that spawned the kthread did a get_task_struct()
84 * on task_struct, that will be released on stop...
85 * ...so we should NOT nullify th->task here.
86 */
87 SCSC_TAG_DEBUG(MXLOG_TRANS, "%s exiting.\n", th->name);
88 }
89
90 /**
91 * A thread that forwards messages sent across the transport to
92 * the registered handlers for each channel.
93 */
94 static int mxlog_thread_function(void *arg)
95 {
96 struct mxlog_transport *mxlog_transport = (struct mxlog_transport *)arg;
97 struct mxlog_thread *th = &mxlog_transport->mxlog_thread;
98 int ret;
99 u32 header;
100 char *buf = NULL;
101 size_t buf_sz = 4096;
102
103 buf = kmalloc(buf_sz, GFP_KERNEL);
104 if (!buf) {
105 SCSC_TAG_ERR(MXLOG_TRANS, "Failed to alloc %s local buffer...exiting.\n", th->name);
106 return -ENOMEM;
107 }
108 /* completion is used only for startup thread-synchronization */
109 complete(&th->completion);
110 /* Thread ready...start ISR processing*/
111 th->block_thread = 0;
112 while (!kthread_should_stop()) {
113 /* wait until an error occurs, or we need to process */
114 ret = wait_event_interruptible(th->wakeup_q,
115 (th->wakeup_flag && !th->block_thread) ||
116 kthread_should_stop());
117
118 if (kthread_should_stop()) {
119 SCSC_TAG_DEBUG(MXLOG_TRANS, "signalled to exit\n");
120 break;
121 }
122 if (ret < 0) {
123 SCSC_TAG_DEBUG(MXLOG_TRANS,
124 "wait_event returned %d, thread will exit\n", ret);
125 thread_wait_until_stopped(mxlog_transport);
126 break;
127 }
128 th->wakeup_flag = 0;
129 SCSC_TAG_DEBUG(MXLOG_TRANS, "wokeup: r=%d\n", ret);
130 if (!mxlog_transport->header_handler_fn) {
131 /* Invalid header handler:
132 * unrecoverable log and terminate
133 */
134 SCSC_TAG_WARNING(MXLOG_TRANS,
135 "mxlog_transport->header_handler_fn_==NULL\n");
136 break;
137 }
138 while (mif_stream_read(&mxlog_transport->mif_stream,
139 &header, sizeof(uint32_t))) {
140 u8 level = 0;
141 u8 phase = 0;
142 u32 num_bytes = 0;
143
144 mutex_lock(&mxlog_transport->lock);
145 if (!mxlog_transport->header_handler_fn) {
146 /* Invalid header handler:
147 * unrecoverable log and terminate
148 */
149 SCSC_TAG_WARNING(MXLOG_TRANS,
150 "mxlog_transport->header_handler_fn_==NULL. Channel has been released\n");
151 mutex_unlock(&mxlog_transport->lock);
152 /* not recoverable, terminate straight away */
153 goto mxlog_thread_exit;
154 }
155 /**
156 * A generic header processor will properly retrieve
157 * level and num_bytes as specifically implemented
158 * by the phase.
159 */
160 if (mxlog_transport->header_handler_fn(header, &phase,
161 &level, &num_bytes)) {
162 SCSC_TAG_ERR(MXLOG_TRANS,
163 "Bad sync in header: header=0x%08x\n", header);
164 mutex_unlock(&mxlog_transport->lock);
165 /* not recoverable, terminate straight away */
166 goto mxlog_thread_exit;
167 }
168 if (num_bytes > 0 &&
169 num_bytes < (MXLOG_TRANSPORT_BUF_LENGTH - sizeof(uint32_t))) {
170 u32 ret_bytes = 0;
171
172 /* 2nd read - payload (msg) */
173 ret_bytes = mif_stream_read(&mxlog_transport->mif_stream,
174 buf, num_bytes);
175 mxlog_transport->channel_handler_fn(phase, buf,
176 ret_bytes,
177 level,
178 mxlog_transport->channel_handler_data);
179 } else {
180 SCSC_TAG_ERR(MXLOG_TRANS,
181 "Bad num_bytes(%d) in header: header=0x%08x\n",
182 num_bytes, header);
183 }
184 mutex_unlock(&mxlog_transport->lock);
185 }
186 }
187
188 mxlog_thread_exit:
189 SCSC_TAG_INFO(MXLOG_TRANS, "exiting....\n");
190 kfree(buf);
191 return 0;
192 }
193
194 static int mxlog_thread_start(struct mxlog_transport *mxlog_transport)
195 {
196 int err;
197 struct mxlog_thread *th = &mxlog_transport->mxlog_thread;
198
199 if (th->task != NULL) {
200 SCSC_TAG_WARNING(MXLOG_TRANS, "%s thread already started\n", th->name);
201 return 0;
202 }
203
204 /* Initialise thread structure */
205 th->block_thread = 1;
206 init_waitqueue_head(&th->wakeup_q);
207 init_completion(&th->completion);
208 th->wakeup_flag = 0;
209 snprintf(th->name, MXLOG_THREAD_NAME_MAX_LENGTH, "mxlog_thread");
210
211 /* Start the kernel thread */
212 th->task = kthread_run(mxlog_thread_function, mxlog_transport, "%s", th->name);
213 if (IS_ERR(th->task)) {
214 int err = (int)PTR_ERR(th->task);
215
216 th->task = NULL;
217 return err;
218 }
219
220 /**
221 * Avoid this task_struct vanishes immediately
222 * if the kthread exits by its own.
223 */
224 get_task_struct(th->task);
225
226 /* wait until thread function is running */
227 #define LOG_THREAD_START_TMO_SEC (3)
228 err = wait_for_completion_timeout(&th->completion, msecs_to_jiffies(LOG_THREAD_START_TMO_SEC * 1000));
229 if (err == 0) {
230 SCSC_TAG_ERR(MXLOG_TRANS, "timeout starting %s\n", th->name);
231 kthread_stop(th->task);
232 put_task_struct(th->task);
233 return -ETIMEDOUT;
234 }
235 SCSC_TAG_INFO(MXLOG_TRANS, "Started thread %s\n", th->name);
236
237 return 0;
238 }
239
240 static void mxlog_thread_stop(struct mxlog_transport *mxlog_transport)
241 {
242 struct mxlog_thread *th = &mxlog_transport->mxlog_thread;
243
244 if (!th->task) {
245 SCSC_TAG_WARNING(MXLOG_TRANS, "%s is already stopped\n", th->name);
246 return;
247 }
248 SCSC_TAG_INFO(MXLOG_TRANS, "Stopping thread %s [%d]\n", th->name, th->task->pid);
249 /* kthread_stop() marks thread as KTHREAD_SHOULD_STOP
250 * and wait for it to terminate
251 */
252 if (kthread_stop(th->task))
253 SCSC_TAG_ERR(MXLOG_TRANS, "Failed to stop %s [%d]\n", th->name, th->task->pid);
254 /* Finally release the task_struct we held on start */
255 put_task_struct(th->task);
256 th->task = NULL;
257 }
258
259 void mxlog_transport_release(struct mxlog_transport *mxlog_transport)
260 {
261 mxlog_thread_stop(mxlog_transport);
262 mif_stream_release(&mxlog_transport->mif_stream);
263 }
264
265 void mxlog_transport_config_serialise(struct mxlog_transport *mxlog_transport,
266 struct mxlogconf *mxlogconf)
267 {
268 mif_stream_config_serialise(&mxlog_transport->mif_stream, &mxlogconf->stream_conf);
269 }
270
271 /** Public functions */
272 int mxlog_transport_init(struct mxlog_transport *mxlog_transport, struct scsc_mx *mx)
273 {
274 int r;
275 uint32_t mem_length = MXLOG_TRANSPORT_BUF_LENGTH;
276 uint32_t packet_size = MXLOG_TRANSPORT_PACKET_SIZE;
277 uint32_t num_packets;
278
279 /*
280 * Initialising a buffer of 1 byte is never legitimate, do not allow it.
281 * The memory buffer length must be a multiple of the packet size.
282 */
283
284 memset(mxlog_transport, 0, sizeof(struct mxlog_transport));
285 mutex_init(&mxlog_transport->lock);
286 num_packets = mem_length / packet_size;
287 mxlog_transport->mx = mx;
288 r = mif_stream_init(&mxlog_transport->mif_stream, SCSC_MIF_ABS_TARGET_R4, MIF_STREAM_DIRECTION_IN, num_packets, packet_size, mx, MIF_STREAM_INTRBIT_TYPE_ALLOC, input_irq_handler, mxlog_transport);
289 if (r)
290 return r;
291 r = mxlog_thread_start(mxlog_transport);
292 if (r) {
293 mif_stream_release(&mxlog_transport->mif_stream);
294 return r;
295 }
296
297 return 0;
298 }
299
300 void mxlog_transport_register_channel_handler(struct mxlog_transport *mxlog_transport,
301 mxlog_header_handler parser,
302 mxlog_channel_handler handler,
303 void *data)
304 {
305 mutex_lock(&mxlog_transport->lock);
306 mxlog_transport->header_handler_fn = parser;
307 mxlog_transport->channel_handler_fn = handler;
308 mxlog_transport->channel_handler_data = (void *)data;
309 mutex_unlock(&mxlog_transport->lock);
310 }