1/*
2 * This file is part of the MicroPython project, http://micropython.org/
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (c) 2017 Paul Sokolovsky
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 * THE SOFTWARE.
25 */
26
27#include "py/mpconfig.h"
28#if MICROPY_PY_MACHINE
29
30#include <string.h>
31
32#include "py/obj.h"
33#include "py/runtime.h"
34#include "extmod/virtpin.h"
35#include "extmod/machine_signal.h"
36
37// Signal class
38
39typedef struct _machine_signal_t {
40 mp_obj_base_t base;
41 mp_obj_t pin;
42 bool invert;
43} machine_signal_t;
44
45STATIC mp_obj_t signal_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
46 mp_obj_t pin;
47 bool invert = false;
48
49 #if defined(MICROPY_PY_MACHINE_PIN_MAKE_NEW)
50 mp_pin_p_t *pin_p = NULL;
51
52 if (n_args > 0 && mp_obj_is_obj(args[0])) {
53 mp_obj_base_t *pin_base = (mp_obj_base_t *)MP_OBJ_TO_PTR(args[0]);
54 pin_p = (mp_pin_p_t *)pin_base->type->protocol;
55 }
56
57 if (pin_p == NULL) {
58 // If first argument isn't a Pin-like object, we filter out "invert"
59 // from keyword arguments and pass them all to the exported Pin
60 // constructor to create one.
61 mp_obj_t *pin_args = mp_local_alloc((n_args + n_kw * 2) * sizeof(mp_obj_t));
62 memcpy(pin_args, args, n_args * sizeof(mp_obj_t));
63 const mp_obj_t *src = args + n_args;
64 mp_obj_t *dst = pin_args + n_args;
65 mp_obj_t *sig_value = NULL;
66 for (size_t cnt = n_kw; cnt; cnt--) {
67 if (*src == MP_OBJ_NEW_QSTR(MP_QSTR_invert)) {
68 invert = mp_obj_is_true(src[1]);
69 n_kw--;
70 } else {
71 *dst++ = *src;
72 *dst++ = src[1];
73 }
74 if (*src == MP_OBJ_NEW_QSTR(MP_QSTR_value)) {
75 // Value is pertained to Signal, so we should invert
76 // it for Pin if needed, and we should do it only when
77 // inversion status is guaranteedly known.
78 sig_value = dst - 1;
79 }
80 src += 2;
81 }
82
83 if (invert && sig_value != NULL) {
84 *sig_value = mp_obj_is_true(*sig_value) ? MP_OBJ_NEW_SMALL_INT(0) : MP_OBJ_NEW_SMALL_INT(1);
85 }
86
87 // Here we pass NULL as a type, hoping that mp_pin_make_new()
88 // will just ignore it as set a concrete type. If not, we'd need
89 // to expose port's "default" pin type too.
90 pin = MICROPY_PY_MACHINE_PIN_MAKE_NEW(NULL, n_args, n_kw, pin_args);
91
92 mp_local_free(pin_args);
93 } else
94 #endif
95 // Otherwise there should be 1 or 2 args
96 {
97 if (n_args == 1) {
98 pin = args[0];
99 if (n_kw == 0) {
100 } else if (n_kw == 1 && args[1] == MP_OBJ_NEW_QSTR(MP_QSTR_invert)) {
101 invert = mp_obj_is_true(args[2]);
102 } else {
103 goto error;
104 }
105 } else {
106 error:
107 mp_raise_TypeError(NULL);
108 }
109 }
110
111 machine_signal_t *o = m_new_obj(machine_signal_t);
112 o->base.type = type;
113 o->pin = pin;
114 o->invert = invert;
115 return MP_OBJ_FROM_PTR(o);
116}
117
118STATIC mp_uint_t signal_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) {
119 (void)errcode;
120 machine_signal_t *self = MP_OBJ_TO_PTR(self_in);
121
122 switch (request) {
123 case MP_PIN_READ: {
124 return mp_virtual_pin_read(self->pin) ^ self->invert;
125 }
126 case MP_PIN_WRITE: {
127 mp_virtual_pin_write(self->pin, arg ^ self->invert);
128 return 0;
129 }
130 }
131 return -1;
132}
133
134// fast method for getting/setting signal value
135STATIC mp_obj_t signal_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
136 mp_arg_check_num(n_args, n_kw, 0, 1, false);
137 if (n_args == 0) {
138 // get pin
139 return MP_OBJ_NEW_SMALL_INT(mp_virtual_pin_read(self_in));
140 } else {
141 // set pin
142 mp_virtual_pin_write(self_in, mp_obj_is_true(args[0]));
143 return mp_const_none;
144 }
145}
146
147STATIC mp_obj_t signal_value(size_t n_args, const mp_obj_t *args) {
148 return signal_call(args[0], n_args - 1, 0, args + 1);
149}
150STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(signal_value_obj, 1, 2, signal_value);
151
152STATIC mp_obj_t signal_on(mp_obj_t self_in) {
153 mp_virtual_pin_write(self_in, 1);
154 return mp_const_none;
155}
156STATIC MP_DEFINE_CONST_FUN_OBJ_1(signal_on_obj, signal_on);
157
158STATIC mp_obj_t signal_off(mp_obj_t self_in) {
159 mp_virtual_pin_write(self_in, 0);
160 return mp_const_none;
161}
162STATIC MP_DEFINE_CONST_FUN_OBJ_1(signal_off_obj, signal_off);
163
164STATIC const mp_rom_map_elem_t signal_locals_dict_table[] = {
165 { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&signal_value_obj) },
166 { MP_ROM_QSTR(MP_QSTR_on), MP_ROM_PTR(&signal_on_obj) },
167 { MP_ROM_QSTR(MP_QSTR_off), MP_ROM_PTR(&signal_off_obj) },
168};
169
170STATIC MP_DEFINE_CONST_DICT(signal_locals_dict, signal_locals_dict_table);
171
172STATIC const mp_pin_p_t signal_pin_p = {
173 .ioctl = signal_ioctl,
174};
175
176const mp_obj_type_t machine_signal_type = {
177 { &mp_type_type },
178 .name = MP_QSTR_Signal,
179 .make_new = signal_make_new,
180 .call = signal_call,
181 .protocol = &signal_pin_p,
182 .locals_dict = (void *)&signal_locals_dict,
183};
184
185#endif // MICROPY_PY_MACHINE
186