1 /*-
2  * Copyright (c) 2010 Max Khon <fjoe@freebsd.org>
3  * All rights reserved.
4  *
5  * This software was developed by Max Khon under sponsorship from
6  * the FreeBSD Foundation and Ethon Technologies GmbH.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $Id: bsd-compat.c 9253 2010-09-02 10:12:09Z fjoe $
30  */
31 
32 #include <sys/types.h>
33 #include <sys/limits.h>
34 #include <sys/bus.h>
35 #include <sys/callout.h>
36 #include <sys/firmware.h>
37 #include <sys/param.h>
38 #include <sys/proc.h>
39 #include <sys/syscallsubr.h>
40 #include <sys/systm.h>
41 #include <sys/taskqueue.h>
42 
43 #include <machine/stdarg.h>
44 
45 #include "mbox_if.h"
46 
47 #include <interface/compat/vchi_bsd.h>
48 
49 MALLOC_DEFINE(M_VCHI, "VCHI", "VCHI");
50 
51 /*
52  * Timer API
53  */
54 static void
55 run_timer(void *arg)
56 {
57 	struct timer_list *t = (struct timer_list *) arg;
58 	void (*function)(unsigned long);
59 
60 	mtx_lock_spin(&t->mtx);
61 	if (callout_pending(&t->callout)) {
62 		/* callout was reset */
63 		mtx_unlock_spin(&t->mtx);
64 		return;
65 	}
66 	if (!callout_active(&t->callout)) {
67 		/* callout was stopped */
68 		mtx_unlock_spin(&t->mtx);
69 		return;
70 	}
71 	callout_deactivate(&t->callout);
72 
73 	function = t->function;
74 	mtx_unlock_spin(&t->mtx);
75 
76 	function(t->data);
77 }
78 
79 void
80 vchiq_init_timer(struct timer_list *t)
81 {
82 	mtx_init(&t->mtx, "dahdi timer lock", NULL, MTX_SPIN);
83 	callout_init(&t->callout, 1);
84 	t->expires = 0;
85 	/*
86 	 * function and data are not initialized intentionally:
87 	 * they are not initialized by Linux implementation too
88 	 */
89 }
90 
91 void
92 vchiq_setup_timer(struct timer_list *t, void (*function)(unsigned long), unsigned long data)
93 {
94 	t->function = function;
95 	t->data = data;
96 	vchiq_init_timer(t);
97 }
98 
99 void
100 vchiq_mod_timer(struct timer_list *t, unsigned long expires)
101 {
102 	mtx_lock_spin(&t->mtx);
103 	callout_reset(&t->callout, expires - jiffies, run_timer, t);
104 	mtx_unlock_spin(&t->mtx);
105 }
106 
107 void
108 vchiq_add_timer(struct timer_list *t)
109 {
110 	vchiq_mod_timer(t, t->expires);
111 }
112 
113 int
114 vchiq_del_timer_sync(struct timer_list *t)
115 {
116 	mtx_lock_spin(&t->mtx);
117 	callout_stop(&t->callout);
118 	mtx_unlock_spin(&t->mtx);
119 
120 	mtx_destroy(&t->mtx);
121 	return 0;
122 }
123 
124 int
125 vchiq_del_timer(struct timer_list *t)
126 {
127 	vchiq_del_timer_sync(t);
128 	return 0;
129 }
130 
131 /*
132  * Completion API
133  */
134 void
135 init_completion(struct completion *c)
136 {
137 	cv_init(&c->cv, "VCHI completion cv");
138 	mtx_init(&c->lock, "VCHI completion lock", "condvar", MTX_DEF);
139 	c->done = 0;
140 }
141 
142 void
143 destroy_completion(struct completion *c)
144 {
145 	cv_destroy(&c->cv);
146 	mtx_destroy(&c->lock);
147 }
148 
149 void
150 complete(struct completion *c)
151 {
152 	mtx_lock(&c->lock);
153 
154 	if (c->done >= 0) {
155 		KASSERT(c->done < INT_MAX, ("c->done overflow")); /* XXX check */
156 		c->done++;
157 		cv_signal(&c->cv);
158 	} else {
159 		KASSERT(c->done == -1, ("Invalid value of c->done: %d", c->done));
160 	}
161 
162 	mtx_unlock(&c->lock);
163 }
164 
165 void
166 complete_all(struct completion *c)
167 {
168 	mtx_lock(&c->lock);
169 
170 	if (c->done >= 0) {
171 		KASSERT(c->done < INT_MAX, ("c->done overflow")); /* XXX check */
172 		c->done = -1;
173 		cv_broadcast(&c->cv);
174 	} else {
175 		KASSERT(c->done == -1, ("Invalid value of c->done: %d", c->done));
176 	}
177 
178 	mtx_unlock(&c->lock);
179 }
180 
181 void
182 INIT_COMPLETION_locked(struct completion *c)
183 {
184 	mtx_lock(&c->lock);
185 
186 	c->done = 0;
187 
188 	mtx_unlock(&c->lock);
189 }
190 
191 static void
192 _completion_claim(struct completion *c)
193 {
194 
195 	KASSERT(mtx_owned(&c->lock),
196 	    ("_completion_claim should be called with acquired lock"));
197 	KASSERT(c->done != 0, ("_completion_claim on non-waited completion"));
198 	if (c->done > 0)
199 		c->done--;
200 	else
201 		KASSERT(c->done == -1, ("Invalid value of c->done: %d", c->done));
202 }
203 
204 void
205 wait_for_completion(struct completion *c)
206 {
207 	mtx_lock(&c->lock);
208 	if (!c->done)
209 		cv_wait(&c->cv, &c->lock);
210 	c->done--;
211 	mtx_unlock(&c->lock);
212 }
213 
214 int
215 try_wait_for_completion(struct completion *c)
216 {
217 	int res = 0;
218 
219 	mtx_lock(&c->lock);
220 	if (!c->done)
221 		res = 1;
222 	else
223 		c->done--;
224 	mtx_unlock(&c->lock);
225 	return res == 0;
226 }
227 
228 int
229 wait_for_completion_interruptible_timeout(struct completion *c, unsigned long timeout)
230 {
231 	int res = 0;
232 	unsigned long start, now;
233 	start = jiffies;
234 
235 	mtx_lock(&c->lock);
236 	while (c->done == 0) {
237 		res = cv_timedwait_sig(&c->cv, &c->lock, timeout);
238 		if (res)
239 			goto out;
240 		now = jiffies;
241 		if (timeout < (now - start)) {
242 			res = EWOULDBLOCK;
243 			goto out;
244 		}
245 
246 		timeout -= (now - start);
247 		start = now;
248 	}
249 
250 	_completion_claim(c);
251 	res = 0;
252 
253 out:
254 	mtx_unlock(&c->lock);
255 
256 	if (res == EWOULDBLOCK) {
257 		return 0;
258 	} else if ((res == EINTR) || (res == ERESTART)) {
259 		return -ERESTART;
260 	} else {
261 		KASSERT((res == 0), ("res = %d", res));
262 		return timeout;
263 	}
264 }
265 
266 int
267 wait_for_completion_interruptible(struct completion *c)
268 {
269 	int res = 0;
270 
271 	mtx_lock(&c->lock);
272 	while (c->done == 0) {
273 		res = cv_wait_sig(&c->cv, &c->lock);
274 		if (res)
275 			goto out;
276 	}
277 
278 	_completion_claim(c);
279 
280 out:
281 	mtx_unlock(&c->lock);
282 
283 	if ((res == EINTR) || (res == ERESTART))
284 		res = -ERESTART;
285 	return res;
286 }
287 
288 int
289 wait_for_completion_killable(struct completion *c)
290 {
291 
292 	return wait_for_completion_interruptible(c);
293 }
294 
295 /*
296  * Semaphore API
297  */
298 
299 void sema_sysinit(void *arg)
300 {
301 	struct semaphore *s = arg;
302 
303 	_sema_init(s, 1);
304 }
305 
306 void
307 _sema_init(struct semaphore *s, int value)
308 {
309 	bzero(s, sizeof(*s));
310 	mtx_init(&s->mtx, "sema lock", "VCHIQ sepmaphore backing lock",
311 		MTX_DEF | MTX_NOWITNESS | MTX_QUIET);
312 	cv_init(&s->cv, "sema cv");
313 	s->value = value;
314 }
315 
316 void
317 _sema_destroy(struct semaphore *s)
318 {
319 	mtx_destroy(&s->mtx);
320 	cv_destroy(&s->cv);
321 }
322 
323 void
324 down(struct semaphore *s)
325 {
326 
327 	mtx_lock(&s->mtx);
328 	while (s->value == 0) {
329 		s->waiters++;
330 		cv_wait(&s->cv, &s->mtx);
331 		s->waiters--;
332 	}
333 
334 	s->value--;
335 	mtx_unlock(&s->mtx);
336 }
337 
338 int
339 down_interruptible(struct semaphore *s)
340 {
341 	int ret ;
342 
343 	ret = 0;
344 
345 	mtx_lock(&s->mtx);
346 
347 	while (s->value == 0) {
348 		s->waiters++;
349 		ret = cv_wait_sig(&s->cv, &s->mtx);
350 		s->waiters--;
351 
352 		if (ret == EINTR) {
353 			mtx_unlock(&s->mtx);
354 			return (-EINTR);
355 		}
356 
357 		if (ret == ERESTART)
358 			continue;
359 	}
360 
361 	s->value--;
362 	mtx_unlock(&s->mtx);
363 
364 	return (0);
365 }
366 
367 int
368 down_trylock(struct semaphore *s)
369 {
370 	int ret;
371 
372 	ret = 0;
373 
374 	mtx_lock(&s->mtx);
375 
376 	if (s->value > 0) {
377 		/* Success. */
378 		s->value--;
379 		ret = 0;
380 	} else {
381 		ret = -EAGAIN;
382 	}
383 
384 	mtx_unlock(&s->mtx);
385 
386 	return (ret);
387 }
388 
389 void
390 up(struct semaphore *s)
391 {
392 	mtx_lock(&s->mtx);
393 	s->value++;
394 	if (s->waiters && s->value > 0)
395 		cv_signal(&s->cv);
396 
397 	mtx_unlock(&s->mtx);
398 }
399 
400 /*
401  * Logging API
402  */
403 void
404 rlprintf(int pps, const char *fmt, ...)
405 {
406 	va_list ap;
407 	static struct timeval last_printf;
408 	static int count;
409 
410 	if (ppsratecheck(&last_printf, &count, pps)) {
411 		va_start(ap, fmt);
412 		vprintf(fmt, ap);
413 		va_end(ap);
414 	}
415 }
416 
417 void
418 device_rlprintf(int pps, device_t dev, const char *fmt, ...)
419 {
420 	va_list ap;
421 	static struct timeval last_printf;
422 	static int count;
423 
424 	if (ppsratecheck(&last_printf, &count, pps)) {
425 		va_start(ap, fmt);
426 		device_print_prettyname(dev);
427 		vprintf(fmt, ap);
428 		va_end(ap);
429 	}
430 }
431 
432 /*
433  * Signals API
434  */
435 
436 void
437 flush_signals(VCHIQ_THREAD_T thr)
438 {
439 	printf("Implement ME: %s\n", __func__);
440 }
441 
442 int
443 fatal_signal_pending(VCHIQ_THREAD_T thr)
444 {
445 	printf("Implement ME: %s\n", __func__);
446 	return (0);
447 }
448 
449 /*
450  * kthread API
451  */
452 
453 /*
454  *  This is a hack to avoid memory leak
455  */
456 #define MAX_THREAD_DATA_SLOTS	32
457 static int thread_data_slot = 0;
458 
459 struct thread_data {
460 	void *data;
461 	int (*threadfn)(void *);
462 };
463 
464 static struct thread_data thread_slots[MAX_THREAD_DATA_SLOTS];
465 
466 static void
467 kthread_wrapper(void *data)
468 {
469 	struct thread_data *slot;
470 
471 	slot = data;
472 	slot->threadfn(slot->data);
473 }
474 
475 VCHIQ_THREAD_T
476 vchiq_thread_create(int (*threadfn)(void *data),
477 	void *data,
478 	const char namefmt[], ...)
479 {
480 	VCHIQ_THREAD_T newp;
481 	va_list ap;
482 	char name[MAXCOMLEN+1];
483 	struct thread_data *slot;
484 
485 	if (thread_data_slot >= MAX_THREAD_DATA_SLOTS) {
486 		printf("kthread_create: out of thread data slots\n");
487 		return (NULL);
488 	}
489 
490 	slot = &thread_slots[thread_data_slot];
491 	slot->data = data;
492 	slot->threadfn = threadfn;
493 
494 	va_start(ap, namefmt);
495 	vsnprintf(name, sizeof(name), namefmt, ap);
496 	va_end(ap);
497 
498 	newp = NULL;
499 	if (kproc_create(kthread_wrapper, (void*)slot, &newp, 0, 0,
500 	    "%s", name) != 0) {
501 		/* Just to be sure */
502 		newp = NULL;
503 	}
504 	else
505 		thread_data_slot++;
506 
507 	return newp;
508 }
509 
510 void
511 set_user_nice(VCHIQ_THREAD_T thr, int nice)
512 {
513 	/* NOOP */
514 }
515 
516 void
517 wake_up_process(VCHIQ_THREAD_T thr)
518 {
519 	/* NOOP */
520 }
521 
522 void
523 bcm_mbox_write(int channel, uint32_t data)
524 {
525 	device_t mbox;
526 
527         mbox = devclass_get_device(devclass_find("mbox"), 0);
528 
529         if (mbox)
530                 MBOX_WRITE(mbox, channel, data);
531 }
532