1/**
2 * @file
3 * SNMP thread synchronization implementation.
4 */
5
6/*
7 * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without modification,
11 * are permitted provided that the following conditions are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright notice,
14 *    this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright notice,
16 *    this list of conditions and the following disclaimer in the documentation
17 *    and/or other materials provided with the distribution.
18 * 3. The name of the author may not be used to endorse or promote products
19 *    derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
22 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
24 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
26 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
29 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
30 * OF SUCH DAMAGE.
31 *
32 * Author: Dirk Ziegelmeier <dziegel@gmx.de>
33 */
34
35#include "lwip/apps/snmp_opts.h"
36
37#if LWIP_SNMP && (NO_SYS == 0) /* don't build if not configured for use in lwipopts.h */
38
39#include "lwip/apps/snmp_threadsync.h"
40#include "lwip/apps/snmp_core.h"
41#include "lwip/sys.h"
42#include <string.h>
43
44static void
45call_synced_function(struct threadsync_data *call_data, snmp_threadsync_called_fn fn)
46{
47  sys_mutex_lock(&call_data->threadsync_node->instance->sem_usage_mutex);
48  call_data->threadsync_node->instance->sync_fn(fn, call_data);
49  sys_sem_wait(&call_data->threadsync_node->instance->sem);
50  sys_mutex_unlock(&call_data->threadsync_node->instance->sem_usage_mutex);
51}
52
53static void
54threadsync_get_value_synced(void *ctx)
55{
56  struct threadsync_data *call_data = (struct threadsync_data*)ctx;
57
58  call_data->retval.s16 = call_data->proxy_instance.get_value(&call_data->proxy_instance, call_data->arg1.value);
59
60  sys_sem_signal(&call_data->threadsync_node->instance->sem);
61}
62
63static s16_t
64threadsync_get_value(struct snmp_node_instance* instance, void* value)
65{
66  struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr;
67
68  call_data->arg1.value = value;
69  call_synced_function(call_data, threadsync_get_value_synced);
70
71  return call_data->retval.s16;
72}
73
74static void
75threadsync_set_test_synced(void *ctx)
76{
77  struct threadsync_data *call_data = (struct threadsync_data*)ctx;
78
79  call_data->retval.err = call_data->proxy_instance.set_test(&call_data->proxy_instance, call_data->arg2.len, call_data->arg1.value);
80
81  sys_sem_signal(&call_data->threadsync_node->instance->sem);
82}
83
84static snmp_err_t
85threadsync_set_test(struct snmp_node_instance* instance, u16_t len, void *value)
86{
87  struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr;
88
89  call_data->arg1.value = value;
90  call_data->arg2.len = len;
91  call_synced_function(call_data, threadsync_set_test_synced);
92
93  return call_data->retval.err;
94}
95
96static void
97threadsync_set_value_synced(void *ctx)
98{
99  struct threadsync_data *call_data = (struct threadsync_data*)ctx;
100
101  call_data->retval.err = call_data->proxy_instance.set_value(&call_data->proxy_instance, call_data->arg2.len, call_data->arg1.value);
102
103  sys_sem_signal(&call_data->threadsync_node->instance->sem);
104}
105
106static snmp_err_t
107threadsync_set_value(struct snmp_node_instance* instance, u16_t len, void *value)
108{
109  struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr;
110
111  call_data->arg1.value = value;
112  call_data->arg2.len = len;
113  call_synced_function(call_data, threadsync_set_value_synced);
114
115  return call_data->retval.err;
116}
117
118static void
119threadsync_release_instance_synced(void* ctx)
120{
121  struct threadsync_data *call_data = (struct threadsync_data*)ctx;
122
123  call_data->proxy_instance.release_instance(&call_data->proxy_instance);
124
125  sys_sem_signal(&call_data->threadsync_node->instance->sem);
126}
127
128static void
129threadsync_release_instance(struct snmp_node_instance *instance)
130{
131  struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr;
132
133  if (call_data->proxy_instance.release_instance != NULL) {
134    call_synced_function(call_data, threadsync_release_instance_synced);
135  }
136}
137
138static void
139get_instance_synced(void* ctx)
140{
141  struct threadsync_data *call_data   = (struct threadsync_data*)ctx;
142  const struct snmp_leaf_node *leaf   = (const struct snmp_leaf_node*)(const void*)call_data->proxy_instance.node;
143
144  call_data->retval.err = leaf->get_instance(call_data->arg1.root_oid, call_data->arg2.root_oid_len, &call_data->proxy_instance);
145
146  sys_sem_signal(&call_data->threadsync_node->instance->sem);
147}
148
149static void
150get_next_instance_synced(void* ctx)
151{
152  struct threadsync_data *call_data   = (struct threadsync_data*)ctx;
153  const struct snmp_leaf_node *leaf   = (const struct snmp_leaf_node*)(const void*)call_data->proxy_instance.node;
154
155  call_data->retval.err = leaf->get_next_instance(call_data->arg1.root_oid, call_data->arg2.root_oid_len, &call_data->proxy_instance);
156
157  sys_sem_signal(&call_data->threadsync_node->instance->sem);
158}
159
160static snmp_err_t
161do_sync(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance, snmp_threadsync_called_fn fn)
162{
163  const struct snmp_threadsync_node *threadsync_node = (const struct snmp_threadsync_node*)(const void*)instance->node;
164  struct threadsync_data *call_data = &threadsync_node->instance->data;
165
166  if (threadsync_node->node.node.oid != threadsync_node->target->node.oid) {
167    LWIP_DEBUGF(SNMP_DEBUG, ("Sync node OID does not match target node OID"));
168    return SNMP_ERR_NOSUCHINSTANCE;
169  }
170
171  memset(&call_data->proxy_instance, 0, sizeof(call_data->proxy_instance));
172
173  instance->reference.ptr = call_data;
174  snmp_oid_assign(&call_data->proxy_instance.instance_oid, instance->instance_oid.id, instance->instance_oid.len);
175
176  call_data->proxy_instance.node = &threadsync_node->target->node;
177  call_data->threadsync_node     = threadsync_node;
178
179  call_data->arg1.root_oid       = root_oid;
180  call_data->arg2.root_oid_len   = root_oid_len;
181  call_synced_function(call_data, fn);
182
183  if (call_data->retval.err == SNMP_ERR_NOERROR) {
184    instance->access           = call_data->proxy_instance.access;
185    instance->asn1_type        = call_data->proxy_instance.asn1_type;
186    instance->release_instance = threadsync_release_instance;
187    instance->get_value        = (call_data->proxy_instance.get_value != NULL)? threadsync_get_value : NULL;
188    instance->set_value        = (call_data->proxy_instance.set_value != NULL)? threadsync_set_value : NULL;
189    instance->set_test         = (call_data->proxy_instance.set_test != NULL)?  threadsync_set_test  : NULL;
190    snmp_oid_assign(&instance->instance_oid, call_data->proxy_instance.instance_oid.id, call_data->proxy_instance.instance_oid.len);
191  }
192
193  return call_data->retval.err;
194}
195
196snmp_err_t
197snmp_threadsync_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
198{
199  return do_sync(root_oid, root_oid_len, instance, get_instance_synced);
200}
201
202snmp_err_t
203snmp_threadsync_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
204{
205  return do_sync(root_oid, root_oid_len, instance, get_next_instance_synced);
206}
207
208/** Initializes thread synchronization instance */
209void snmp_threadsync_init(struct snmp_threadsync_instance *instance, snmp_threadsync_synchronizer_fn sync_fn)
210{
211  err_t err = sys_mutex_new(&instance->sem_usage_mutex);
212  LWIP_ASSERT("Failed to set up mutex", err == ERR_OK);
213  err = sys_sem_new(&instance->sem, 0);
214  LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */
215  LWIP_ASSERT("Failed to set up semaphore", err == ERR_OK);
216  instance->sync_fn = sync_fn;
217}
218
219#endif /* LWIP_SNMP */
220