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
49MALLOC_DEFINE(M_VCHI, "VCHI", "VCHI");
50
51/*
52 * Timer API
53 */
54static void
55run_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
79void
80init_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
91void
92setup_timer(struct timer_list *t, void (*function)(unsigned long), unsigned long data)
93{
94	t->function = function;
95	t->data = data;
96	init_timer(t);
97}
98
99void
100mod_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
107void
108add_timer(struct timer_list *t)
109{
110	mod_timer(t, t->expires);
111}
112
113int
114del_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
124int
125del_timer(struct timer_list *t)
126{
127	del_timer_sync(t);
128	return 0;
129}
130
131/*
132 * Completion API
133 */
134void
135init_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
142void
143destroy_completion(struct completion *c)
144{
145	cv_destroy(&c->cv);
146	mtx_destroy(&c->lock);
147}
148
149void
150complete(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
165void
166complete_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
181void
182INIT_COMPLETION_locked(struct completion *c)
183{
184	mtx_lock(&c->lock);
185
186	c->done = 0;
187
188	mtx_unlock(&c->lock);
189}
190
191static 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
204void
205wait_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
214int
215try_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
228int
229wait_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
253out:
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
266int
267wait_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
280out:
281	mtx_unlock(&c->lock);
282
283	if ((res == EINTR) || (res == ERESTART))
284		res = -ERESTART;
285	return res;
286}
287
288int
289wait_for_completion_killable(struct completion *c)
290{
291
292	return wait_for_completion_interruptible(c);
293}
294
295/*
296 * Semaphore API
297 */
298
299void sema_sysinit(void *arg)
300{
301	struct semaphore *s = arg;
302
303	_sema_init(s, 1);
304}
305
306void
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
316void
317_sema_destroy(struct semaphore *s)
318{
319	mtx_destroy(&s->mtx);
320	cv_destroy(&s->cv);
321}
322
323void
324down(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
338int
339down_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
367int
368down_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
389void
390up(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 */
403void
404rlprintf(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
417void
418device_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
436void
437flush_signals(VCHIQ_THREAD_T thr)
438{
439	printf("Implement ME: %s\n", __func__);
440}
441
442int
443fatal_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
457static int thread_data_slot = 0;
458
459struct thread_data {
460	void *data;
461	int (*threadfn)(void *);
462};
463
464static struct thread_data thread_slots[MAX_THREAD_DATA_SLOTS];
465
466static void
467kthread_wrapper(void *data)
468{
469	struct thread_data *slot;
470
471	slot = data;
472	slot->threadfn(slot->data);
473}
474
475VCHIQ_THREAD_T
476vchiq_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
510void
511set_user_nice(VCHIQ_THREAD_T thr, int nice)
512{
513	/* NOOP */
514}
515
516void
517wake_up_process(VCHIQ_THREAD_T thr)
518{
519	/* NOOP */
520}
521
522void
523bcm_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