1/* 2 * Copyright 2019, Data61 3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO) 4 * ABN 41 687 119 230. 5 * 6 * This software may be distributed and modified according to the terms of 7 * the BSD 2-Clause license. Note that NO WARRANTY is provided. 8 * See "LICENSE_BSD2.txt" for details. 9 * 10 * @TAG(DATA61_BSD) 11 */ 12 13#include <stdbool.h> 14#include <stddef.h> 15#include <utils/util.h> 16#include <platsupport/gpio_utils.h> 17 18/* This is forward declared in the header file. */ 19struct gpio_chain { 20 list_t pin_list; 21}; 22 23struct gpio_callback_token { 24 bool is_read; 25 char *buffer; 26 size_t bits_processed; 27 size_t nbits_to_process; 28} 29 30static int gpio_chain_comparator(void *a, void *b) 31{ 32 gpio_t *gpio_a = a; 33 gpio_t *gpio_b = b; 34 35 if (gpio_a->id == gpio_b->id) { 36 return 0; 37 } 38 39 return -1; 40} 41 42int gpio_chain_init(ps_malloc_ops_t *malloc_ops, gpio_chain_t **ret_chain) 43{ 44 if (!malloc_ops || !ret_chain) { 45 ZF_LOGE("Arguments are NULL!"); 46 return -EINVAL; 47 } 48 49 int error = ps_calloc(malloc_ops, 1, sizeof(**ret_chain), (void **) ret_chain); 50 if (error) { 51 return -ENOMEM; 52 } 53 54 error = list_init(&(*ret_chain)->pin_list); 55 if (error) { 56 ps_free(malloc_ops, sizeof(**ret_chain), *ret_chain); 57 return error; 58 } 59 60 return 0; 61} 62 63int gpio_chain_destroy(ps_malloc_ops_t *malloc_ops, gpio_chain_t *chain) 64{ 65 if (!malloc_ops || !chain) { 66 ZF_LOGE("Arguments are NULL!"); 67 return -EINVAL; 68 } 69 70 /* We're expected to have removed all elements from 71 * the list before destroying it */ 72 int error = list_remove_all(&chain->pin_list); 73 if (error) { 74 return error; 75 } 76 77 error = list_destroy(&chain->pin_list); 78 if (error) { 79 return error; 80 } 81 82 ps_free(malloc_ops, sizeof(*chain), chain); 83 84 return 0; 85} 86 87int gpio_chain_add(gpio_chain_t *chain, gpio_t *gpio) 88{ 89 if (!chain || !gpio) { 90 ZF_LOGE("Arguments are NULL!"); 91 return -EINVAL; 92 } 93 94 int error = list_append(&chain->pin_list, (void *) gpio); 95 if (error) { 96 return error; 97 } 98 99 return 0; 100} 101 102int gpio_chain_remove(gpio_chain_t *chain, gpio_t *gpio) 103{ 104 if (!chain || !gpio) { 105 ZF_LOGE("Arguments are NULL!"); 106 return -EINVAL; 107 } 108 109 int not_found = list_remove(&chain->pin_list, gpio, gpio_chain_comparator); 110 if (not_found) { 111 return -ENOENT; 112 } 113 return 0; 114} 115 116int gpio_chain_io_callback(void *data, void *token) 117{ 118 gpio_t *gpio = data; 119 struct gpio_callback_token *cb_token = token; 120 121 /* Do nothing if we've already processed enough bits */ 122 if (cb_token->nbits_to_process == cb_token->bits_processed) { 123 return 0; 124 } 125 126 size_t curr_byte = cb_token->bits_processed / sizeof(*(cb_token->buffer)); 127 size_t curr_bit = cb_token->bits_processed % sizeof(*(cb_token->buffer)); 128 129 if (cb_token->is_read) { 130 int val = gpio_get(gpio); 131 132 val <<= curr_bit; 133 cb_token->buffer[curr_byte] &= ~BIT(curr_bit); 134 cb_token->buffer[curr_byte] |= val; 135 } else { 136 if (cb_token->buffer[curr_byte] & BIT(curr_bit)) { 137 gpio_set(gpio); 138 } else { 139 gpio_clr(gpio); 140 } 141 } 142 143 cb_token->bits_processed++; 144 145 return 0; 146} 147 148int gpio_chain_read(gpio_chain_t *chain, char *data, int len) 149{ 150 if (!chain || !data) { 151 ZF_LOGE("Arguments are NULL!"); 152 return -EINVAL; 153 } 154 155 struct gpio_callback_token cb_token = { .is_read = true, .buffer = data, .bits_processed = 0, 156 .nbits_to_process = len 157 }; 158 159 int error = list_foreach(&chain->pin_list, gpio_chain_io_callback, &cb_token); 160 if (error) { 161 return error; 162 } 163 164 return cb_token->bits_processed; 165} 166 167int gpio_chain_write(gpio_chain_t *chain, const char *data, int len) 168{ 169 if (!chain || !data) { 170 ZF_LOGE("Arguments are NULL!"); 171 return -EINVAL; 172 } 173 174 struct gpio_callback_token cb_token = { .is_read = false, .buffer = data, .bits_processed = 0, 175 .nbits_to_process = len 176 }; 177 178 int error = list_foreach(&chain->pin_list, gpio_chain_io_callback, &cb_token); 179 if (error) { 180 return error; 181 } 182 183 return cb_token->bits_processed; 184} 185