#include "dwc_os.h" #include "dwc_list.h" #ifdef DWC_CCLIB # include "dwc_cc.h" #endif #ifdef DWC_CRYPTOLIB # include "dwc_modpow.h" # include "dwc_dh.h" # include "dwc_crypto.h" #endif #ifdef DWC_NOTIFYLIB # include "dwc_notifier.h" #endif /* OS-Level Implementations */ /* This is the NetBSD 4.0.1 kernel implementation of the DWC platform library. */ /* MISC */ void *DWC_MEMSET(void *dest, uint8_t byte, uint32_t size) { return memset(dest, byte, size); } void *DWC_MEMCPY(void *dest, void const *src, uint32_t size) { return memcpy(dest, src, size); } void *DWC_MEMMOVE(void *dest, void *src, uint32_t size) { bcopy(src, dest, size); return dest; } int DWC_MEMCMP(void *m1, void *m2, uint32_t size) { return memcmp(m1, m2, size); } int DWC_STRNCMP(void *s1, void *s2, uint32_t size) { return strncmp(s1, s2, size); } int DWC_STRCMP(void *s1, void *s2) { return strcmp(s1, s2); } int DWC_STRLEN(char const *str) { return strlen(str); } char *DWC_STRCPY(char *to, char const *from) { return strcpy(to, from); } char *DWC_STRDUP(char const *str) { int len = DWC_STRLEN(str) + 1; char *new = DWC_ALLOC_ATOMIC(len); if (!new) { return NULL; } DWC_MEMCPY(new, str, len); return new; } int DWC_ATOI(char *str, int32_t *value) { char *end = NULL; /* NetBSD doesn't have 'strtol' in the kernel, but 'strtoul' * should be equivalent on 2's complement machines */ *value = strtoul(str, &end, 0); if (*end == '\0') { return 0; } return -1; } int DWC_ATOUI(char *str, uint32_t *value) { char *end = NULL; *value = strtoul(str, &end, 0); if (*end == '\0') { return 0; } return -1; } #ifdef DWC_UTFLIB /* From usbstring.c */ int DWC_UTF8_TO_UTF16LE(uint8_t const *s, uint16_t *cp, unsigned len) { int count = 0; u8 c; u16 uchar; /* this insists on correct encodings, though not minimal ones. * BUT it currently rejects legit 4-byte UTF-8 code points, * which need surrogate pairs. (Unicode 3.1 can use them.) */ while (len != 0 && (c = (u8) *s++) != 0) { if (unlikely(c & 0x80)) { // 2-byte sequence: // 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx if ((c & 0xe0) == 0xc0) { uchar = (c & 0x1f) << 6; c = (u8) *s++; if ((c & 0xc0) != 0xc0) goto fail; c &= 0x3f; uchar |= c; // 3-byte sequence (most CJKV characters): // zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx } else if ((c & 0xf0) == 0xe0) { uchar = (c & 0x0f) << 12; c = (u8) *s++; if ((c & 0xc0) != 0xc0) goto fail; c &= 0x3f; uchar |= c << 6; c = (u8) *s++; if ((c & 0xc0) != 0xc0) goto fail; c &= 0x3f; uchar |= c; /* no bogus surrogates */ if (0xd800 <= uchar && uchar <= 0xdfff) goto fail; // 4-byte sequence (surrogate pairs, currently rare): // 11101110wwwwzzzzyy + 110111yyyyxxxxxx // = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx // (uuuuu = wwww + 1) // FIXME accept the surrogate code points (only) } else goto fail; } else uchar = c; put_unaligned (cpu_to_le16 (uchar), cp++); count++; len--; } return count; fail: return -1; } #endif /* DWC_UTFLIB */ /* dwc_debug.h */ dwc_bool_t DWC_IN_IRQ(void) { // return in_irq(); return 0; } dwc_bool_t DWC_IN_BH(void) { // return in_softirq(); return 0; } void DWC_VPRINTF(char *format, va_list args) { vprintf(format, args); } int DWC_VSNPRINTF(char *str, int size, char *format, va_list args) { return vsnprintf(str, size, format, args); } void DWC_PRINTF(char *format, ...) { va_list args; va_start(args, format); DWC_VPRINTF(format, args); va_end(args); } int DWC_SPRINTF(char *buffer, char *format, ...) { int retval; va_list args; va_start(args, format); retval = vsprintf(buffer, format, args); va_end(args); return retval; } int DWC_SNPRINTF(char *buffer, int size, char *format, ...) { int retval; va_list args; va_start(args, format); retval = vsnprintf(buffer, size, format, args); va_end(args); return retval; } void __DWC_WARN(char *format, ...) { va_list args; va_start(args, format); DWC_VPRINTF(format, args); va_end(args); } void __DWC_ERROR(char *format, ...) { va_list args; va_start(args, format); DWC_VPRINTF(format, args); va_end(args); } void DWC_EXCEPTION(char *format, ...) { va_list args; va_start(args, format); DWC_VPRINTF(format, args); va_end(args); // BUG_ON(1); ??? } #ifdef DEBUG void __DWC_DEBUG(char *format, ...) { va_list args; va_start(args, format); DWC_VPRINTF(format, args); va_end(args); } #endif /* dwc_mem.h */ #if 0 dwc_pool_t *DWC_DMA_POOL_CREATE(uint32_t size, uint32_t align, uint32_t alloc) { struct dma_pool *pool = dma_pool_create("Pool", NULL, size, align, alloc); return (dwc_pool_t *)pool; } void DWC_DMA_POOL_DESTROY(dwc_pool_t *pool) { dma_pool_destroy((struct dma_pool *)pool); } void *DWC_DMA_POOL_ALLOC(dwc_pool_t *pool, uint64_t *dma_addr) { // return dma_pool_alloc((struct dma_pool *)pool, GFP_KERNEL, dma_addr); return dma_pool_alloc((struct dma_pool *)pool, M_WAITOK, dma_addr); } void *DWC_DMA_POOL_ZALLOC(dwc_pool_t *pool, uint64_t *dma_addr) { void *vaddr = DWC_DMA_POOL_ALLOC(pool, dma_addr); memset(..); } void DWC_DMA_POOL_FREE(dwc_pool_t *pool, void *vaddr, void *daddr) { dma_pool_free(pool, vaddr, daddr); } #endif void *__DWC_DMA_ALLOC(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr) { dwc_dmactx_t *dma = (dwc_dmactx_t *)dma_ctx; int error; error = bus_dmamem_alloc(dma->dma_tag, size, 1, size, dma->segs, sizeof(dma->segs) / sizeof(dma->segs[0]), &dma->nsegs, BUS_DMA_NOWAIT); if (error) { printf("%s: bus_dmamem_alloc(%ju) failed: %d\n", __func__, (uintmax_t)size, error); goto fail_0; } error = bus_dmamem_map(dma->dma_tag, dma->segs, dma->nsegs, size, (caddr_t *)&dma->dma_vaddr, BUS_DMA_NOWAIT | BUS_DMA_COHERENT); if (error) { printf("%s: bus_dmamem_map failed: %d\n", __func__, error); goto fail_1; } error = bus_dmamap_create(dma->dma_tag, size, 1, size, 0, BUS_DMA_NOWAIT, &dma->dma_map); if (error) { printf("%s: bus_dmamap_create failed: %d\n", __func__, error); goto fail_2; } error = bus_dmamap_load(dma->dma_tag, dma->dma_map, dma->dma_vaddr, size, NULL, BUS_DMA_NOWAIT); if (error) { printf("%s: bus_dmamap_load failed: %d\n", __func__, error); goto fail_3; } dma->dma_paddr = (bus_addr_t)dma->segs[0].ds_addr; *dma_addr = dma->dma_paddr; return dma->dma_vaddr; fail_3: bus_dmamap_destroy(dma->dma_tag, dma->dma_map); fail_2: bus_dmamem_unmap(dma->dma_tag, dma->dma_vaddr, size); fail_1: bus_dmamem_free(dma->dma_tag, dma->segs, dma->nsegs); fail_0: dma->dma_map = NULL; dma->dma_vaddr = NULL; dma->nsegs = 0; return NULL; } void __DWC_DMA_FREE(void *dma_ctx, uint32_t size, void *virt_addr, dwc_dma_t dma_addr) { dwc_dmactx_t *dma = (dwc_dmactx_t *)dma_ctx; if (dma->dma_map != NULL) { bus_dmamap_sync(dma->dma_tag, dma->dma_map, 0, size, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(dma->dma_tag, dma->dma_map); bus_dmamap_destroy(dma->dma_tag, dma->dma_map); bus_dmamem_unmap(dma->dma_tag, dma->dma_vaddr, size); bus_dmamem_free(dma->dma_tag, dma->segs, dma->nsegs); dma->dma_paddr = 0; dma->dma_map = NULL; dma->dma_vaddr = NULL; dma->nsegs = 0; } } void *__DWC_ALLOC(void *mem_ctx, uint32_t size) { return malloc(size, M_DEVBUF, M_WAITOK | M_ZERO); } void *__DWC_ALLOC_ATOMIC(void *mem_ctx, uint32_t size) { return malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO); } void __DWC_FREE(void *mem_ctx, void *addr) { free(addr, M_DEVBUF); } #ifdef DWC_CRYPTOLIB /* dwc_crypto.h */ void DWC_RANDOM_BYTES(uint8_t *buffer, uint32_t length) { get_random_bytes(buffer, length); } int DWC_AES_CBC(uint8_t *message, uint32_t messagelen, uint8_t *key, uint32_t keylen, uint8_t iv[16], uint8_t *out) { struct crypto_blkcipher *tfm; struct blkcipher_desc desc; struct scatterlist sgd; struct scatterlist sgs; tfm = crypto_alloc_blkcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC); if (tfm == NULL) { printk("failed to load transform for aes CBC\n"); return -1; } crypto_blkcipher_setkey(tfm, key, keylen); crypto_blkcipher_set_iv(tfm, iv, 16); sg_init_one(&sgd, out, messagelen); sg_init_one(&sgs, message, messagelen); desc.tfm = tfm; desc.flags = 0; if (crypto_blkcipher_encrypt(&desc, &sgd, &sgs, messagelen)) { crypto_free_blkcipher(tfm); DWC_ERROR("AES CBC encryption failed"); return -1; } crypto_free_blkcipher(tfm); return 0; } int DWC_SHA256(uint8_t *message, uint32_t len, uint8_t *out) { struct crypto_hash *tfm; struct hash_desc desc; struct scatterlist sg; tfm = crypto_alloc_hash("sha256", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(tfm)) { DWC_ERROR("Failed to load transform for sha256: %ld", PTR_ERR(tfm)); return 0; } desc.tfm = tfm; desc.flags = 0; sg_init_one(&sg, message, len); crypto_hash_digest(&desc, &sg, len, out); crypto_free_hash(tfm); return 1; } int DWC_HMAC_SHA256(uint8_t *message, uint32_t messagelen, uint8_t *key, uint32_t keylen, uint8_t *out) { struct crypto_hash *tfm; struct hash_desc desc; struct scatterlist sg; tfm = crypto_alloc_hash("hmac(sha256)", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(tfm)) { DWC_ERROR("Failed to load transform for hmac(sha256): %ld", PTR_ERR(tfm)); return 0; } desc.tfm = tfm; desc.flags = 0; sg_init_one(&sg, message, messagelen); crypto_hash_setkey(tfm, key, keylen); crypto_hash_digest(&desc, &sg, messagelen, out); crypto_free_hash(tfm); return 1; } #endif /* DWC_CRYPTOLIB */ /* Byte Ordering Conversions */ uint32_t DWC_CPU_TO_LE32(uint32_t *p) { #ifdef __LITTLE_ENDIAN return *p; #else uint8_t *u_p = (uint8_t *)p; return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24)); #endif } uint32_t DWC_CPU_TO_BE32(uint32_t *p) { #ifdef __BIG_ENDIAN return *p; #else uint8_t *u_p = (uint8_t *)p; return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24)); #endif } uint32_t DWC_LE32_TO_CPU(uint32_t *p) { #ifdef __LITTLE_ENDIAN return *p; #else uint8_t *u_p = (uint8_t *)p; return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24)); #endif } uint32_t DWC_BE32_TO_CPU(uint32_t *p) { #ifdef __BIG_ENDIAN return *p; #else uint8_t *u_p = (uint8_t *)p; return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24)); #endif } uint16_t DWC_CPU_TO_LE16(uint16_t *p) { #ifdef __LITTLE_ENDIAN return *p; #else uint8_t *u_p = (uint8_t *)p; return (u_p[1] | (u_p[0] << 8)); #endif } uint16_t DWC_CPU_TO_BE16(uint16_t *p) { #ifdef __BIG_ENDIAN return *p; #else uint8_t *u_p = (uint8_t *)p; return (u_p[1] | (u_p[0] << 8)); #endif } uint16_t DWC_LE16_TO_CPU(uint16_t *p) { #ifdef __LITTLE_ENDIAN return *p; #else uint8_t *u_p = (uint8_t *)p; return (u_p[1] | (u_p[0] << 8)); #endif } uint16_t DWC_BE16_TO_CPU(uint16_t *p) { #ifdef __BIG_ENDIAN return *p; #else uint8_t *u_p = (uint8_t *)p; return (u_p[1] | (u_p[0] << 8)); #endif } /* Registers */ uint32_t DWC_READ_REG32(void *io_ctx, uint32_t volatile *reg) { dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx; bus_size_t ior = (bus_size_t)reg; return bus_space_read_4(io->iot, io->ioh, ior); } #if 0 uint64_t DWC_READ_REG64(void *io_ctx, uint64_t volatile *reg) { dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx; bus_size_t ior = (bus_size_t)reg; return bus_space_read_8(io->iot, io->ioh, ior); } #endif void DWC_WRITE_REG32(void *io_ctx, uint32_t volatile *reg, uint32_t value) { dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx; bus_size_t ior = (bus_size_t)reg; bus_space_write_4(io->iot, io->ioh, ior, value); } #if 0 void DWC_WRITE_REG64(void *io_ctx, uint64_t volatile *reg, uint64_t value) { dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx; bus_size_t ior = (bus_size_t)reg; bus_space_write_8(io->iot, io->ioh, ior, value); } #endif void DWC_MODIFY_REG32(void *io_ctx, uint32_t volatile *reg, uint32_t clear_mask, uint32_t set_mask) { dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx; bus_size_t ior = (bus_size_t)reg; bus_space_write_4(io->iot, io->ioh, ior, (bus_space_read_4(io->iot, io->ioh, ior) & ~clear_mask) | set_mask); } #if 0 void DWC_MODIFY_REG64(void *io_ctx, uint64_t volatile *reg, uint64_t clear_mask, uint64_t set_mask) { dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx; bus_size_t ior = (bus_size_t)reg; bus_space_write_8(io->iot, io->ioh, ior, (bus_space_read_8(io->iot, io->ioh, ior) & ~clear_mask) | set_mask); } #endif /* Locking */ dwc_spinlock_t *DWC_SPINLOCK_ALLOC(void) { struct simplelock *sl = DWC_ALLOC(sizeof(*sl)); if (!sl) { DWC_ERROR("Cannot allocate memory for spinlock"); return NULL; } simple_lock_init(sl); return (dwc_spinlock_t *)sl; } void DWC_SPINLOCK_FREE(dwc_spinlock_t *lock) { struct simplelock *sl = (struct simplelock *)lock; DWC_FREE(sl); } void DWC_SPINLOCK(dwc_spinlock_t *lock) { simple_lock((struct simplelock *)lock); } void DWC_SPINUNLOCK(dwc_spinlock_t *lock) { simple_unlock((struct simplelock *)lock); } void DWC_SPINLOCK_IRQSAVE(dwc_spinlock_t *lock, dwc_irqflags_t *flags) { simple_lock((struct simplelock *)lock); *flags = splbio(); } void DWC_SPINUNLOCK_IRQRESTORE(dwc_spinlock_t *lock, dwc_irqflags_t flags) { splx(flags); simple_unlock((struct simplelock *)lock); } dwc_mutex_t *DWC_MUTEX_ALLOC(void) { dwc_mutex_t *mutex = DWC_ALLOC(sizeof(struct lock)); if (!mutex) { DWC_ERROR("Cannot allocate memory for mutex"); return NULL; } lockinit((struct lock *)mutex, 0, "dw3mtx", 0, 0); return mutex; } #if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES)) #else void DWC_MUTEX_FREE(dwc_mutex_t *mutex) { DWC_FREE(mutex); } #endif void DWC_MUTEX_LOCK(dwc_mutex_t *mutex) { lockmgr((struct lock *)mutex, LK_EXCLUSIVE, NULL); } int DWC_MUTEX_TRYLOCK(dwc_mutex_t *mutex) { int status; status = lockmgr((struct lock *)mutex, LK_EXCLUSIVE | LK_NOWAIT, NULL); return status == 0; } void DWC_MUTEX_UNLOCK(dwc_mutex_t *mutex) { lockmgr((struct lock *)mutex, LK_RELEASE, NULL); } /* Timing */ void DWC_UDELAY(uint32_t usecs) { DELAY(usecs); } void DWC_MDELAY(uint32_t msecs) { do { DELAY(1000); } while (--msecs); } void DWC_MSLEEP(uint32_t msecs) { struct timeval tv; tv.tv_sec = msecs / 1000; tv.tv_usec = (msecs - tv.tv_sec * 1000) * 1000; tsleep(&tv, 0, "dw3slp", tvtohz(&tv)); } uint32_t DWC_TIME(void) { struct timeval tv; microuptime(&tv); // or getmicrouptime? (less precise, but faster) return tv.tv_sec * 1000 + tv.tv_usec / 1000; } /* Timers */ struct dwc_timer { struct callout t; char *name; dwc_spinlock_t *lock; dwc_timer_callback_t cb; void *data; }; dwc_timer_t *DWC_TIMER_ALLOC(char *name, dwc_timer_callback_t cb, void *data) { dwc_timer_t *t = DWC_ALLOC(sizeof(*t)); if (!t) { DWC_ERROR("Cannot allocate memory for timer"); return NULL; } callout_init(&t->t); t->name = DWC_STRDUP(name); if (!t->name) { DWC_ERROR("Cannot allocate memory for timer->name"); goto no_name; } t->lock = DWC_SPINLOCK_ALLOC(); if (!t->lock) { DWC_ERROR("Cannot allocate memory for timer->lock"); goto no_lock; } t->cb = cb; t->data = data; return t; no_lock: DWC_FREE(t->name); no_name: DWC_FREE(t); return NULL; } void DWC_TIMER_FREE(dwc_timer_t *timer) { callout_stop(&timer->t); DWC_SPINLOCK_FREE(timer->lock); DWC_FREE(timer->name); DWC_FREE(timer); } void DWC_TIMER_SCHEDULE(dwc_timer_t *timer, uint32_t time) { struct timeval tv; tv.tv_sec = time / 1000; tv.tv_usec = (time - tv.tv_sec * 1000) * 1000; callout_reset(&timer->t, tvtohz(&tv), timer->cb, timer->data); } void DWC_TIMER_CANCEL(dwc_timer_t *timer) { callout_stop(&timer->t); } /* Wait Queues */ struct dwc_waitq { struct simplelock lock; int abort; }; dwc_waitq_t *DWC_WAITQ_ALLOC(void) { dwc_waitq_t *wq = DWC_ALLOC(sizeof(*wq)); if (!wq) { DWC_ERROR("Cannot allocate memory for waitqueue"); return NULL; } simple_lock_init(&wq->lock); wq->abort = 0; return wq; } void DWC_WAITQ_FREE(dwc_waitq_t *wq) { DWC_FREE(wq); } int32_t DWC_WAITQ_WAIT(dwc_waitq_t *wq, dwc_waitq_condition_t cond, void *data) { int ipl; int result = 0; simple_lock(&wq->lock); ipl = splbio(); /* Skip the sleep if already aborted or triggered */ if (!wq->abort && !cond(data)) { splx(ipl); result = ltsleep(wq, PCATCH, "dw3wat", 0, &wq->lock); // infinite timeout ipl = splbio(); } if (result == 0) { // awoken if (wq->abort) { wq->abort = 0; result = -DWC_E_ABORT; } else { result = 0; } splx(ipl); simple_unlock(&wq->lock); } else { wq->abort = 0; splx(ipl); simple_unlock(&wq->lock); if (result == ERESTART) { // signaled - restart result = -DWC_E_RESTART; } else { // signaled - must be EINTR result = -DWC_E_ABORT; } } return result; } int32_t DWC_WAITQ_WAIT_TIMEOUT(dwc_waitq_t *wq, dwc_waitq_condition_t cond, void *data, int32_t msecs) { struct timeval tv, tv1, tv2; int ipl; int result = 0; tv.tv_sec = msecs / 1000; tv.tv_usec = (msecs - tv.tv_sec * 1000) * 1000; simple_lock(&wq->lock); ipl = splbio(); /* Skip the sleep if already aborted or triggered */ if (!wq->abort && !cond(data)) { splx(ipl); getmicrouptime(&tv1); result = ltsleep(wq, PCATCH, "dw3wto", tvtohz(&tv), &wq->lock); getmicrouptime(&tv2); ipl = splbio(); } if (result == 0) { // awoken if (wq->abort) { wq->abort = 0; splx(ipl); simple_unlock(&wq->lock); result = -DWC_E_ABORT; } else { splx(ipl); simple_unlock(&wq->lock); tv2.tv_usec -= tv1.tv_usec; if (tv2.tv_usec < 0) { tv2.tv_usec += 1000000; tv2.tv_sec--; } tv2.tv_sec -= tv1.tv_sec; result = tv2.tv_sec * 1000 + tv2.tv_usec / 1000; result = msecs - result; if (result <= 0) result = 1; } } else { wq->abort = 0; splx(ipl); simple_unlock(&wq->lock); if (result == ERESTART) { // signaled - restart result = -DWC_E_RESTART; } else if (result == EINTR) { // signaled - interrupt result = -DWC_E_ABORT; } else { // timed out result = -DWC_E_TIMEOUT; } } return result; } void DWC_WAITQ_TRIGGER(dwc_waitq_t *wq) { wakeup(wq); } void DWC_WAITQ_ABORT(dwc_waitq_t *wq) { int ipl; simple_lock(&wq->lock); ipl = splbio(); wq->abort = 1; wakeup(wq); splx(ipl); simple_unlock(&wq->lock); } /* Threading */ struct dwc_thread { struct proc *proc; int abort; }; dwc_thread_t *DWC_THREAD_RUN(dwc_thread_function_t func, char *name, void *data) { int retval; dwc_thread_t *thread = DWC_ALLOC(sizeof(*thread)); if (!thread) { return NULL; } thread->abort = 0; retval = kthread_create1((void (*)(void *))func, data, &thread->proc, "%s", name); if (retval) { DWC_FREE(thread); return NULL; } return thread; } int DWC_THREAD_STOP(dwc_thread_t *thread) { int retval; thread->abort = 1; retval = tsleep(&thread->abort, 0, "dw3stp", 60 * hz); if (retval == 0) { /* DWC_THREAD_EXIT() will free the thread struct */ return 0; } /* NOTE: We leak the thread struct if thread doesn't die */ if (retval == EWOULDBLOCK) { return -DWC_E_TIMEOUT; } return -DWC_E_UNKNOWN; } dwc_bool_t DWC_THREAD_SHOULD_STOP(dwc_thread_t *thread) { return thread->abort; } void DWC_THREAD_EXIT(dwc_thread_t *thread) { wakeup(&thread->abort); DWC_FREE(thread); kthread_exit(0); } /* tasklets - Runs in interrupt context (cannot sleep) - Each tasklet runs on a single CPU - Different tasklets can be running simultaneously on different CPUs [ On NetBSD there is no corresponding mechanism, drivers don't have bottom- halves. So we just call the callback directly from DWC_TASK_SCHEDULE() ] */ struct dwc_tasklet { dwc_tasklet_callback_t cb; void *data; }; static void tasklet_callback(void *data) { dwc_tasklet_t *task = (dwc_tasklet_t *)data; task->cb(task->data); } dwc_tasklet_t *DWC_TASK_ALLOC(char *name, dwc_tasklet_callback_t cb, void *data) { dwc_tasklet_t *task = DWC_ALLOC(sizeof(*task)); if (task) { task->cb = cb; task->data = data; } else { DWC_ERROR("Cannot allocate memory for tasklet"); } return task; } void DWC_TASK_FREE(dwc_tasklet_t *task) { DWC_FREE(task); } void DWC_TASK_SCHEDULE(dwc_tasklet_t *task) { tasklet_callback(task); } /* workqueues - Runs in process context (can sleep) */ typedef struct work_container { dwc_work_callback_t cb; void *data; dwc_workq_t *wq; char *name; int hz; struct work task; } work_container_t; struct dwc_workq { struct workqueue *taskq; dwc_spinlock_t *lock; dwc_waitq_t *waitq; int pending; struct work_container *container; }; static void do_work(struct work *task, void *data) { dwc_workq_t *wq = (dwc_workq_t *)data; work_container_t *container = wq->container; dwc_irqflags_t flags; if (container->hz) { tsleep(container, 0, "dw3wrk", container->hz); } container->cb(container->data); DWC_DEBUG("Work done: %s, container=%p", container->name, container); DWC_SPINLOCK_IRQSAVE(wq->lock, &flags); if (container->name) DWC_FREE(container->name); DWC_FREE(container); wq->pending--; DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags); DWC_WAITQ_TRIGGER(wq->waitq); } static int work_done(void *data) { dwc_workq_t *workq = (dwc_workq_t *)data; return workq->pending == 0; } int DWC_WORKQ_WAIT_WORK_DONE(dwc_workq_t *workq, int timeout) { return DWC_WAITQ_WAIT_TIMEOUT(workq->waitq, work_done, workq, timeout); } dwc_workq_t *DWC_WORKQ_ALLOC(char *name) { int result; dwc_workq_t *wq = DWC_ALLOC(sizeof(*wq)); if (!wq) { DWC_ERROR("Cannot allocate memory for workqueue"); return NULL; } result = workqueue_create(&wq->taskq, name, do_work, wq, 0 /*PWAIT*/, IPL_BIO, 0); if (result) { DWC_ERROR("Cannot create workqueue"); goto no_taskq; } wq->pending = 0; wq->lock = DWC_SPINLOCK_ALLOC(); if (!wq->lock) { DWC_ERROR("Cannot allocate memory for spinlock"); goto no_lock; } wq->waitq = DWC_WAITQ_ALLOC(); if (!wq->waitq) { DWC_ERROR("Cannot allocate memory for waitqueue"); goto no_waitq; } return wq; no_waitq: DWC_SPINLOCK_FREE(wq->lock); no_lock: workqueue_destroy(wq->taskq); no_taskq: DWC_FREE(wq); return NULL; } void DWC_WORKQ_FREE(dwc_workq_t *wq) { #ifdef DEBUG dwc_irqflags_t flags; DWC_SPINLOCK_IRQSAVE(wq->lock, &flags); if (wq->pending != 0) { struct work_container *container = wq->container; DWC_ERROR("Destroying work queue with pending work"); if (container && container->name) { DWC_ERROR("Work %s still pending", container->name); } } DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags); #endif DWC_WAITQ_FREE(wq->waitq); DWC_SPINLOCK_FREE(wq->lock); workqueue_destroy(wq->taskq); DWC_FREE(wq); } void DWC_WORKQ_SCHEDULE(dwc_workq_t *wq, dwc_work_callback_t cb, void *data, char *format, ...) { dwc_irqflags_t flags; work_container_t *container; static char name[128]; va_list args; va_start(args, format); DWC_VSNPRINTF(name, 128, format, args); va_end(args); DWC_SPINLOCK_IRQSAVE(wq->lock, &flags); wq->pending++; DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags); DWC_WAITQ_TRIGGER(wq->waitq); container = DWC_ALLOC_ATOMIC(sizeof(*container)); if (!container) { DWC_ERROR("Cannot allocate memory for container"); return; } container->name = DWC_STRDUP(name); if (!container->name) { DWC_ERROR("Cannot allocate memory for container->name"); DWC_FREE(container); return; } container->cb = cb; container->data = data; container->wq = wq; container->hz = 0; wq->container = container; DWC_DEBUG("Queueing work: %s, container=%p", container->name, container); workqueue_enqueue(wq->taskq, &container->task); } void DWC_WORKQ_SCHEDULE_DELAYED(dwc_workq_t *wq, dwc_work_callback_t cb, void *data, uint32_t time, char *format, ...) { dwc_irqflags_t flags; work_container_t *container; static char name[128]; struct timeval tv; va_list args; va_start(args, format); DWC_VSNPRINTF(name, 128, format, args); va_end(args); DWC_SPINLOCK_IRQSAVE(wq->lock, &flags); wq->pending++; DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags); DWC_WAITQ_TRIGGER(wq->waitq); container = DWC_ALLOC_ATOMIC(sizeof(*container)); if (!container) { DWC_ERROR("Cannot allocate memory for container"); return; } container->name = DWC_STRDUP(name); if (!container->name) { DWC_ERROR("Cannot allocate memory for container->name"); DWC_FREE(container); return; } container->cb = cb; container->data = data; container->wq = wq; tv.tv_sec = time / 1000; tv.tv_usec = (time - tv.tv_sec * 1000) * 1000; container->hz = tvtohz(&tv); wq->container = container; DWC_DEBUG("Queueing work: %s, container=%p", container->name, container); workqueue_enqueue(wq->taskq, &container->task); } int DWC_WORKQ_PENDING(dwc_workq_t *wq) { return wq->pending; }