1 /* Copyright JS Foundation and other contributors, http://js.foundation
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "jerryscript-mbed-util/logging.h"
16 #include "jerryscript-mbed-drivers/I2C-js.h"
17 #include "jerryscript-mbed-library-registry/wrap_tools.h"
18
19 #include "mbed.h"
20
21 /**
22 * I2C#destructor
23 *
24 * Called if/when the I2C object is GC'ed.
25 */
NAME_FOR_CLASS_NATIVE_DESTRUCTOR(I2C)26 void NAME_FOR_CLASS_NATIVE_DESTRUCTOR(I2C) (void *void_ptr) {
27 delete static_cast<I2C*>(void_ptr);
28 }
29
30 /**
31 * Type infomation of the native I2C pointer
32 *
33 * Set I2C#destructor as the free callback.
34 */
35 static const jerry_object_native_info_t native_obj_type_info = {
36 .free_cb = NAME_FOR_CLASS_NATIVE_DESTRUCTOR(I2C)
37 };
38
39 /**
40 * I2C#frequency (native JavaScript method)
41 *
42 * Set the frequency of the I2C bus.
43 *
44 * @param frequency New I2C Frequency
45 */
DECLARE_CLASS_FUNCTION(I2C,frequency)46 DECLARE_CLASS_FUNCTION(I2C, frequency) {
47 CHECK_ARGUMENT_COUNT(I2C, frequency, (args_count == 1));
48 CHECK_ARGUMENT_TYPE_ALWAYS(I2C, frequency, 0, number);
49
50 // Unwrap native I2C object
51 void *void_ptr;
52 bool has_ptr = jerry_get_object_native_pointer(this_obj, &void_ptr, &native_obj_type_info);
53
54 if (!has_ptr) {
55 return jerry_create_error(JERRY_ERROR_TYPE,
56 (const jerry_char_t *) "Failed to get native I2C pointer");
57 }
58
59 I2C *native_ptr = static_cast<I2C*>(void_ptr);
60
61 int hz = jerry_get_number_value(args[0]);
62 native_ptr->frequency(hz);
63
64 return jerry_create_undefined();
65 }
66
67 /**
68 * I2C#read (native JavaScript method)
69 *
70 * Read data from the I2C bus.
71 *
72 * @overload I2C#read(int)
73 * Read a single byte from the I2C bus
74 *
75 * @param ack indicates if the byte is to be acknowledged (1 => acknowledge)
76 *
77 * @returns array: Data read from the I2C bus
78 *
79 * @overload I2C#read(int, array, int)
80 * Read a series of bytes from the I2C bus
81 *
82 * @param address I2C address to read from
83 * @param data Array to read into
84 * @param length Length of data to read
85 *
86 * @returns array: Data read from the I2C bus
87 */
DECLARE_CLASS_FUNCTION(I2C,read)88 DECLARE_CLASS_FUNCTION(I2C, read) {
89 CHECK_ARGUMENT_COUNT(I2C, read, (args_count == 1 || args_count == 3 || args_count == 4));
90
91 if (args_count == 1) {
92 CHECK_ARGUMENT_TYPE_ALWAYS(I2C, read, 0, number);
93 void *void_ptr;
94 bool has_ptr = jerry_get_object_native_pointer(this_obj, &void_ptr, &native_obj_type_info);
95
96 if (!has_ptr) {
97 return jerry_create_error(JERRY_ERROR_TYPE,
98 (const jerry_char_t *) "Failed to get native I2C pointer");
99 }
100
101 I2C *native_ptr = static_cast<I2C*>(void_ptr);
102
103 int data = jerry_get_number_value(args[0]);
104 int result = native_ptr->read(data);
105
106 return jerry_create_number(result);
107 } else {
108 CHECK_ARGUMENT_TYPE_ALWAYS(I2C, read, 0, number);
109 CHECK_ARGUMENT_TYPE_ALWAYS(I2C, read, 1, array);
110 CHECK_ARGUMENT_TYPE_ALWAYS(I2C, read, 2, number);
111
112 CHECK_ARGUMENT_TYPE_ON_CONDITION(I2C, read, 3, boolean, (args_count == 4));
113
114 void *void_ptr;
115 bool has_ptr = jerry_get_object_native_pointer(this_obj, &void_ptr, &native_obj_type_info);
116
117 if (!has_ptr) {
118 return jerry_create_error(JERRY_ERROR_TYPE,
119 (const jerry_char_t *) "Failed to get native I2C pointer");
120 }
121
122 I2C *native_ptr = static_cast<I2C*>(void_ptr);
123
124 const uint32_t data_len = jerry_get_array_length(args[1]);
125
126 int address = jerry_get_number_value(args[0]);
127 int length = jerry_get_number_value(args[2]);
128
129 char *data = new char[data_len];
130
131 bool repeated = false;
132 if (args_count == 4) {
133 repeated = jerry_get_boolean_value(args[3]);
134 }
135
136 int result = native_ptr->read(address, data, length, repeated);
137
138 jerry_value_t out_array = jerry_create_array(data_len);
139
140 for (uint32_t i = 0; i < data_len; i++) {
141 jerry_value_t val = jerry_create_number(double(data[i]));
142 jerry_release_value(jerry_set_property_by_index(out_array, i, val));
143 jerry_release_value(val);
144 }
145
146 delete[] data;
147
148 if (result == 0) {
149 // ACK
150 return out_array;
151 } else {
152 // NACK
153 const char *error_msg = "NACK received from I2C bus";
154
155 jerry_release_value(out_array);
156 return jerry_create_error(JERRY_ERROR_COMMON, reinterpret_cast<const jerry_char_t *>(error_msg));
157 }
158 }
159 }
160
161 /**
162 * I2C#write (native JavaScript method)
163 *
164 * @overload I2C#write(int)
165 * Write a single byte to the I2C bus
166 *
167 * @param data Data byte to write to the I2C bus
168 *
169 * @returns 1 on success, 0 on failure
170 *
171 * @overload I2C#write(int, array, int, bool)
172 * Write an array of data to a certain address on the I2C bus
173 *
174 * @param address 8-bit I2C slave address
175 * @param data Array of bytes to send
176 * @param length Length of data to write
177 * @param repeated (optional) If true, do not send stop at end.
178 *
179 * @returns 0 on success, non-0 on failure
180 */
DECLARE_CLASS_FUNCTION(I2C,write)181 DECLARE_CLASS_FUNCTION(I2C, write) {
182 CHECK_ARGUMENT_COUNT(I2C, write, (args_count == 1 || args_count == 3 || args_count == 4));
183
184 if (args_count == 1) {
185 CHECK_ARGUMENT_TYPE_ALWAYS(I2C, write, 0, number);
186
187 // Extract native I2C object
188 void *void_ptr;
189 bool has_ptr = jerry_get_object_native_pointer(this_obj, &void_ptr, &native_obj_type_info);
190
191 if (!has_ptr) {
192 return jerry_create_error(JERRY_ERROR_TYPE,
193 (const jerry_char_t *) "Failed to get native I2C pointer");
194 }
195
196 I2C *native_ptr = static_cast<I2C*>(void_ptr);
197
198 // Unwrap arguments
199 int data = jerry_get_number_value(args[0]);
200
201 int result = native_ptr->write(data);
202 return jerry_create_number(result);
203 } else {
204 // 3 or 4
205 CHECK_ARGUMENT_TYPE_ALWAYS(I2C, write, 0, number);
206 CHECK_ARGUMENT_TYPE_ALWAYS(I2C, write, 1, array);
207 CHECK_ARGUMENT_TYPE_ALWAYS(I2C, write, 2, number);
208 CHECK_ARGUMENT_TYPE_ON_CONDITION(I2C, write, 3, boolean, (args_count == 4));
209
210 // Extract native I2C object
211 void *void_ptr;
212 bool has_ptr = jerry_get_object_native_pointer(this_obj, &void_ptr, &native_obj_type_info);
213
214 if (!has_ptr) {
215 return jerry_create_error(JERRY_ERROR_TYPE,
216 (const jerry_char_t *) "Failed to get native I2C pointer");
217 }
218
219 I2C *native_ptr = static_cast<I2C*>(void_ptr);
220
221 // Unwrap arguments
222 int address = jerry_get_number_value(args[0]);
223 const uint32_t data_len = jerry_get_array_length(args[1]);
224 int length = jerry_get_number_value(args[2]);
225 bool repeated = args_count == 4 && jerry_get_boolean_value(args[3]);
226
227 // Construct data byte array
228 char *data = new char[data_len];
229 for (uint32_t i = 0; i < data_len; i++) {
230 data[i] = jerry_get_number_value(jerry_get_property_by_index(args[1], i));
231 }
232
233 int result = native_ptr->write(address, data, length, repeated);
234
235 // free dynamically allocated resources
236 delete[] data;
237
238 return jerry_create_number(result);
239 }
240 }
241
242 /**
243 * I2C#start (native JavaScript method)
244 *
245 * Creates a start condition on the I2C bus.
246 */
DECLARE_CLASS_FUNCTION(I2C,start)247 DECLARE_CLASS_FUNCTION(I2C, start) {
248 CHECK_ARGUMENT_COUNT(I2C, start, (args_count == 0));
249
250 // Extract native I2C object
251 void *void_ptr;
252 bool has_ptr = jerry_get_object_native_pointer(this_obj, &void_ptr, &native_obj_type_info);
253
254 if (!has_ptr) {
255 return jerry_create_error(JERRY_ERROR_TYPE,
256 (const jerry_char_t *) "Failed to get native I2C pointer");
257 }
258
259 I2C *native_ptr = static_cast<I2C*>(void_ptr);
260
261 native_ptr->start();
262 return jerry_create_undefined();
263 }
264
265 /**
266 * I2C#stop (native JavaScript method)
267 *
268 * Creates a stop condition on the I2C bus.
269 */
DECLARE_CLASS_FUNCTION(I2C,stop)270 DECLARE_CLASS_FUNCTION(I2C, stop) {
271 CHECK_ARGUMENT_COUNT(I2C, stop, (args_count == 0));
272
273 // Extract native I2C object
274 void *void_ptr;
275 bool has_ptr = jerry_get_object_native_pointer(this_obj, &void_ptr, &native_obj_type_info);
276
277 if (!has_ptr) {
278 return jerry_create_error(JERRY_ERROR_TYPE,
279 (const jerry_char_t *) "Failed to get native I2C pointer");
280 }
281
282 I2C *native_ptr = static_cast<I2C*>(void_ptr);
283
284 native_ptr->stop();
285 return jerry_create_undefined();
286 }
287
288 /**
289 * I2C (native JavaScript constructor)
290 *
291 * @param sda mbed pin for I2C data
292 * @param scl mbed pin for I2C clock
293 * @returns a JavaScript object representing the I2C bus.
294 */
DECLARE_CLASS_CONSTRUCTOR(I2C)295 DECLARE_CLASS_CONSTRUCTOR(I2C) {
296 CHECK_ARGUMENT_COUNT(I2C, __constructor, (args_count == 2));
297 CHECK_ARGUMENT_TYPE_ALWAYS(I2C, __constructor, 0, number);
298 CHECK_ARGUMENT_TYPE_ALWAYS(I2C, __constructor, 1, number);
299
300 int sda = jerry_get_number_value(args[0]);
301 int scl = jerry_get_number_value(args[1]);
302
303 I2C *native_ptr = new I2C((PinName)sda, (PinName)scl);
304
305 jerry_value_t js_object = jerry_create_object();
306 jerry_set_object_native_pointer(js_object, native_ptr, &native_obj_type_info);
307
308 ATTACH_CLASS_FUNCTION(js_object, I2C, frequency);
309 ATTACH_CLASS_FUNCTION(js_object, I2C, read);
310 ATTACH_CLASS_FUNCTION(js_object, I2C, write);
311 ATTACH_CLASS_FUNCTION(js_object, I2C, start);
312 ATTACH_CLASS_FUNCTION(js_object, I2C, stop);
313
314 return js_object;
315 }
316