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/stdarg.h>
40 #include <sys/syscallsubr.h>
41 #include <sys/systm.h>
42 #include <sys/taskqueue.h>
43
44 #include "mbox_if.h"
45
46 #include <interface/compat/vchi_bsd.h>
47
48 MALLOC_DEFINE(M_VCHI, "VCHI", "VCHI");
49
50 /*
51 * Timer API
52 */
53 static void
run_timer(void * arg)54 run_timer(void *arg)
55 {
56 struct timer_list *t = (struct timer_list *) arg;
57 void (*function)(unsigned long);
58
59 mtx_lock_spin(&t->mtx);
60 if (callout_pending(&t->callout)) {
61 /* callout was reset */
62 mtx_unlock_spin(&t->mtx);
63 return;
64 }
65 if (!callout_active(&t->callout)) {
66 /* callout was stopped */
67 mtx_unlock_spin(&t->mtx);
68 return;
69 }
70 callout_deactivate(&t->callout);
71
72 function = t->function;
73 mtx_unlock_spin(&t->mtx);
74
75 function(t->data);
76 }
77
78 void
vchiq_init_timer(struct timer_list * t)79 vchiq_init_timer(struct timer_list *t)
80 {
81 mtx_init(&t->mtx, "dahdi timer lock", NULL, MTX_SPIN);
82 callout_init(&t->callout, 1);
83 t->expires = 0;
84 /*
85 * function and data are not initialized intentionally:
86 * they are not initialized by Linux implementation too
87 */
88 }
89
90 void
vchiq_setup_timer(struct timer_list * t,void (* function)(unsigned long),unsigned long data)91 vchiq_setup_timer(struct timer_list *t, void (*function)(unsigned long), unsigned long data)
92 {
93 t->function = function;
94 t->data = data;
95 vchiq_init_timer(t);
96 }
97
98 void
vchiq_mod_timer(struct timer_list * t,unsigned long expires)99 vchiq_mod_timer(struct timer_list *t, unsigned long expires)
100 {
101 mtx_lock_spin(&t->mtx);
102 callout_reset(&t->callout, expires - jiffies, run_timer, t);
103 mtx_unlock_spin(&t->mtx);
104 }
105
106 void
vchiq_add_timer(struct timer_list * t)107 vchiq_add_timer(struct timer_list *t)
108 {
109 vchiq_mod_timer(t, t->expires);
110 }
111
112 int
vchiq_del_timer_sync(struct timer_list * t)113 vchiq_del_timer_sync(struct timer_list *t)
114 {
115 mtx_lock_spin(&t->mtx);
116 callout_stop(&t->callout);
117 mtx_unlock_spin(&t->mtx);
118
119 mtx_destroy(&t->mtx);
120 return 0;
121 }
122
123 int
vchiq_del_timer(struct timer_list * t)124 vchiq_del_timer(struct timer_list *t)
125 {
126 vchiq_del_timer_sync(t);
127 return 0;
128 }
129
130 /*
131 * Completion API
132 */
133 void
init_completion(struct completion * c)134 init_completion(struct completion *c)
135 {
136 cv_init(&c->cv, "VCHI completion cv");
137 mtx_init(&c->lock, "VCHI completion lock", "condvar", MTX_DEF);
138 c->done = 0;
139 }
140
141 void
destroy_completion(struct completion * c)142 destroy_completion(struct completion *c)
143 {
144 cv_destroy(&c->cv);
145 mtx_destroy(&c->lock);
146 }
147
148 void
complete(struct completion * c)149 complete(struct completion *c)
150 {
151 mtx_lock(&c->lock);
152
153 if (c->done >= 0) {
154 KASSERT(c->done < INT_MAX, ("c->done overflow")); /* XXX check */
155 c->done++;
156 cv_signal(&c->cv);
157 } else {
158 KASSERT(c->done == -1, ("Invalid value of c->done: %d", c->done));
159 }
160
161 mtx_unlock(&c->lock);
162 }
163
164 void
complete_all(struct completion * c)165 complete_all(struct completion *c)
166 {
167 mtx_lock(&c->lock);
168
169 if (c->done >= 0) {
170 KASSERT(c->done < INT_MAX, ("c->done overflow")); /* XXX check */
171 c->done = -1;
172 cv_broadcast(&c->cv);
173 } else {
174 KASSERT(c->done == -1, ("Invalid value of c->done: %d", c->done));
175 }
176
177 mtx_unlock(&c->lock);
178 }
179
180 void
INIT_COMPLETION_locked(struct completion * c)181 INIT_COMPLETION_locked(struct completion *c)
182 {
183 mtx_lock(&c->lock);
184
185 c->done = 0;
186
187 mtx_unlock(&c->lock);
188 }
189
190 static void
_completion_claim(struct completion * c)191 _completion_claim(struct completion *c)
192 {
193
194 KASSERT(mtx_owned(&c->lock),
195 ("_completion_claim should be called with acquired lock"));
196 KASSERT(c->done != 0, ("_completion_claim on non-waited completion"));
197 if (c->done > 0)
198 c->done--;
199 else
200 KASSERT(c->done == -1, ("Invalid value of c->done: %d", c->done));
201 }
202
203 void
wait_for_completion(struct completion * c)204 wait_for_completion(struct completion *c)
205 {
206 mtx_lock(&c->lock);
207 if (!c->done)
208 cv_wait(&c->cv, &c->lock);
209 c->done--;
210 mtx_unlock(&c->lock);
211 }
212
213 int
try_wait_for_completion(struct completion * c)214 try_wait_for_completion(struct completion *c)
215 {
216 int res = 0;
217
218 mtx_lock(&c->lock);
219 if (!c->done)
220 res = 1;
221 else
222 c->done--;
223 mtx_unlock(&c->lock);
224 return res == 0;
225 }
226
227 int
wait_for_completion_interruptible_timeout(struct completion * c,unsigned long timeout)228 wait_for_completion_interruptible_timeout(struct completion *c, unsigned long timeout)
229 {
230 int res = 0;
231 unsigned long start, now;
232 start = jiffies;
233
234 mtx_lock(&c->lock);
235 while (c->done == 0) {
236 res = cv_timedwait_sig(&c->cv, &c->lock, timeout);
237 if (res)
238 goto out;
239 now = jiffies;
240 if (timeout < (now - start)) {
241 res = EWOULDBLOCK;
242 goto out;
243 }
244
245 timeout -= (now - start);
246 start = now;
247 }
248
249 _completion_claim(c);
250 res = 0;
251
252 out:
253 mtx_unlock(&c->lock);
254
255 if (res == EWOULDBLOCK) {
256 return 0;
257 } else if ((res == EINTR) || (res == ERESTART)) {
258 return -ERESTART;
259 } else {
260 KASSERT((res == 0), ("res = %d", res));
261 return timeout;
262 }
263 }
264
265 int
wait_for_completion_interruptible(struct completion * c)266 wait_for_completion_interruptible(struct completion *c)
267 {
268 int res = 0;
269
270 mtx_lock(&c->lock);
271 while (c->done == 0) {
272 res = cv_wait_sig(&c->cv, &c->lock);
273 if (res)
274 goto out;
275 }
276
277 _completion_claim(c);
278
279 out:
280 mtx_unlock(&c->lock);
281
282 if ((res == EINTR) || (res == ERESTART))
283 res = -ERESTART;
284 return res;
285 }
286
287 int
wait_for_completion_killable(struct completion * c)288 wait_for_completion_killable(struct completion *c)
289 {
290
291 return wait_for_completion_interruptible(c);
292 }
293
294 /*
295 * Semaphore API
296 */
297
sema_sysinit(void * arg)298 void sema_sysinit(void *arg)
299 {
300 struct semaphore *s = arg;
301
302 _sema_init(s, 1);
303 }
304
305 void
_sema_init(struct semaphore * s,int value)306 _sema_init(struct semaphore *s, int value)
307 {
308 bzero(s, sizeof(*s));
309 mtx_init(&s->mtx, "sema lock", "VCHIQ sepmaphore backing lock",
310 MTX_DEF | MTX_NOWITNESS | MTX_QUIET);
311 cv_init(&s->cv, "sema cv");
312 s->value = value;
313 }
314
315 void
_sema_destroy(struct semaphore * s)316 _sema_destroy(struct semaphore *s)
317 {
318 mtx_destroy(&s->mtx);
319 cv_destroy(&s->cv);
320 }
321
322 void
down(struct semaphore * s)323 down(struct semaphore *s)
324 {
325
326 mtx_lock(&s->mtx);
327 while (s->value == 0) {
328 s->waiters++;
329 cv_wait(&s->cv, &s->mtx);
330 s->waiters--;
331 }
332
333 s->value--;
334 mtx_unlock(&s->mtx);
335 }
336
337 int
down_interruptible(struct semaphore * s)338 down_interruptible(struct semaphore *s)
339 {
340 int ret ;
341
342 ret = 0;
343 mtx_lock(&s->mtx);
344
345 while (s->value == 0) {
346 s->waiters++;
347 ret = cv_wait_sig(&s->cv, &s->mtx);
348 s->waiters--;
349
350 /* XXXMDC As per its semaphore.c, linux can only return EINTR */
351 if (ret) {
352 mtx_unlock(&s->mtx);
353 return -EINTR;
354 }
355 }
356
357 s->value--;
358 mtx_unlock(&s->mtx);
359
360 return (0);
361 }
362
363 int
down_trylock(struct semaphore * s)364 down_trylock(struct semaphore *s)
365 {
366 int ret;
367
368 ret = 0;
369
370 mtx_lock(&s->mtx);
371
372 if (s->value > 0) {
373 /* Success. */
374 s->value--;
375 ret = 0;
376 } else {
377 ret = -EAGAIN;
378 }
379
380 mtx_unlock(&s->mtx);
381
382 return (ret);
383 }
384
385 void
up(struct semaphore * s)386 up(struct semaphore *s)
387 {
388 mtx_lock(&s->mtx);
389 s->value++;
390 if (s->waiters && s->value > 0)
391 cv_signal(&s->cv);
392
393 mtx_unlock(&s->mtx);
394 }
395
396 /*
397 * Logging API
398 */
399 void
rlprintf(int pps,const char * fmt,...)400 rlprintf(int pps, const char *fmt, ...)
401 {
402 va_list ap;
403 static struct timeval last_printf;
404 static int count;
405
406 if (ppsratecheck(&last_printf, &count, pps)) {
407 va_start(ap, fmt);
408 vprintf(fmt, ap);
409 va_end(ap);
410 }
411 }
412
413 void
device_rlprintf(int pps,device_t dev,const char * fmt,...)414 device_rlprintf(int pps, device_t dev, const char *fmt, ...)
415 {
416 va_list ap;
417 static struct timeval last_printf;
418 static int count;
419
420 if (ppsratecheck(&last_printf, &count, pps)) {
421 va_start(ap, fmt);
422 device_print_prettyname(dev);
423 vprintf(fmt, ap);
424 va_end(ap);
425 }
426 }
427
428 /*
429 * Signals API
430 */
431
432 void
flush_signals(VCHIQ_THREAD_T thr)433 flush_signals(VCHIQ_THREAD_T thr)
434 {
435 printf("Implement ME: %s\n", __func__);
436 }
437
438 int
fatal_signal_pending(VCHIQ_THREAD_T thr)439 fatal_signal_pending(VCHIQ_THREAD_T thr)
440 {
441 return (curproc_sigkilled());
442 }
443
444 /*
445 * kthread API
446 */
447
448 /*
449 * This is a hack to avoid memory leak
450 */
451 #define MAX_THREAD_DATA_SLOTS 32
452 static int thread_data_slot = 0;
453
454 struct thread_data {
455 void *data;
456 int (*threadfn)(void *);
457 };
458
459 static struct thread_data thread_slots[MAX_THREAD_DATA_SLOTS];
460
461 static void
kthread_wrapper(void * data)462 kthread_wrapper(void *data)
463 {
464 struct thread_data *slot;
465
466 slot = data;
467 slot->threadfn(slot->data);
468 }
469
470 VCHIQ_THREAD_T
vchiq_thread_create(int (* threadfn)(void * data),void * data,const char namefmt[],...)471 vchiq_thread_create(int (*threadfn)(void *data),
472 void *data,
473 const char namefmt[], ...)
474 {
475 VCHIQ_THREAD_T newp;
476 va_list ap;
477 char name[MAXCOMLEN+1];
478 struct thread_data *slot;
479
480 if (thread_data_slot >= MAX_THREAD_DATA_SLOTS) {
481 printf("kthread_create: out of thread data slots\n");
482 return (NULL);
483 }
484
485 slot = &thread_slots[thread_data_slot];
486 slot->data = data;
487 slot->threadfn = threadfn;
488
489 va_start(ap, namefmt);
490 vsnprintf(name, sizeof(name), namefmt, ap);
491 va_end(ap);
492
493 newp = NULL;
494 if (kproc_create(kthread_wrapper, (void*)slot, &newp, 0, 0,
495 "%s", name) != 0) {
496 /* Just to be sure */
497 newp = NULL;
498 }
499 else
500 thread_data_slot++;
501
502 return newp;
503 }
504
505 void
set_user_nice(VCHIQ_THREAD_T thr,int nice)506 set_user_nice(VCHIQ_THREAD_T thr, int nice)
507 {
508 /* NOOP */
509 }
510
511 void
wake_up_process(VCHIQ_THREAD_T thr)512 wake_up_process(VCHIQ_THREAD_T thr)
513 {
514 /* NOOP */
515 }
516
517 void
bcm_mbox_write(int channel,uint32_t data)518 bcm_mbox_write(int channel, uint32_t data)
519 {
520 device_t mbox;
521
522 mbox = devclass_get_device(devclass_find("mbox"), 0);
523
524 if (mbox)
525 MBOX_WRITE(mbox, channel, data);
526 }
527