1 /* Copyright (c) 2014, Nordic Semiconductor ASA
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to deal
5  * in the Software without restriction, including without limitation the rights
6  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7  * copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in all
11  * copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19  * SOFTWARE.
20  */
21 
22 /** @file
23 @brief Implementation of the ACI transport layer module
24 */
25 
26 #include <string>
27 #include <stdexcept>
28 #include <stdio.h>
29 
30 #include "hal_platform.h"
31 #include "hal_aci_tl.h"
32 #include "aci_queue.h"
33 
34 #define HIGH                    1
35 #define LOW                     0
36 
37 #define REVERSE_BITS(byte) (((reverse_lookup[(byte & 0x0F)]) << 4) + reverse_lookup[((byte & 0xF0) >> 4)])
38 static const uint8_t reverse_lookup[] = { 0, 8,  4, 12, 2, 10, 6, 14,1, 9, 5, 13,3, 11, 7, 15 };
39 
40 static void m_aci_data_print(hal_aci_data_t *p_data);
41 static void m_aci_event_check(void);
42 static void m_aci_isr(void);
43 static void m_aci_pins_set(aci_pins_t *a_pins_ptr);
44 static inline void m_aci_reqn_disable (void);
45 static inline void m_aci_reqn_enable (void);
46 static void m_aci_q_flush(void);
47 static bool m_aci_spi_transfer(hal_aci_data_t * data_to_send, hal_aci_data_t * received_data);
48 
49 static uint8_t        spi_readwrite(uint8_t aci_byte);
50 
51 static bool           aci_debug_print = false;
52 
53 aci_queue_t    aci_tx_q;
54 aci_queue_t    aci_rx_q;
55 
56 static aci_pins_t    *a_pins_local_ptr;
57 
m_aci_data_print(hal_aci_data_t * p_data)58 void m_aci_data_print(hal_aci_data_t *p_data)
59 {
60   const uint8_t length = p_data->buffer[0];
61   uint8_t i;
62   printf("%d\n", length);
63   printf(" :\n");
64   for (i=0; i<=length; i++)
65   {
66     printf("%x", p_data->buffer[i]);
67     printf(", ");
68   }
69   printf("\n");
70 }
71 
72 /*
73   Interrupt service routine called when the RDYN line goes low. Runs the SPI transfer.
74 */
m_aci_isr(void)75 static void m_aci_isr(void)
76 {
77   hal_aci_data_t data_to_send;
78   hal_aci_data_t received_data;
79 
80   // Receive from queue
81   if (!aci_queue_dequeue_from_isr(&aci_tx_q, &data_to_send))
82   {
83     /* queue was empty, nothing to send */
84     data_to_send.status_byte = 0;
85     data_to_send.buffer[0] = 0;
86   }
87 
88   // Receive and/or transmit data
89   m_aci_spi_transfer(&data_to_send, &received_data);
90 
91   if (!aci_queue_is_full_from_isr(&aci_rx_q) && !aci_queue_is_empty_from_isr(&aci_tx_q))
92   {
93     m_aci_reqn_enable();
94   }
95 
96   // Check if we received data
97   if (received_data.buffer[0] > 0)
98   {
99     if (!aci_queue_enqueue_from_isr(&aci_rx_q, &received_data))
100     {
101       /* Receive Buffer full.
102          Should never happen.
103          Spin in a while loop.
104       */
105       while(1);
106     }
107 
108     // Disable ready line interrupt until we have room to store incoming messages
109     if (aci_queue_is_full_from_isr(&aci_rx_q))
110     {
111       // detachInterrupt(a_pins_local_ptr->interrupt_number);
112     }
113   }
114 
115   return;
116 }
117 
118 /*
119   Checks the RDYN line and runs the SPI transfer if required.
120 */
m_aci_event_check(void)121 static void m_aci_event_check(void)
122 {
123   hal_aci_data_t data_to_send;
124   hal_aci_data_t received_data;
125 
126   // No room to store incoming messages
127   if (aci_queue_is_full(&aci_rx_q))
128   {
129     return;
130   }
131 
132   // If the ready line is disabled and we have pending messages outgoing we enable the request line
133   if (HIGH == mraa_gpio_read (a_pins_local_ptr->m_rdy_ctx))
134   // if (HIGH == digitalRead(a_pins_local_ptr->rdyn_pin))
135   {
136     if (!aci_queue_is_empty(&aci_tx_q))
137     {
138       m_aci_reqn_enable();
139     }
140 
141     return;
142   }
143 
144   // Receive from queue
145   if (!aci_queue_dequeue(&aci_tx_q, &data_to_send))
146   {
147     /* queue was empty, nothing to send */
148     data_to_send.status_byte = 0;
149     data_to_send.buffer[0] = 0;
150   }
151 
152   // Receive and/or transmit data
153   m_aci_spi_transfer(&data_to_send, &received_data);
154 
155   /* If there are messages to transmit, and we can store the reply, we request a new transfer */
156   if (!aci_queue_is_full(&aci_rx_q) && !aci_queue_is_empty(&aci_tx_q))
157   {
158     m_aci_reqn_enable();
159   }
160 
161   // Check if we received data
162   if (received_data.buffer[0] > 0)
163   {
164     if (!aci_queue_enqueue(&aci_rx_q, &received_data))
165     {
166       /* Receive Buffer full.
167          Should never happen.
168          Spin in a while loop.
169       */
170       while(1);
171     }
172   }
173 
174   return;
175 }
176 
177 /** @brief Point the low level library at the ACI pins specified
178  *  @details
179  *  The ACI pins are specified in the application and a pointer is made available for
180  *  the low level library to use
181  */
m_aci_pins_set(aci_pins_t * a_pins_ptr)182 static void m_aci_pins_set(aci_pins_t *a_pins_ptr)
183 {
184   a_pins_local_ptr = a_pins_ptr;
185 }
186 
m_aci_reqn_disable(void)187 static inline void m_aci_reqn_disable (void)
188 {
189     mraa_gpio_write (a_pins_local_ptr->m_req_ctx, HIGH);
190 }
191 
m_aci_reqn_enable(void)192 static inline void m_aci_reqn_enable (void)
193 {
194     mraa_gpio_write (a_pins_local_ptr->m_req_ctx, LOW);
195 }
196 
m_aci_q_flush(void)197 static void m_aci_q_flush(void)
198 {
199   // noInterrupts();
200   /* re-initialize aci cmd queue and aci event queue to flush them*/
201   aci_queue_init(&aci_tx_q);
202   aci_queue_init(&aci_rx_q);
203   // interrupts();
204 }
205 
m_aci_spi_transfer(hal_aci_data_t * data_to_send,hal_aci_data_t * received_data)206 static bool m_aci_spi_transfer(hal_aci_data_t * data_to_send, hal_aci_data_t * received_data)
207 {
208   uint8_t byte_cnt;
209   uint8_t byte_sent_cnt;
210   uint8_t max_bytes;
211 
212   m_aci_reqn_enable();
213 
214   // Send length, receive header
215   byte_sent_cnt = 0;
216   received_data->status_byte = spi_readwrite(data_to_send->buffer[byte_sent_cnt++]);
217   // Send first byte, receive length from slave
218   received_data->buffer[0] = spi_readwrite(data_to_send->buffer[byte_sent_cnt++]);
219   if (0 == data_to_send->buffer[0])
220   {
221     max_bytes = received_data->buffer[0];
222   }
223   else
224   {
225     // Set the maximum to the biggest size. One command byte is already sent
226     max_bytes = (received_data->buffer[0] > (data_to_send->buffer[0] - 1))
227                                           ? received_data->buffer[0]
228                                           : (data_to_send->buffer[0] - 1);
229   }
230 
231   if (max_bytes > HAL_ACI_MAX_LENGTH)
232   {
233     max_bytes = HAL_ACI_MAX_LENGTH;
234   }
235 
236   // Transmit/receive the rest of the packet
237   for (byte_cnt = 0; byte_cnt < max_bytes; byte_cnt++)
238   {
239     received_data->buffer[byte_cnt+1] =  spi_readwrite(data_to_send->buffer[byte_sent_cnt++]);
240   }
241 
242   // RDYN should follow the REQN line in approx 100ns
243   m_aci_reqn_disable();
244 
245   return (max_bytes > 0);
246 }
247 
hal_aci_tl_debug_print(bool enable)248 void hal_aci_tl_debug_print(bool enable)
249 {
250     aci_debug_print = enable;
251 }
252 
hal_aci_tl_pin_reset(void)253 void hal_aci_tl_pin_reset(void)
254 {
255     if (UNUSED != a_pins_local_ptr->reset_pin)
256     {
257         // pinMode(a_pins_local_ptr->reset_pin, OUTPUT);
258 
259         if ((REDBEARLAB_SHIELD_V1_1     == a_pins_local_ptr->board_name) ||
260             (REDBEARLAB_SHIELD_V2012_07 == a_pins_local_ptr->board_name))
261         {
262             //The reset for the Redbearlab v1.1 and v2012.07 boards are inverted and has a Power On Reset
263             //circuit that takes about 100ms to trigger the reset
264             mraa_gpio_write (a_pins_local_ptr->m_rst_ctx, HIGH);
265             usleep (100000);
266             mraa_gpio_write (a_pins_local_ptr->m_rst_ctx, LOW);
267         }
268         else
269         {
270             mraa_gpio_write (a_pins_local_ptr->m_rst_ctx, HIGH);
271             mraa_gpio_write (a_pins_local_ptr->m_rst_ctx, LOW);
272             mraa_gpio_write (a_pins_local_ptr->m_rst_ctx, HIGH);
273         }
274     }
275 }
276 
hal_aci_tl_event_peek(hal_aci_data_t * p_aci_data)277 bool hal_aci_tl_event_peek(hal_aci_data_t *p_aci_data)
278 {
279   if (!a_pins_local_ptr->interface_is_interrupt)
280   {
281     m_aci_event_check();
282   }
283 
284   if (aci_queue_peek(&aci_rx_q, p_aci_data))
285   {
286     return true;
287   }
288 
289   return false;
290 }
291 
hal_aci_tl_event_get(hal_aci_data_t * p_aci_data)292 bool hal_aci_tl_event_get(hal_aci_data_t *p_aci_data)
293 {
294   bool was_full;
295 
296   if (!a_pins_local_ptr->interface_is_interrupt && !aci_queue_is_full(&aci_rx_q))
297   {
298     m_aci_event_check();
299   }
300 
301   was_full = aci_queue_is_full(&aci_rx_q);
302 
303   if (aci_queue_dequeue(&aci_rx_q, p_aci_data))
304   {
305     if (aci_debug_print)
306     {
307       printf(" E");
308       m_aci_data_print(p_aci_data);
309     }
310 
311     if (was_full && a_pins_local_ptr->interface_is_interrupt)
312     {
313       /* Enable RDY line interrupt again */
314       // attachInterrupt(a_pins_local_ptr->interrupt_number, m_aci_isr, LOW);
315     }
316 
317     /* Attempt to pull REQN LOW since we've made room for new messages */
318     if (!aci_queue_is_full(&aci_rx_q) && !aci_queue_is_empty(&aci_tx_q))
319     {
320       m_aci_reqn_enable();
321     }
322 
323     return true;
324   }
325 
326   return false;
327 }
328 
hal_aci_tl_init(aci_pins_t * a_pins,bool debug)329 void hal_aci_tl_init(aci_pins_t *a_pins, bool debug)
330 {
331     mraa_result_t error = MRAA_SUCCESS;
332     aci_debug_print = debug;
333 
334     /* Needs to be called as the first thing for proper intialization*/
335     m_aci_pins_set(a_pins);
336 
337     /*
338      * Init SPI
339      */
340     a_pins->m_spi = mraa_spi_init (0);
341     if (a_pins->m_spi == NULL) {
342         throw std::invalid_argument(std::string(__FUNCTION__) +
343                                     ": mraa_spi_init() failed");
344     }
345 
346     mraa_spi_frequency (a_pins->m_spi, 2000000);
347     mraa_spi_mode (a_pins->m_spi, MRAA_SPI_MODE0);
348 
349     /* Initialize the ACI Command queue. This must be called after the delay above. */
350     aci_queue_init(&aci_tx_q);
351     aci_queue_init(&aci_rx_q);
352 
353     // Configure the IO lines
354     a_pins->m_rdy_ctx = mraa_gpio_init (a_pins->rdyn_pin);
355     if (a_pins->m_rdy_ctx == NULL) {
356         throw std::invalid_argument(std::string(__FUNCTION__) +
357                                     ": mraa_gpio_init(rdyn) failed, invalid pin?");
358     }
359 
360     a_pins->m_req_ctx = mraa_gpio_init (a_pins->reqn_pin);
361     if (a_pins->m_req_ctx == NULL) {
362         throw std::invalid_argument(std::string(__FUNCTION__) +
363                                     ": mraa_gpio_init(reqn) failed, invalid pin?");
364     }
365 
366     a_pins->m_rst_ctx = mraa_gpio_init (a_pins->reset_pin);
367     if (a_pins->m_rst_ctx == NULL) {
368         throw std::invalid_argument(std::string(__FUNCTION__) +
369                                     ": mraa_gpio_init(reset) failed, invalid pin?");
370     }
371 
372     error = mraa_gpio_dir (a_pins->m_rdy_ctx, MRAA_GPIO_IN);
373     if (error != MRAA_SUCCESS) {
374         printf ("[ERROR] GPIO failed to initilize \n");
375     }
376 
377     error = mraa_gpio_dir (a_pins->m_req_ctx, MRAA_GPIO_OUT);
378     if (error != MRAA_SUCCESS) {
379         printf ("[ERROR] GPIO failed to initilize \n");
380     }
381 
382     error = mraa_gpio_dir (a_pins->m_rst_ctx, MRAA_GPIO_OUT);
383     if (error != MRAA_SUCCESS) {
384         printf ("[ERROR] GPIO failed to initilize \n");
385     }
386 
387     if (UNUSED != a_pins->active_pin) {
388     }
389 
390   /* Pin reset the nRF8001, required when the nRF8001 setup is being changed */
391   hal_aci_tl_pin_reset();
392 
393   /* Set the nRF8001 to a known state as required by the datasheet*/
394   mraa_gpio_write (a_pins->m_req_ctx, LOW);
395 
396   usleep(30000); //Wait for the nRF8001 to get hold of its lines - the lines float for a few ms after the reset
397 
398     /* Attach the interrupt to the RDYN line as requested by the caller */
399     if (a_pins->interface_is_interrupt) {
400         // We use the LOW level of the RDYN line as the atmega328 can wakeup from sleep only on LOW
401         // attachInterrupt(a_pins->interrupt_number, m_aci_isr, LOW);
402     }
403 }
404 
hal_aci_tl_send(hal_aci_data_t * p_aci_cmd)405 bool hal_aci_tl_send(hal_aci_data_t *p_aci_cmd)
406 {
407   const uint8_t length = p_aci_cmd->buffer[0];
408   bool ret_val = false;
409 
410   if (length > HAL_ACI_MAX_LENGTH)
411   {
412     return false;
413   }
414 
415   ret_val = aci_queue_enqueue(&aci_tx_q, p_aci_cmd);
416   if (ret_val)
417   {
418     if(!aci_queue_is_full(&aci_rx_q))
419     {
420       // Lower the REQN only when successfully enqueued
421       m_aci_reqn_enable();
422     }
423   }
424 
425   return ret_val;
426 }
427 
spi_readwrite(const uint8_t aci_byte)428 static uint8_t spi_readwrite(const uint8_t aci_byte)
429 {
430     uint8_t reversed, ret;
431     reversed = mraa_spi_write (a_pins_local_ptr->m_spi, REVERSE_BITS (aci_byte));
432     ret = REVERSE_BITS (reversed);
433     return ret;
434 }
435 
hal_aci_tl_rx_q_empty(void)436 bool hal_aci_tl_rx_q_empty (void)
437 {
438   return aci_queue_is_empty(&aci_rx_q);
439 }
440 
hal_aci_tl_rx_q_full(void)441 bool hal_aci_tl_rx_q_full (void)
442 {
443   return aci_queue_is_full(&aci_rx_q);
444 }
445 
hal_aci_tl_tx_q_empty(void)446 bool hal_aci_tl_tx_q_empty (void)
447 {
448   return aci_queue_is_empty(&aci_tx_q);
449 }
450 
hal_aci_tl_tx_q_full(void)451 bool hal_aci_tl_tx_q_full (void)
452 {
453   return aci_queue_is_full(&aci_tx_q);
454 }
455 
hal_aci_tl_q_flush(void)456 void hal_aci_tl_q_flush (void)
457 {
458   m_aci_q_flush();
459 }
460