786dd85db9a9539a890bed72ff53e438bac91fed
[contrib/upm.git] / src / sm130 / sm130.cxx
1 /*
2  * Author: Yevgeniy Kiveisha <yevgeniy.kiveisha@intel.com>
3  * Copyright (c) 2014 Intel Corporation.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining
6  * a copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be
14  * included in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24
25 #include <iostream>
26 #include <unistd.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include "sm130.h"
31
32 using namespace upm;
33
34 struct SM130Exception : public std::exception {
35     std::string message;
36     SM130Exception (std::string msg) : message (msg) { }
37     ~SM130Exception () throw () { }
38     const char* what() const throw () { return message.c_str(); }
39 };
40
41 SM130::SM130 (int bus, int devAddr, int rst, int dready) {
42     mraa_result_t error = MRAA_SUCCESS;
43
44     this->m_name = "SM130";
45
46     this->m_i2cAddr = devAddr;
47     this->m_bus = bus;
48
49     this->m_i2Ctx = mraa_i2c_init(this->m_bus);
50
51     mraa_result_t ret = mraa_i2c_address(this->m_i2Ctx, this->m_i2cAddr);
52     if (ret != MRAA_SUCCESS) {
53         throw SM130Exception ("Couldn't initilize I2C.");
54     }
55
56     this->m_resetPinCtx = mraa_gpio_init (rst);
57     if (m_resetPinCtx == NULL) {
58         throw SM130Exception ("Couldn't initilize RESET pin.");
59     }
60
61     this->m_dataReadyPinCtx = mraa_gpio_init (dready);
62     if (m_dataReadyPinCtx == NULL) {
63         throw SM130Exception ("Couldn't initilize DATA READY pin.");
64     }
65
66     error = mraa_gpio_dir (this->m_resetPinCtx, MRAA_GPIO_OUT);
67     if (error != MRAA_SUCCESS) {
68         throw SM130Exception ("Couldn't set direction for RESET pin.");
69     }
70
71     error = mraa_gpio_dir (this->m_dataReadyPinCtx, MRAA_GPIO_OUT);
72     if (error != MRAA_SUCCESS) {
73         throw SM130Exception ("Couldn't set direction for DATA READY pin.");
74     }
75 }
76
77 SM130::~SM130 () {
78     mraa_result_t error = MRAA_SUCCESS;
79
80     error = mraa_i2c_stop(this->m_i2Ctx);
81     if (error != MRAA_SUCCESS) {
82         mraa_result_print(error);
83     }
84     error = mraa_gpio_close (this->m_resetPinCtx);
85     if (error != MRAA_SUCCESS) {
86         mraa_result_print(error);
87     }
88     error = mraa_gpio_close (this->m_dataReadyPinCtx);
89     if (error != MRAA_SUCCESS) {
90         mraa_result_print(error);
91     }
92 }
93
94 const char*
95 SM130::getFirmwareVersion () {
96     // Send VERSION command and retry a few times if no response
97     for (uint8_t n = 0; n < 10; n++) {
98         sendCommand (CMD_VERSION);
99         if (available() && getCommand() == CMD_VERSION)
100         //  return versionString;
101         usleep(100 * 1000);
102     }
103
104     return 0;
105 }
106
107 uint8_t
108 SM130::available () {
109     // If in SEEK mode and using DREADY pin, check the status
110     if (this->m_LastCMD == CMD_SEEK_TAG) {
111         if (!mraa_gpio_read(this->m_dataReadyPinCtx)) {
112             return false;
113         }
114     }
115
116     // Set the maximum length of the expected response packet
117     uint8_t len;
118     switch(this->m_LastCMD) {
119         case CMD_ANTENNA_POWER:
120         case CMD_AUTHENTICATE:
121         case CMD_DEC_VALUE:
122         case CMD_INC_VALUE:
123         case CMD_WRITE_KEY:
124         case CMD_HALT_TAG:
125         case CMD_SLEEP:
126             len = 4;
127             break;
128         case CMD_WRITE4:
129         case CMD_WRITE_VALUE:
130         case CMD_READ_VALUE:
131             len = 8;
132         case CMD_SEEK_TAG:
133         case CMD_SELECT_TAG:
134             len = 11;
135             break;
136         default:
137             len = SIZE_PACKET;
138     }
139
140     // If valid data received, process the response packet
141     if (this->i2cRecievePacket(len) > 0) {
142         // Init response variables
143         this->m_TagType = this->m_TagLength = *this->m_TagString = 0;
144
145         // If packet length is 2, the command failed. Set error code.
146         errorCode = this->getPacketLength () < 3 ? this->m_Data[2] : 0;
147
148         // Process command response
149         switch (this->getCommand ()) {
150             case CMD_RESET:
151             case CMD_VERSION:
152                 // RESET and VERSION commands produce the firmware version
153                 len = std::min ((unsigned int) getPacketLength (), sizeof (this->m_Version)) - 1;
154                 memcpy(this->m_Version, this->m_Data + 2, len);
155                 this->m_Version[len] = 0;
156                 break;
157
158             case CMD_SEEK_TAG:
159             case CMD_SELECT_TAG:
160                 // If no error, get tag number
161                 if(errorCode == 0 && this->getPacketLength () >= 6)
162                 {
163                     this->m_TagLength = this->getPacketLength () - 2;
164                     this->m_TagType = this->m_Data[2];
165                     memcpy(this->m_TagNumber, this->m_Data + 3, this->m_TagLength);
166                     this->arrayToHex (this->m_TagString, this->m_TagNumber, this->m_TagLength);
167                 }
168                 break;
169
170             case CMD_AUTHENTICATE:
171                 break;
172
173             case CMD_READ16:
174                 break;
175
176             case CMD_WRITE16:
177             case CMD_WRITE4:
178                 break;
179
180             case CMD_ANTENNA_POWER:
181                 errorCode = 0;
182                 antennaPower = this->m_Data[2];
183                 break;
184
185             case CMD_SLEEP:
186                 // If in SLEEP mode, no data is available
187                 return false;
188         }
189
190         // Data available
191         return true;
192     }
193     // No data available
194     return false;
195 }
196
197 uint16_t
198 SM130::i2cRecievePacket (uint32_t len) {
199     int readByte = 0;
200
201     mraa_i2c_address(this->m_i2Ctx, this->m_i2cAddr);
202     readByte = mraa_i2c_read(this->m_i2Ctx, this->m_Data, len);
203
204     if (readByte > 0) {
205         // verify checksum if length > 0 and <= SIZE_PAYLOAD
206         if (this->m_Data[0] > 0 && this->m_Data[0] <= SIZE_PAYLOAD)
207         {
208             uint8_t i, sum;
209             for (i = 0, sum = 0; i <= this->m_Data[0]; i++) {
210                 sum += this->m_Data[i];
211             }
212             // return with length of response, or -1 if invalid checksum
213             return sum == this->m_Data[i] ? this->m_Data[0] : -1;
214         }
215     }
216
217     return readByte;
218 }
219
220 void
221 SM130::arrayToHex (char *s, uint8_t array[], uint8_t len) {
222     for (uint8_t i = 0; i < len; i++) {
223         *s++ = toHex(array[i] >> 4);
224         *s++ = toHex(array[i]);
225     }
226
227     *s = 0;
228 }
229
230 char
231 SM130::toHex (uint8_t b) {
232     b = b & 0x0f;
233
234     return b < 10 ? b + '0' : b + 'A' - 10;
235 }
236
237 mraa_result_t
238 SM130::i2cTransmitPacket (uint32_t len) {
239     mraa_result_t error = MRAA_SUCCESS;
240     uint8_t sum = 0;
241
242     // Save last command
243     this->m_LastCMD = this->m_Data[0];
244
245     // calculate the sum check
246     for (int i = 0; i < len; i++) {
247         sum += this->m_Data[i];
248     }
249
250     // placing the sum check to the last byte of the packet
251     this->m_Data[len + 1] = sum;
252
253     error = mraa_i2c_address (this->m_i2Ctx, this->m_i2cAddr);
254     error = mraa_i2c_write (this->m_i2Ctx, this->m_Data, len + 1);
255
256     return error;
257 }
258
259 mraa_result_t
260 SM130::sendCommand (uint8_t cmd) {
261     this->m_Data[0] = 1;
262     this->m_Data[1] = cmd;
263     this->i2cTransmitPacket(2);
264 }
265