| /* |
| * Copyright (c) 2018 The Fuchsia Authors |
| * |
| * Permission to use, copy, modify, and/or distribute this software for any |
| * purpose with or without fee is hereby granted, provided that the above |
| * copyright notice and this permission notice appear in all copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
| * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
| * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| #include "device.h" |
| |
| #include <stdatomic.h> |
| |
| #include "debug.h" |
| |
| pthread_mutex_t irq_callback_lock; |
| |
| async_dispatcher_t* default_dispatcher; |
| |
| static void brcmf_timer_handler(async_dispatcher_t* dispatcher, async_task_t* task, |
| zx_status_t status) { |
| if (status != ZX_OK) { |
| return; |
| } |
| brcmf_timer_info_t* timer = containerof(task, brcmf_timer_info_t, task); |
| timer->callback_function(timer->data); |
| mtx_lock(&timer->lock); |
| timer->scheduled = false; |
| sync_completion_signal(&timer->finished); |
| mtx_unlock(&timer->lock); |
| } |
| |
| void brcmf_timer_init(brcmf_timer_info_t* timer, brcmf_timer_callback_t* callback, void* data) { |
| memset(&timer->task.state, 0, sizeof(timer->task.state)); |
| timer->task.handler = brcmf_timer_handler; |
| timer->data = data; |
| timer->callback_function = callback; |
| timer->finished = SYNC_COMPLETION_INIT; |
| timer->scheduled = false; |
| mtx_init(&timer->lock, mtx_plain); |
| } |
| |
| void brcmf_timer_set(brcmf_timer_info_t* timer, zx_duration_t delay) { |
| mtx_lock(&timer->lock); |
| async_cancel_task(default_dispatcher, &timer->task); // Make sure it's not scheduled |
| timer->task.deadline = delay + async_now(default_dispatcher); |
| timer->scheduled = true; |
| sync_completion_reset(&timer->finished); |
| async_post_task(default_dispatcher, &timer->task); |
| mtx_unlock(&timer->lock); |
| } |
| |
| void brcmf_timer_stop(brcmf_timer_info_t* timer) { |
| mtx_lock(&timer->lock); |
| if (!timer->scheduled) { |
| mtx_unlock(&timer->lock); |
| return; |
| } |
| zx_status_t result = async_cancel_task(default_dispatcher, &timer->task); |
| mtx_unlock(&timer->lock); |
| if (result != ZX_OK) { |
| sync_completion_wait(&timer->finished, ZX_TIME_INFINITE); |
| } |
| } |
| |
| struct net_device* brcmf_allocate_net_device(size_t priv_size, const char* name) { |
| struct net_device* dev = calloc(1, sizeof(*dev)); |
| if (dev == NULL) { |
| return NULL; |
| } |
| dev->priv = calloc(1, priv_size); |
| if (dev->priv == NULL) { |
| free(dev); |
| return NULL; |
| } |
| strlcpy(dev->name, name, sizeof(dev->name)); |
| return dev; |
| } |
| |
| void brcmf_free_net_device(struct net_device* dev) { |
| if (dev != NULL) { |
| free(dev->priv); |
| } |
| free(dev); |
| } |
| |
| void brcmf_enable_tx(struct net_device* dev) { |
| brcmf_dbg(INFO, " * * NOTE: brcmf_enable_tx called. Enable TX. (Was netif_wake_queue)"); |
| } |
| |
| // This is a kill-flies-with-sledgehammers, just-get-it-working version; TODO(NET-805) for |
| // efficiency. |
| |
| bool brcmf_test_and_set_bit_in_array(size_t bit_number, atomic_ulong* addr) { |
| size_t index = bit_number >> 6; |
| uint64_t bit = 1 << (bit_number & 0x3f); |
| return !!(atomic_fetch_or(&addr[index], bit) & bit); |
| } |
| |
| bool brcmf_test_and_clear_bit_in_array(size_t bit_number, atomic_ulong* addr) { |
| uint32_t index = bit_number >> 6; |
| uint64_t bit = 1 << (bit_number & 0x3f); |
| return !!(atomic_fetch_and(&addr[index], ~bit) & bit); |
| } |
| |
| bool brcmf_test_bit_in_array(size_t bit_number, atomic_ulong* addr) { |
| uint32_t index = bit_number >> 6; |
| uint64_t bit = 1 << (bit_number & 0x3f); |
| return !!(atomic_load(&addr[index]) & bit); |
| } |
| |
| void brcmf_clear_bit_in_array(size_t bit_number, atomic_ulong* addr) { |
| (void)brcmf_test_and_clear_bit_in_array(bit_number, addr); |
| } |
| |
| void brcmf_set_bit_in_array(size_t bit_number, atomic_ulong* addr) { |
| (void)brcmf_test_and_set_bit_in_array(bit_number, addr); |
| } |
| |
| // TODO(jeffbrown): Once we have an equivalent of debugfs, implement / connect these. |
| zx_status_t brcmf_debugfs_create_directory(const char *name, zx_handle_t parent, |
| zx_handle_t* new_directory_out) { |
| if (new_directory_out) { |
| *new_directory_out = ZX_HANDLE_INVALID; |
| } |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| zx_status_t brcmf_debugfs_create_sequential_file(void* dev, const char* fn, zx_handle_t parent, |
| zx_status_t (*read_fn)(struct seq_file* seq, |
| void* data), |
| zx_handle_t* new_file_out) { |
| if (new_file_out) { |
| *new_file_out = ZX_HANDLE_INVALID; |
| } |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| zx_status_t brcmf_debugfs_rm_recursive(zx_handle_t dir) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| zx_status_t brcmf_debugfs_create_u32_file(const char* name, uint32_t permissions, |
| zx_handle_t parent, uint32_t* data_to_access) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |