Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
GridConnect.cxx
Go to the documentation of this file.
1
35#include <unistd.h>
36#include <cerrno>
37
38#include "utils/GridConnect.hxx"
39
40ssize_t GridConnect::encode(struct can_frame *frame, unsigned char buf[])
41{
42 /* allocate a buffer for the Grid connect format */
43 buf[0] = ':';
44
45 ssize_t index;
46
47 /* handle the identifier */
48 if (IS_CAN_FRAME_EFF(*frame))
49 {
50 buf[1] = 'X';
51 uint32_t id = GET_CAN_FRAME_ID_EFF(*frame);
52 for (int i = 9; i >= 2; --i, id >>= 4)
53 {
54 buf[i] = nibble_to_ascii(id);
55 }
56 index = 10;
57 }
58 else
59 {
60 buf[1] = 'S';
61 uint16_t id = GET_CAN_FRAME_ID(*frame);
62 for (int i = 4; i >= 2; --i, id >>= 4)
63 {
64 buf[i] = nibble_to_ascii(id);
65 }
66 index = 5;
67 }
68
69 /* handle remote or normal */
70 if (IS_CAN_FRAME_RTR(*frame))
71 {
72 buf[index] = 'R';
73 }
74 else
75 {
76 buf[index] = 'N';
77 }
78 index++;
79
80 /* handle the data */
81 for (int i = 0; i < frame->can_dlc; i++, index += 2)
82 {
83 buf[index + 0] = nibble_to_ascii(frame->data[i] >> 4);
84 buf[index + 1] = nibble_to_ascii(frame->data[i]);
85 }
86
87 /* stop character */
88 buf[index] = ';';
89 index++;
90
91 return index;
92}
93
94
95
103ssize_t GridConnect::write_generic(int fd, const void *data, size_t len, bool doub)
104{
105 struct can_frame *can_frame = (struct can_frame*)data;
106 size_t remaining = len;
107
108 /* check for len that is multiple of a can_frame size */
109 if ((len % sizeof(struct can_frame)) != 0)
110 {
111 errno = EINVAL;
112 return -1;
113 }
114
115 /* allocate a buffer for the Grid connect format */
116 unsigned char buf[56];
117
118 /* while there are packets to transmit */
119 while (remaining)
120 {
121 ssize_t size = encode(can_frame, buf);
122
123 if (doub)
124 {
125 /* duplicate each byte */
126 for (ssize_t i = (size - 1), j = ((size * 2) - 1);
127 i > 0; --i, j -= 2)
128 {
129 buf[j] = buf[i];
130 buf[j - 1] = buf[i];
131 }
132 buf[0] = buf[1] = '!';
133
134 size *= 2;
135 }
136
137 /* write the formated packet */
138 ssize_t result = ::write(fd, buf, size);
139
140 if (result != size)
141 {
142 return -1;
143 }
144
145 /* get ready for next packet */
146 remaining -= sizeof(struct can_frame);
147 can_frame++;
148 } /* while (remaining) */
149
150 return len;
151}
152
160ssize_t GridConnect::read_generic(int fd, void *data, size_t len, bool doub)
161{
162 struct can_frame *can_frame = (struct can_frame*)data;
163 size_t remaining = len;
164
165 const char SOF = doub ? '!' : ':';
166 const int factor = doub ? 2 : 1;
167
168 /* check for len that is multiple of a can_frame size */
169 if ((len % sizeof(struct can_frame)) != 0)
170 {
171 errno = EINVAL;
172 return -1;
173 }
174
175 /* while there are packets to receive */
176 while (remaining)
177 {
178 /* Though the maximum packet size is 28, we need an extra byte
179 * in the case that we are working with a double encoded frame
180 */
181 char buf[29];
182
184 for ( ; /* until we find a CAN frame */ ; )
185 {
186 do
187 {
188 ssize_t result = ::read(fd, buf, factor);
189 if (result < factor)
190 {
191 return -1;
192 }
193 } while(buf[0] != SOF);
194
195 /* We have a start of frame, now lets get the rest of the packet */
196 int i;
197 for (i = 1; i < 28; ++i)
198 {
199 ssize_t result = ::read(fd, buf + i, factor);
200 if (result < factor)
201 {
202 return -1;
203 }
204 if (buf[i] == ';')
205 {
206 /* found an end of frame */
207 break;
208 }
209 }
210 if (i != 28)
211 {
212 /* we found the end of frame character */
213 break;
214 }
215 } /* for ( ; until we find a CAN frame ; ) */
216
217 int index;
218 /* determine if the frame is standard or extended */
219 if (buf[1] == 'X')
220 {
221 SET_CAN_FRAME_EFF(*can_frame);
222 uint32_t id;
223 id = ascii_pair_to_byte(buf + 2) << 24;
224 id += ascii_pair_to_byte(buf + 4) << 16;
225 id += ascii_pair_to_byte(buf + 6) << 8;
226 id += ascii_pair_to_byte(buf + 8) << 0;
227 SET_CAN_FRAME_ID_EFF(*can_frame, id);
228 index = 10;
229 }
230 else if (buf[1] == 'S')
231 {
232 CLR_CAN_FRAME_EFF(*can_frame);
233 uint16_t id;
234 id = ascii_pair_to_byte(buf + 2) << 4;
235 id |= ascii_pair_to_byte(buf + 3) << 0;
236 SET_CAN_FRAME_ID(*can_frame, id);
237 index = 5;
238 }
239 else
240 {
241 /* unexpected character, try again */
242 continue;
243 }
244
245 /* determine if the frame is normal or remote */
246 if (buf[index] == 'N')
247 {
248 CLR_CAN_FRAME_RTR(*can_frame);
249 }
250 else if (buf[index] == 'R')
251 {
252 SET_CAN_FRAME_RTR(*can_frame);
253 }
254 else
255 {
256 /* unexpected character, try again */
257 continue;
258 }
259 index++;
260
261 /* grab the data */
262 int i;
263 for (i = 0; buf[index] != ';'; index += 2, i++)
264 {
265 can_frame->data[i] = ascii_pair_to_byte(buf + index);
266 }
267 can_frame->can_dlc = i;
268
269 CLR_CAN_FRAME_ERR(*can_frame);
270
271 /* get ready for next packet */
272 remaining -= sizeof(struct can_frame);
273 can_frame++;
274 } /* while (remaining) */
275 return len;
276}
277
static int ascii_pair_to_byte(const char *pair)
Take a pair of ASCII characters and convert them to a byte value.
static ssize_t write_generic(int fd, const void *data, size_t len, bool doub)
Write a CAN packet to Grid Connect interface in single or double format.
static char nibble_to_ascii(int nibble)
Builds an ASCII character representation of a nibble value.
static ssize_t write(int fd, const void *data, size_t len)
Write a CAN packet to Grid Connect interface in single format.
static ssize_t read(int fd, void *data, size_t len)
Read a CAN packet to Grid Connect interface in single format.
static ssize_t encode(struct can_frame *frame, unsigned char buf[])
static ssize_t read_generic(int fd, void *data, size_t len, bool doub)
Read a CAN packet to Grid Connect interface in single or double format.