int led_task(void) { DCC_LOG1(LOG_TRACE, "[%d] started.", thinkos_thread_self()); while (1) { DCC_LOG(LOG_MSG, "thinkos_flag_wait()..."); thinkos_flag_wait(led_flag); if (led1_flash_tail != led1_flash_head) { led1_flash_tail++; if (!led_locked) led1_on(); } if (led2_flash_tail != led2_flash_head) { led2_flash_tail++; if (!led_locked) led2_on(); } if ((led1_flash_tail == led1_flash_head) && (led2_flash_tail == led2_flash_head)) thinkos_flag_clr(led_flag); thinkos_sleep(100); if (!led_locked) { led1_off(); led2_off(); } thinkos_sleep(100); } }
int jtag3ctrl_init(const void * rbf, int size) { int ret; thinkos_escalate(jtag3ctrl_bus_init, NULL); if ((ret = altera_configure(rbf, size)) < 0) { DCC_LOG1(LOG_ERROR, "altera_configure() failed: %d!", ret); INF(" # altera_configure() failed: %d!", ret); return ret; } else { INF("- FPGA configuration done (%d bytes)", ret); } /* Enable clock output */ stm32f_mco2_enable(); /* wait for the FPGA initialize */ thinkos_sleep(20); /* initial configuration */ reg_wr(REG_CFG, 0); return 0; }
int main(int argc, char ** argv) { struct board_cfg * cfg = (struct board_cfg *)(CFG_ADDR); uint8_t addr = 1; int i; if (cfg->magic == CFG_MAGIC) addr = cfg->mstp_addr; else addr = 0; stdio_init(); supervisor_init(); INF("Starting RS485 test"); net_start(addr); net_probe(true); for (i = 0;; ++i) { (void)i; thinkos_sleep(100); net_send(0, "U U U U ", 8); } return 0; }
int main(int argc, char ** argv) { usb_cdc_class_t * cdc; FILE * f_tty; FILE * f_raw; struct tty_dev * tty; int i; DCC_LOG_INIT(); DCC_LOG_CONNECT(); DCC_LOG(LOG_TRACE, "1. cm3_udelay_calibrate()"); cm3_udelay_calibrate(); DCC_LOG(LOG_TRACE, "2. thinkos_init()"); thinkos_init(THINKOS_OPT_PRIORITY(8) | THINKOS_OPT_ID(7)); DCC_LOG(LOG_TRACE, "usb_cdc_init()"); cdc = usb_cdc_init(&stm32f_otg_fs_dev, *((uint64_t *)STM32F_UID)); DCC_LOG(LOG_TRACE, "usb_cdc_fopen()"); f_raw = usb_cdc_fopen(cdc); tty = tty_attach(f_raw); f_tty = tty_fopen(tty); for (i = 0; ;i++) { // usb_xmodem_recv(cdc); thinkos_sleep(2000); fprintf(f_tty, "[%d] hello world...\n", i); } return 0; }
int http_server_task(struct httpd * httpd) { struct httpctl httpctl; struct httpctl * ctl = &httpctl; const struct httpdobj * obj; INF("Webserver started (thread %d).", thinkos_thread_self()); for (;;) { if (http_accept(httpd, ctl) < 0) { ERR("tcp_accept() failed!\n"); thinkos_sleep(1000); continue; } if ((obj = http_obj_lookup(ctl)) != NULL) { switch (ctl->method) { case HTTP_GET: DBG("HTTP GET \"%s\"", obj->oid); http_get(ctl, obj); break; case HTTP_POST: DBG("HTTP POST \"%s\"", obj->oid); http_post(ctl, obj); break; } } http_close(ctl); } return 0; }
static int __btn_task(void) { int st; printf("%s(): thread %d started.\n", __func__, thinkos_thread_self()); stm32_gpio_clock_en(STM32_GPIOA); stm32_gpio_mode(PUSH_BTN, INPUT, 0); btn_st = stm32_gpio_stat(PUSH_BTN) ? 1 : 0; for (;;) { thinkos_sleep(50); /* process push button */ st = stm32_gpio_stat(PUSH_BTN) ? 1 : 0; if (btn_st != st) { btn_st = st; thinkos_mutex_lock(btn_mutex); btn_event = st ? BTN_PRESSED : BTN_RELEASED; thinkos_mutex_unlock(btn_mutex); } } return 0; }
int main(int argc, char ** argv) { struct vcom vcom; int i = 0; DCC_LOG_CONNECT(); DCC_LOG_INIT(); thinkos_init(THINKOS_OPT_PRIORITY(0) | THINKOS_OPT_ID(0)); vcom.usb = usb_cdc_init(); vcom.serial = serial_init(stm32f_uart5); thinkos_thread_create((void *)usb_recv_task, (void *)&vcom, usb_recv_stack, STACK_SIZE, 0); thinkos_thread_create((void *)usb_ctrl_task, (void *)&vcom, usb_ctrl_stack, STACK_SIZE, 0); thinkos_thread_create((void *)serial_recv_task, (void *)&vcom, serial_recv_stack, STACK_SIZE, 0); thinkos_thread_create((void *)serial_ctrl_task, (void *)&vcom, serial_ctrl_stack, STACK_SIZE, 0); for (i = 0; ;i++) { thinkos_sleep(10000); } return 0; }
int consumer_task(void * arg) { int i = 0; printf(" %s(): [%d] started...\n", __func__, thinkos_thread_self()); thinkos_sleep(100); /* set the production enable flag to start production */ do { printf(" %3d ", i); /* wait for an item to be produced */ while (thinkos_sem_timedwait(sem_full, 50) == THINKOS_ETIMEDOUT) { printf("."); } /* unload the buffer */ printf(" %016llx %llu\n", buffer, buffer); i++; /* signal the empty buffer */ thinkos_sem_post(sem_empty); } while (!prod_done); /* get the last produced item, if any */ if (thinkos_sem_timedwait(sem_full, 0) == 0) { printf(" %3d ", i); printf(" %016llx %llu\n", buffer, buffer); i++; thinkos_sem_post(sem_empty); } return i; };
int main(int argc, char ** argv) { /* Calibrate the the uDelay loop */ cm3_udelay_calibrate(); /* Initialize the stdin, stdout and stderr */ stdio_init(); /* Print a useful information message */ printf("\n"); printf("---------------------------------------------------------\n"); printf(" ThinkOS - Semaphore example\n"); printf("---------------------------------------------------------\n"); printf("\n"); /* Initialize the ThinkOS kernel */ thinkos_init(THINKOS_OPT_PRIORITY(0) | THINKOS_OPT_ID(0)); /* Run the test */ semaphore_test(); thinkos_sleep(10000); return 0; }
static int accelerometer_task(struct acc_info * acc) { struct { int8_t x; uint8_t res1; int8_t y; uint8_t res2; int8_t z; } data; uint8_t cfg[4]; uint8_t st; int x = 0; int y = 0; int z = 0; int x_off = 0; int y_off = 0; int z_off = 0; printf("%s(): thread %d started.\n", __func__, thinkos_thread_self()); if (lis302_init() < 0) { return -1; } cfg[0] = CTRL_PD | CTRL_ZEN | CTRL_YEN | CTRL_XEN; cfg[1] = 0; cfg[3] = 0; lis302_wr(LIS302_CTRL_REG1, cfg, 3); for (; ;) { thinkos_sleep(1); /* poll the sensor */ lis302_rd(LIS302_STATUS_REG, &st, 1); if (st & STAT_ZYXDA) { /* get the forces data */ lis302_rd(LIS302_OUT_X, &data, 5); /* Filter */ x = (x * (AVG_N - 1) / AVG_N) + data.x; y = (y * (AVG_N - 1) / AVG_N) + data.y; z = (z * (AVG_N - 1) / AVG_N) + data.z; if (acc->cal_req) { x_off = -x; y_off = -y; z_off = -z; acc->cal_req = false; } acc->x = x_off + x; acc->y = y_off + y; acc->z = z_off + z; thinkos_sem_post(acc->sem); } } }
void __attribute__((noreturn)) net_recv_task(void) { void * pkt; int len; DCC_LOG1(LOG_TRACE, "thread=%d", thinkos_thread_self()); DBG("<%d> started...", thinkos_thread_self()); for (;;) { pkt = pktbuf_alloc(); if (pkt == NULL) { DCC_LOG(LOG_ERROR, "pktbuf_alloc() failed!"); DBG("pktbuf_alloc() failed!"); thinkos_sleep(1000); continue; } len = rs485_pkt_receive(&net.link, &pkt, pktbuf_len); if (len < 0) { DBG("rs485_pkt_receive() failed!"); thinkos_sleep(1000); continue; } if (len == 0) { DBG("rs485_pkt_receive() == 0!"); thinkos_sleep(1000); continue; } if (pkt != NULL) { if (net.probe_mode) net_probe_recv((char *)pkt, len); else if (net.pkt_mode) net_pkt_recv((struct net_pkt *)pkt, len); else net_recv((char *)pkt, len); pktbuf_free(pkt); } } }
/* ---------------------------------------------------------------------- * Supervisory task * ---------------------------------------------------------------------- */ int supervisor_task(void) { for (;;) { thinkos_sleep(200); led_on(0); led_on(1); thinkos_sleep(100); led_off(0); led_off(1); thinkos_sleep(400); led_on(2); led_on(3); thinkos_sleep(100); led_off(2); led_off(3); thinkos_sleep(400); } }
void __attribute__((noreturn)) watchdog_task(void) { unsigned int i; for (i = 0; ;++i) { thinkos_sleep(5000); DCC_LOG1(LOG_TRACE, "tick %d", i); } }
static void btn_init(void) { btn_mutex = thinkos_mutex_alloc(); printf("%s(): btn_mutex=%d.\n", __func__, btn_mutex); thinkos_thread_create((void *)__btn_task, (void *)NULL, btn_stack, sizeof(btn_stack) | THINKOS_OPT_PRIORITY(2)); thinkos_sleep(100); }
int main(int argc, char ** argv) { uint32_t * uid = STM32F_UID; int i = 0; DCC_LOG_INIT(); DCC_LOG_CONNECT(); /* calibrate usecond delay loop */ cm3_udelay_calibrate(); DCC_LOG(LOG_TRACE, "1. io_init()"); io_init(); DCC_LOG(LOG_TRACE, "2. leds_init()"); leds_init(); DCC_LOG(LOG_TRACE, "3. stdio_init()"); stdio_init(); printf("\n\n"); printf("-----------------------------------------\n"); printf(" RS485 token ring network test\n"); printf("-----------------------------------------\n"); printf("\n"); DCC_LOG(LOG_TRACE, "4. thinkos_init()"); thinkos_init(THINKOS_OPT_PRIORITY(0) | THINKOS_OPT_ID(32)); DCC_LOG(LOG_TRACE, "5. net_init()"); net_init(); thinkos_thread_create((void *)supervisor_task, (void *)NULL, supervisor_stack, sizeof(supervisor_stack), THINKOS_OPT_PRIORITY(0) | THINKOS_OPT_ID(0)); thinkos_thread_create((void *)tx_task, (void *)NULL, tx_stack, sizeof(tx_stack), THINKOS_OPT_PRIORITY(1) | THINKOS_OPT_ID(1)); thinkos_thread_create((void *)rx_task, (void *)NULL, rx_stack, sizeof(rx_stack), THINKOS_OPT_PRIORITY(2) | THINKOS_OPT_ID(2)); printf("UID=%08x:%08x:%08x\n", uid[2], uid[1], uid[0]); DCC_LOG3(LOG_TRACE, "UID=%08x:%08x:%08x", uid[2], uid[1], uid[0]); for (i = 0; ; ++i) { thinkos_sleep(1000); } return 0; }
int producer_task(void * arg) { uint64_t y; unsigned int i = 0; uint64_t x0 = 0; uint64_t x1 = 0; prod_done = false; printf(" %s(): [%d] started...\n", __func__, thinkos_thread_self()); thinkos_sleep(100); for (i = 0; i < prod_count; i++) { /* let's spend some time thinking */ thinkos_sleep(500); /* working */ if (i == 0) y = 0; else if (i == 1) y = 1; else y = x1 + x0; x0 = x1; x1 = y; /* waiting for room to insert a new item */ thinkos_sem_wait(sem_empty); /* insert the produced item in the buffer */ buffer = y; /* signal a full buffer */ thinkos_sem_post(sem_full); } prod_done = true; return i; }
int main(int argc, char ** argv) { int i; for (i = 0; ; ++i) { led_off((i - 2) & 0x7); led_on(i & 0x7); /* wait 100 ms */ thinkos_sleep(100); } return 0; }
void semaphore_test(void) { int producer_th; int consumer_th; int producer_ret; int consumer_ret; /* allocate the empty signal semaphores */ /* initialize the empty as 1 so we can insert an item immediately. */ sem_empty = thinkos_sem_alloc(1); /* allocate the full signal semaphores */ /* initialize the full as 0 as we don't have produced anything yet. */ sem_full = thinkos_sem_alloc(0); /* create the producer thread */ producer_th = thinkos_thread_create(producer_task, NULL, producer_stack, sizeof(producer_stack)); /* create the consuer thread */ consumer_th = thinkos_thread_create(consumer_task, NULL, consumer_stack, sizeof(consumer_stack)); printf(" * Empty semaphore: %d\n", sem_empty); printf(" * Full semaphore: %d\n", sem_full); printf(" * Producer thread: %d\n", producer_th); printf(" * Consumer thread: %d\n", consumer_th); printf("\n"); thinkos_sleep(100); /* number of items to be produced */ prod_count = 100; /* wait for the production thread to finish */ producer_ret = thinkos_join(producer_th); /* wait for the consumer thread to finish */ consumer_ret = thinkos_join(consumer_th); printf(" * Production return = %d\n", producer_ret); printf(" * Consumer return = %d\n", consumer_ret); /* release the semaphores */ thinkos_sem_free(sem_empty); thinkos_sem_free(sem_full); printf("\n"); };
int main(int argc, char ** argv) { struct board_cfg * cfg = (struct board_cfg *)(CFG_ADDR); struct mstp_lnk * mstp; int mstp_addr; if (cfg->magic == CFG_MAGIC) mstp_addr = cfg->mstp_addr; else mstp_addr = 2; io_init(); stdio_init(); motd(); lattice_ice40_configure(ice40lp384_bin, sizeof_ice40lp384_bin); supervisor_init(); INF("Starting MS/TP network (addr=%d)", mstp_addr); if ((mstp = mstp_start(mstp_addr)) == NULL) { thinkos_sleep(1000); return 1; } INF("Starting MS/TP test"); mstp_test_start(mstp); for (;;) { int c = fgetc(stdin); switch (c) { case '0' ... '9': test_mode = c - '0'; printf("\ntest mode %d\n", test_mode); break; default: show_menu(); } } return 0; }
void tlv320_reset(void) { uint8_t tlv[4]; tracef("%s()...", __func__); if (i2c_read(codec_addr, 0, tlv, 4) < 0) { tracef("%s(): i2c_read() failed!", __func__); return; } /* reset the device */ tlv320_wr(3, CR3_PWDN_NO | CR3_SWRS); /* wait at least 132MCLK ~ 12us (11.2896 MHz) */ thinkos_sleep(1); }
void tone_play(unsigned int tone, unsigned int ms) { uint8_t * wave; unsigned int len; if (tone > wave_max) tone = wave_max; wave = (uint8_t *)wave_lut[tone].buf; len = wave_lut[tone].len; wave_set(wave, len); wave_play(); /* FIXME: this should be handled by an interrupt or other task. This function should return immediately */ thinkos_sleep(ms); wave_pause(); }
static int __leds_task(void) { unsigned int tmr[LED_COUNT]; unsigned int i; unsigned int rate; unsigned int tm; printf("%s(): thread %d started.\n", __func__, thinkos_thread_self()); for (i = 0; i < LED_COUNT; ++i) tmr[i] = led_rate[i]; for (;;) { thinkos_mutex_lock(leds_mutex); for (i = 0; i < LED_COUNT; ++i) { if ((rate = led_rate[i]) == 0) { continue; } tm = MIN(tmr[i], rate); if (tm == 0) { tmr[i] = rate; if (led_state[i]) { __led_off(i); } else { __led_on(i); } } else { tmr[i] = tm - 1; } } thinkos_mutex_unlock(leds_mutex); thinkos_sleep(10); } return 0; }
void leds_init(void) { int i; __leds_io_init(); for (i = 0; i < LED_COUNT; ++i) led_rate[i] = 0; leds_mutex = thinkos_mutex_alloc(); printf("%s(): leds_mutex=%d.\n", __func__, leds_mutex); thinkos_thread_create((void *)__leds_task, (void *)NULL, leds_stack, sizeof(leds_stack) | THINKOS_OPT_PRIORITY(2)); thinkos_sleep(100); }
void __attribute__((noreturn)) monitor_task(void) { int opt; tracef("%s(): <%d> started...", __func__, thinkos_thread_self()); for (;;) { thinkos_sleep(250); if (monitor_auto_flush) opt = TRACE_FLUSH; else opt = 0; if (trace_fprint(monitor_stream, opt) < 0) { /* fall back to stdout */ monitor_stream = stdout; } } }
int cmd_isink(FILE * f, int argc, char ** argv) { unsigned int mode = 2; unsigned int rate = 1; unsigned int pre = 50; unsigned int pulse = 200; if (argc > 5) return SHELL_ERR_EXTRA_ARGS; if (argc > 1) { mode = strtoul(argv[1], NULL, 0); if (mode > 25) return SHELL_ERR_ARG_INVALID; if (argc > 2) { rate = strtoul(argv[2], NULL, 0); if (rate > 3) return SHELL_ERR_ARG_INVALID; if (argc > 3) { pulse = strtoul(argv[3], NULL, 0); if (argc > 4) pre = strtoul(argv[4], NULL, 0); } } } if (pulse < pre) pulse = pre; fprintf(f, "PW pulse: mode=%d rate=%d pulse=%dus pre=%dus ...\n", mode, rate, pulse, pre); isink_mode_set(mode | (rate << 5)); thinkos_sleep(2); isink_pulse(pre, pulse); return 0; }
int main(int argc, char ** argv) { cm3_udelay_calibrate(); stdout = stm32f_usart_open(STM32F_UART5, 115200, SERIAL_8N1); printf("\n"); printf("---------------------------------------------------------\n"); printf(" ThinkOS IRQ Test\n"); printf("---------------------------------------------------------\n"); printf("\n"); thinkos_init(THINKOS_OPT_PRIORITY(0) | THINKOS_OPT_ID(7)); io_init(); irq_test(); printf("---------------------------------------------------------\n"); thinkos_sleep(5000); return 0; }
void net_rcv_task(void) { sndbuf_t * buf; uint32_t ts; int n; tracef("%s(): <%d> started...", __func__, thinkos_thread_self()); for (;;) { while ((buf = sndbuf_alloc()) == NULL) { tracef("%s(): sndbuf_alloc() failed!", __func__); thinkos_sleep(1000); } #if ENABLE_G711 n = g711_alaw_recv(0, buf, &ts); #else n = audio_recv(0, buf, &ts); #endif if (n != sndbuf_len) { tracef("%s(): (n=%d != sndbuf_len)!", __func__, n); } else { if (audio_drv.stream_enabled) { #if !DISABLE_JITBUF jitbuf_enqueue(&audio_drv.jitbuf, buf, ts); #endif } } #if DISABLE_JITBUF xfr_buf = buf; #else sndbuf_free(buf); #endif } }
void * rs485_pkt_drain(struct rs485_link * lnk) { struct stm32_usart * uart = lnk->uart; void * pend_pkt = NULL; uint32_t sr; uint32_t cr; if (lnk->tx.pend_pkt) { /* wait for the DMA transfer to complete */ while (!lnk->tx.isr[TCIF_BIT]) { if (lnk->tx.isr[TEIF_BIT]) { lnk->tx.ifcr[TEIF_BIT] = 1; } thinkos_irq_wait(lnk->tx.dma_irq); } /* clear the the DMA trasfer complete flag */ lnk->tx.ifcr[TCIF_BIT] = 1; /* return a reference to the packet just transmitted */ pend_pkt = lnk->tx.pend_pkt; if ((sr = uart->sr) & USART_TC) { /* pulse the TE bit to generate an idle frame */ cr = uart->cr1; uart->cr1 = cr & ~USART_TE; uart->cr1 = cr | USART_TE; } else { thinkos_sleep(lnk->idle_tm); } } lnk->tx.pend_pkt = NULL; /* return previous pending packet */ return pend_pkt; }
void busy_test(void) { int th[4]; int x[4]; int y[4]; int d[4]; int sum; int ref; int ret; int i; printf("---------------------------------------------------------\n"); printf(" - Scheduler test\n"); for (i = 0; i < 4; i++) { dev[i].enabled = true; dev[i].cnt = 0; } th[0] = thinkos_thread_create(busy_task, (void *)&dev[0], stack[0], STACK_SIZE, THINKOS_OPT_PRIORITY(2) | THINKOS_OPT_ID(8)); th[1] = thinkos_thread_create(busy_task, (void *)&dev[1], stack[1], STACK_SIZE, THINKOS_OPT_PRIORITY(4) | THINKOS_OPT_ID(8)); th[2] = thinkos_thread_create(busy_task, (void *)&dev[2], stack[2], STACK_SIZE, THINKOS_OPT_PRIORITY(6) | THINKOS_OPT_ID(8)); th[3] = thinkos_thread_create(busy_task, (void *)&dev[3], stack[3], STACK_SIZE, THINKOS_OPT_PRIORITY(8) | THINKOS_OPT_ID(8)); printf(" * Threads: %d, %d, %d, %d\n", th[0], th[1], th[2], th[3]); for (i = 0; i < 8; i++) { x[0] = dev[0].cnt; x[1] = dev[1].cnt; x[2] = dev[2].cnt; x[3] = dev[3].cnt; thinkos_sleep(1000); y[0] = dev[0].cnt; y[1] = dev[1].cnt; y[2] = dev[2].cnt; y[3] = dev[3].cnt; d[0] = y[0] - x[0]; d[1] = y[1] - x[1]; d[2] = y[2] - x[2]; d[3] = y[3] - x[3]; sum = d[0] + d[1] + d[2] + d[3]; ref = 100 * d[3]; printf(" %8d + %8d + %8d + %8d = %8d (%d idle)\n", d[0], d[1], d[2], d[3], sum, thinkos_idle_val()); printf(" %d%% %d%% %d%% %d%%\n", ref / d[0], ref / d[1], ref / d[2], ref / d[3]); } for (i = 0; i < 4; i++) { dev[i].enabled = false; printf(" * join(%d) ...", th[i]); ret = thinkos_join(th[i]); printf(" %d\n", ret); } thinkos_sleep(1); printf("\n"); }
void sched_speed_test(void) { // uint32_t stack[4][STACK_SIZE / 4]; // volatile struct my_arg ctrl[4]; int th[4]; int x[4]; int y[4]; int d[4]; int ret; int i; printf("---------------------------------------------------------\n"); printf(" - Scheduler speed test\n"); th[0] = thinkos_thread_create(yield_task, (void *)&dev[0], stack[0], STACK_SIZE, THINKOS_OPT_PRIORITY(1) | THINKOS_OPT_ID(2) | THINKOS_OPT_PAUSED); th[1] = thinkos_thread_create(yield_task, (void *)&dev[1], stack[1], STACK_SIZE, THINKOS_OPT_PRIORITY(1) | THINKOS_OPT_ID(2) | THINKOS_OPT_PAUSED); th[2] = thinkos_thread_create(yield_task, (void *)&dev[2], stack[2], STACK_SIZE, THINKOS_OPT_PRIORITY(2) | THINKOS_OPT_ID(0) | THINKOS_OPT_PAUSED); th[3] = thinkos_thread_create(yield_task, (void *)&dev[3], stack[3], STACK_SIZE, THINKOS_OPT_PRIORITY(2) | THINKOS_OPT_ID(0) | THINKOS_OPT_PAUSED); printf(" * Threads: %d, %d, %d, %d\n", th[0], th[1], th[2], th[3]); /* The threads where paused on creation, start them */ for (i = 0; i < 4; i++) { thinkos_resume(th[i]); } for (i = 0; i < 5; i++) { x[0] = dev[0].cnt; x[1] = dev[1].cnt; x[2] = dev[2].cnt; x[3] = dev[3].cnt; thinkos_sleep(1000); y[0] = dev[0].cnt; y[1] = dev[1].cnt; y[2] = dev[2].cnt; y[3] = dev[3].cnt; d[0] = y[0] - x[0]; d[1] = y[1] - x[1]; d[2] = y[2] - x[2]; d[3] = y[3] - x[3]; printf(" %8d + %8d + %8d + %8d = %8d (%d idle)\n", d[0], d[1], d[2], d[3], d[0] + d[1] + d[2] + d[3], thinkos_idle_val()); } for (i = 0; i < 4; i++) { thinkos_cancel(th[i], i + 30); printf(" * join(%d) ...", th[i]); ret = thinkos_join(th[i]); printf(" %d\n", ret); } printf("\n"); };