ppp_hdlc.c
Go to the documentation of this file.
1 /**
2  * @file ppp_hdlc.c
3  * @brief PPP HDLC driver
4  *
5  * @section License
6  *
7  * Copyright (C) 2010-2018 Oryx Embedded SARL. All rights reserved.
8  *
9  * This file is part of CycloneTCP Open.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24  *
25  * @author Oryx Embedded SARL (www.oryx-embedded.com)
26  * @version 1.9.0
27  **/
28 
29 //Switch to the appropriate trace level
30 #define TRACE_LEVEL NIC_TRACE_LEVEL
31 
32 //Dependencies
33 #include <stdio.h>
34 #include "core/net.h"
35 #include "ppp/ppp.h"
36 #include "ppp/ppp_hdlc.h"
37 #include "debug.h"
38 
39 //Check TCP/IP stack configuration
40 #if (PPP_SUPPORT == ENABLED)
41 
42 
43 /**
44  * @brief PPP HDLC driver
45  **/
46 
48 {
58  NULL,
59  NULL,
60  NULL,
61  FALSE,
62  FALSE,
63  FALSE,
64  FALSE
65 };
66 
67 
68 /**
69  * @brief PPP HDLC driver initialization
70  * @param[in] interface Underlying network interface
71  * @return Error code
72  **/
73 
75 {
76  PppContext *context;
77 
78  //Debug message
79  TRACE_INFO("Initializing PPP HDLC driver...\r\n");
80 
81  //Point to the PPP context
82  context = interface->pppContext;
83 
84  //Initialize variables
85  context->txBufferLen = 0;
86  context->txWriteIndex = 0;
87  context->txReadIndex = 0;
88  context->rxBufferLen = 0;
89  context->rxWriteIndex = 0;
90  context->rxReadIndex = 0;
91  context->rxFrameCount = 0;
92 
93  //Initialize UART
94  interface->uartDriver->init();
95 
96  //Accept any packets from the upper layer
97  osSetEvent(&interface->nicTxEvent);
98 
99  //Successful initialization
100  return NO_ERROR;
101 }
102 
103 
104 /**
105  * @brief PPP HDLC driver timer handler
106  *
107  * This routine is periodically called by the TCP/IP stack to
108  * handle periodic operations such as polling the link state
109  *
110  * @param[in] interface Underlying network interface
111  **/
112 
114 {
115 }
116 
117 
118 /**
119  * @brief Enable interrupts
120  * @param[in] interface Underlying network interface
121  **/
122 
124 {
125  //Enable UART interrupts
126  interface->uartDriver->enableIrq();
127 }
128 
129 
130 /**
131  * @brief Disable interrupts
132  * @param[in] interface Underlying network interface
133  **/
134 
136 {
137  //USART interrupts are always enabled
138 }
139 
140 
141 /**
142  * @brief PPP HDLC driver event handler
143  * @param[in] interface Underlying network interface
144  **/
145 
147 {
148  PppContext *context;
149 
150  //Point to the PPP context
151  context = interface->pppContext;
152 
153  //Check PPP state
154  if(interface->pppContext->pppPhase != PPP_PHASE_DEAD)
155  {
156  //Process all pending packets
157  while(context->rxFrameCount > 0)
158  {
159  //Read incoming packet
160  pppHdlcDriverReceivePacket(interface);
161 
162  //Enter critical section
163  __disable_irq();
164  //Decrement frame counter
165  context->rxFrameCount--;
166  //Exit critical section
167  __enable_irq();
168  }
169  }
170 }
171 
172 
173 /**
174  * @brief Send a packet
175  * @param[in] interface Underlying network interface
176  * @param[in] buffer Multi-part buffer containing the data to send
177  * @param[in] offset Offset to the first data byte
178  * @return Error code
179  **/
180 
182  const NetBuffer *buffer, size_t offset)
183 {
184  uint_t i;
185  size_t j;
186  size_t n;
187  uint8_t *p;
188  uint16_t protocol;
189  uint32_t accm;
190  PppContext *context;
191 
192  //Point to the PPP context
193  context = interface->pppContext;
194 
195  //Point to the beginning of the frame
196  p = netBufferAt(buffer, offset);
197 
198  //Parse the PPP frame header
200 
201  //Check Protocol field
203  {
204  //Use the ACCM value that has been negotiated
205  accm = context->peerConfig.accm;
206  }
207  else
208  {
209  //Use default ACCM mapping
211  }
212 
213  //Send flag
215 
216  //Loop through data chunks
217  for(i = 0; i < buffer->chunkCount; i++)
218  {
219  //Is there any data to copy from the current chunk?
220  if(offset < buffer->chunk[i].length)
221  {
222  //Point to the first byte to be read
223  p = (uint8_t *) buffer->chunk[i].address + offset;
224  //Compute the number of bytes to copy at a time
225  n = buffer->chunk[i].length - offset;
226 
227  //Copy data to TX queue
228  for(j = 0; j < n; j++)
229  {
230  if(p[j] < PPP_MASK_CHAR)
231  {
232  //Check whether the character is flagged
233  if(accm & (1 << p[j]))
234  {
237  }
238  else
239  {
240  //Enqueue current character
241  pppHdlcDriverWriteTxQueue(context, p[j]);
242  }
243  }
244  else if(p[j] == PPP_ESC_CHAR || p[j] == PPP_FLAG_CHAR)
245  {
248  }
249  else
250  {
251  //Enqueue current character
252  pppHdlcDriverWriteTxQueue(context, p[j]);
253  }
254  }
255 
256  //Process the next block from the start
257  offset = 0;
258  }
259  else
260  {
261  //Skip the current chunk
262  offset -= buffer->chunk[i].length;
263  }
264  }
265 
266  //Send flag
268 
269  //Start transferring data
270  interface->uartDriver->startTx();
271 
272  //Check whether the TX queue is available for writing
273  if(context->txBufferLen <= (PPP_TX_BUFFER_SIZE - 3006))
274  {
275  //The transmitter can accept another packet
276  osSetEvent(&interface->nicTxEvent);
277  }
278 
279  //Data successfully written
280  return NO_ERROR;
281 }
282 
283 
284 /**
285  * @brief Receive a packet
286  * @param[in] interface Underlying network interface
287  * @return Error code
288  **/
289 
291 {
292  size_t n;
293  uint8_t c;
294  bool_t escFlag;
295  uint32_t accm;
296  PppContext *context;
297 
298  //Point to the PPP context
299  context = interface->pppContext;
300  //Retrieve ACCM
301  accm = context->localConfig.accm;
302 
303  //Length of the original PPP frame
304  n = 0;
305  //This flag tells whether the next character is escaped
306  escFlag = FALSE;
307 
308  //The receiver must reverse the octet stuffing procedure
309  while(n < PPP_MAX_FRAME_SIZE && context->rxBufferLen > 0)
310  {
311  //Read a single character
312  c = pppHdlcDriverReadRxQueue(context);
313 
314  if(c < PPP_MASK_CHAR)
315  {
316  //Check whether the character is flagged
317  if(accm & (1 << c))
318  {
319  //The extra characters must be removed from the incoming data stream
320  }
321  else
322  {
323  //Copy current character
324  context->frame[n++] = c;
325  }
326  }
327  else if(c == PPP_ESC_CHAR)
328  {
329  //All occurrences of 0x7D indicate that the next character is escaped
330  escFlag = TRUE;
331  }
332  else if(c == PPP_FLAG_CHAR)
333  {
334  //0x7E flag found
335  break;
336  }
337  else if(escFlag)
338  {
339  //The character is XOR'ed with 0x20
340  context->frame[n++] = c ^ PPP_MASK_CHAR;
341  escFlag = FALSE;
342  }
343  else
344  {
345  //Copy current character
346  context->frame[n++] = c;
347  }
348  }
349 
350  //Check whether a valid PPP frame has been received
351  if(n > 0)
352  {
353  //Debug message
354  TRACE_DEBUG("PPP frame received (%" PRIuSIZE " bytes)...\r\n", n);
355  TRACE_DEBUG_ARRAY(" ", context->frame, n);
356 
357  //Pass the packet to the upper layer
358  nicProcessPacket(interface, context->frame, n);
359  }
360 
361  //Successful read operation
362  return NO_ERROR;
363 }
364 
365 
366 /**
367  * @brief Configure MAC address filtering
368  * @param[in] interface Underlying network interface
369  * @return Error code
370  **/
371 
373 {
374  //Not implemented
375  return NO_ERROR;
376 }
377 
378 
379 /**
380  * @brief Send AT command
381  * @param[in] interface Underlying network interface
382  * @param[in] data NULL-terminated string that contains the AT command to be sent
383  * @return Error code
384  **/
385 
387 {
388  size_t i;
389  PppContext *context;
390 
391  //Point to the PPP context
392  context = interface->pppContext;
393 
394  //Send AT command
395  for(i = 0; data[i] != '\0' && i < 3006; i++)
396  pppHdlcDriverWriteTxQueue(context, data[i]);
397 
398  //Start transferring data
399  interface->uartDriver->startTx();
400 
401  //Check whether the TX queue is available for writing
402  if(context->txBufferLen <= (PPP_TX_BUFFER_SIZE - 3006))
403  {
404  //The transmitter can accept another packet
405  osSetEvent(&interface->nicTxEvent);
406  }
407 
408  //Data successfully written
409  return NO_ERROR;
410 }
411 
412 
413 /**
414  * @brief Wait for an incoming AT command
415  * @param[in] interface Underlying network interface
416  * @param[out] data Buffer where to store the incoming AT command
417  * @param[in] size Size of the buffer, in bytes
418  * @return Error code
419  **/
420 
422 {
423  uint_t i;
424  uint_t k;
425  uint_t n;
426  bool_t valid;
427  PppContext *context;
428 
429  //Point to the PPP context
430  context = interface->pppContext;
431 
432  //Point to the first byte of the receive buffer
433  k = context->rxReadIndex;
434  //Number of characters pending in the receive buffer
435  n = context->rxBufferLen;
436 
437  //Loop through received data
438  for(i = 0, valid = FALSE; i < n && !valid; i++)
439  {
440  //Read current character
441  data[i] = context->rxBuffer[k];
442 
443  //Carriage return?
444  if(data[i] == '\r' || data[i] == '\n')
445  {
446  data[i] = '\0';
447  valid = TRUE;
448  }
449  //Special processing of null-modem connections
450  else if(i >= 5 && !memcmp(data + i - 5, "CLIENT", 6))
451  {
452  data[i + 1] = '\0';
453  valid = TRUE;
454  }
455  else if(i >= 5 && !memcmp(data + i - 5, "SERVER", 6))
456  {
457  data[i + 1] = '\0';
458  valid = TRUE;
459  }
460  //Buffer full?
461  else if(i == (size - 2))
462  {
463  data[i + 1] = '\0';
464  valid = TRUE;
465  }
466 
467  //Increment index and wrap around if necessary
468  if(++k >= PPP_RX_BUFFER_SIZE)
469  k = 0;
470  }
471 
472  //Valid command received?
473  if(valid)
474  {
475  //Advance read index
476  context->rxReadIndex = (context->rxReadIndex + i) % PPP_RX_BUFFER_SIZE;
477 
478  //Enter critical section
479  __disable_irq();
480  //Update the length of the RX buffer
481  context->rxBufferLen -= i;
482  //Exit critical section
483  __enable_irq();
484 
485  //Successful processing
486  return NO_ERROR;
487  }
488  else
489  {
490  //data[i] = '\0';
491  //TRACE_INFO("PPP RX buffer residue (%d bytes)\r\n", i);
492  //TRACE_INFO_ARRAY("# ", data, i);
493  return ERROR_BUFFER_EMPTY;
494  }
495 }
496 
497 
498 /**
499  * @brief Purge TX buffer
500  * @param[in] context Pointer to the PPP context
501  * @return Error code
502  **/
503 
505 {
506  //Enter critical section
507  __disable_irq();
508 
509  //Purge TX buffer
510  context->txBufferLen = 0;
511  context->txWriteIndex = 0;
512  context->txReadIndex = 0;
513 
514  //Exit critical section
515  __enable_irq();
516 
517  //Successful operation
518  return NO_ERROR;
519 }
520 
521 
522 /**
523  * @brief Purge RX buffer
524  * @param[in] context Pointer to the PPP context
525  * @return Error code
526  **/
527 
529 {
530  //Enter critical section
531  __disable_irq();
532 
533  //Purge RX buffer
534  context->rxBufferLen = 0;
535  context->rxWriteIndex = 0;
536  context->rxReadIndex = 0;
537  context->rxFrameCount = 0;
538 
539  //Exit critical section
540  __enable_irq();
541 
542  //Successful operation
543  return NO_ERROR;
544 }
545 
546 
547 /**
548  * @brief Write TX queue
549  * @param[in] context Pointer to the PPP context
550  * @param[in] c Character to be written
551  **/
552 
553 void pppHdlcDriverWriteTxQueue(PppContext *context, uint8_t c)
554 {
555  //Enqueue the character
556  context->txBuffer[context->txWriteIndex] = c;
557 
558  //Increment index and wrap around if necessary
559  if(++context->txWriteIndex >= PPP_TX_BUFFER_SIZE)
560  context->txWriteIndex = 0;
561 
562  //Enter critical section
563  __disable_irq();
564  //Update the length of the queue
565  context->txBufferLen++;
566  //Exit critical section
567  __enable_irq();
568 }
569 
570 
571 /**
572  * @brief Read RX queue
573  * @param[in] context Pointer to the PPP context
574  * @return Character read from the queue
575  **/
576 
578 {
579  uint8_t c;
580 
581  //Read a single character
582  c = context->rxBuffer[context->rxReadIndex];
583 
584  //Increment index and wrap around if necessary
585  if(++context->rxReadIndex >= PPP_RX_BUFFER_SIZE)
586  context->rxReadIndex = 0;
587 
588  //Enter critical section
589  __disable_irq();
590  //Update the length of the queue
591  context->rxBufferLen--;
592  //Exit critical section
593  __enable_irq();
594 
595  //Return the character that has been read
596  return c;
597 }
598 
599 
600 /**
601  * @brief Read TX queue
602  * @param[in] interface Underlying network interface
603  * @param[out] c Character read from the queue
604  * @return TRUE if a context switch is required
605  **/
606 
608 {
609  bool_t flag;
610  PppContext *context;
611 
612  //Point to the PPP context
613  context = interface->pppContext;
614  //This flag will be set if a higher priority task must be woken
615  flag = FALSE;
616 
617  //Any data pending in the TX queue?
618  if(context->txBufferLen > 0)
619  {
620  //Read a single character
621  *c = context->txBuffer[context->txReadIndex];
622 
623  //Increment index and wrap around if necessary
624  if(++context->txReadIndex >= PPP_TX_BUFFER_SIZE)
625  context->txReadIndex = 0;
626 
627  //Update the length of the queue
628  context->txBufferLen--;
629 
630  //Check whether the TX is available for writing
631  if(context->txBufferLen == (PPP_TX_BUFFER_SIZE - 3006))
632  {
633  flag = osSetEventFromIsr(&interface->nicTxEvent);
634  }
635  }
636  else
637  {
638  //The TX queue is empty
639  *c = EOF;
640  }
641 
642  //The return value tells whether a context switch is required
643  return flag;
644 }
645 
646 
647 /**
648  * @brief Write RX queue
649  * @param[in] interface Underlying network interface
650  * @param[in] c Character to be written
651  * @return TRUE if a context switch is required
652  **/
653 
655 {
656  bool_t flag;
657  PppContext *context;
658 
659  //Point to the PPP context
660  context = interface->pppContext;
661  //This flag will be set if a higher priority task must be woken
662  flag = FALSE;
663 
664  //Make sure the RX queue is not full
665  if(context->rxBufferLen < PPP_RX_BUFFER_SIZE)
666  {
667  //Enqueue the character
668  context->rxBuffer[context->rxWriteIndex] = c;
669 
670  //Increment index and wrap around if necessary
671  if(++context->rxWriteIndex >= PPP_RX_BUFFER_SIZE)
672  context->rxWriteIndex = 0;
673 
674  //Update the length of the queue
675  context->rxBufferLen++;
676 
677  //0x7E flag found?
678  if(c == PPP_FLAG_CHAR)
679  {
680  //Increment frame counter
681  context->rxFrameCount++;
682 
683  //A complete HDLC frame has been received
684  interface->nicEvent = TRUE;
685  //Notify the TCP/IP stack of the event
686  flag = osSetEventFromIsr(&netEvent);
687  }
688  }
689 
690  //The return value tells whether a context switch is required
691  return flag;
692 }
693 
694 #endif
695 
error_t pppHdlcDriverSendPacket(NetInterface *interface, const NetBuffer *buffer, size_t offset)
Send a packet.
Definition: ppp_hdlc.c:181
PPP (Point-to-Point Protocol)
void pppHdlcDriverEnableIrq(NetInterface *interface)
Enable interrupts.
Definition: ppp_hdlc.c:123
error_t pppHdlcDriverInit(NetInterface *interface)
PPP HDLC driver initialization.
Definition: ppp_hdlc.c:74
char char_t
Definition: compiler_port.h:41
uint8_t c
Definition: ndp.h:510
TCP/IP stack core.
uint32_t accm
Definition: lcp.h:84
Debugging facilities.
uint8_t p
Definition: ndp.h:295
#define PPP_FLAG_CHAR
Definition: ppp.h:123
void pppHdlcDriverWriteTxQueue(PppContext *context, uint8_t c)
Write TX queue.
Definition: ppp_hdlc.c:553
error_t pppHdlcDriverReceivePacket(NetInterface *interface)
Receive a packet.
Definition: ppp_hdlc.c:290
uint8_t pppHdlcDriverReadRxQueue(PppContext *context)
Read RX queue.
Definition: ppp_hdlc.c:577
PPP interface.
Definition: nic.h:70
PPP HDLC driver.
#define TRACE_DEBUG_ARRAY(p, a, n)
Definition: debug.h:99
void * address
Definition: net_mem.h:76
#define PPP_MASK_CHAR
Definition: ppp.h:121
#define TRUE
Definition: os_port.h:48
void pppHdlcDriverDisableIrq(NetInterface *interface)
Disable interrupts.
Definition: ppp_hdlc.c:135
ChunkDesc chunk[]
Definition: net_mem.h:90
error_t pppHdlcDriverPurgeTxBuffer(PppContext *context)
Purge TX buffer.
Definition: ppp_hdlc.c:504
signed int int_t
Definition: compiler_port.h:42
#define PPP_DEFAULT_MRU
Definition: ppp.h:126
void * netBufferAt(const NetBuffer *buffer, size_t offset)
Returns a pointer to the data at the specified position.
Definition: net_mem.c:411
#define PPP_FRAME_HEADER_SIZE
Definition: ppp.h:141
#define PPP_RX_BUFFER_SIZE
Definition: ppp.h:59
const NicDriver pppHdlcDriver
PPP HDLC driver.
Definition: ppp_hdlc.c:47
error_t pppHdlcDriverReceiveAtCommand(NetInterface *interface, char_t *data, size_t size)
Wait for an incoming AT command.
Definition: ppp_hdlc.c:421
size_t pppParseFrameHeader(const uint8_t *frame, size_t length, uint16_t *protocol)
Parse PPP frame header.
Definition: ppp.c:1106
NIC driver.
Definition: nic.h:161
void pppHdlcDriverEventHandler(NetInterface *interface)
PPP HDLC driver event handler.
Definition: ppp_hdlc.c:146
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:86
bool_t osSetEventFromIsr(OsEvent *event)
Set an event object to the signaled state from an interrupt service routine.
#define PPP_ESC_CHAR
Definition: ppp.h:122
bool_t pppHdlcDriverReadTxQueue(NetInterface *interface, int_t *c)
Read TX queue.
Definition: ppp_hdlc.c:607
#define PppContext
Definition: ppp.h:36
#define TRACE_INFO(...)
Definition: debug.h:86
Success.
Definition: error.h:42
OsEvent netEvent
Definition: net.c:72
uint16_t length
Definition: net_mem.h:77
void nicProcessPacket(NetInterface *interface, void *packet, size_t length)
Handle a packet received by the network controller.
Definition: nic.c:239
void osSetEvent(OsEvent *event)
Set the specified event object to the signaled state.
error_t
Error codes.
Definition: error.h:40
unsigned int uint_t
Definition: compiler_port.h:43
uint8_t data[]
Definition: dtls_misc.h:167
#define PRIuSIZE
Definition: compiler_port.h:72
#define NetInterface
Definition: net.h:34
Internet Protocol.
Definition: ppp.h:197
void pppHdlcDriverTick(NetInterface *interface)
PPP HDLC driver timer handler.
Definition: ppp_hdlc.c:113
error_t pppHdlcDriverUpdateMacAddrFilter(NetInterface *interface)
Configure MAC address filtering.
Definition: ppp_hdlc.c:372
bool_t pppHdlcDriverWriteRxQueue(NetInterface *interface, uint8_t c)
Write RX queue.
Definition: ppp_hdlc.c:654
error_t pppHdlcDriverPurgeRxBuffer(PppContext *context)
Purge RX buffer.
Definition: ppp_hdlc.c:528
#define PPP_TX_BUFFER_SIZE
Definition: ppp.h:52
Internet Protocol version 6.
Definition: ppp.h:198
Link dead.
Definition: ppp.h:164
uint8_t protocol
uint8_t length
Definition: dtls_misc.h:140
uint8_t n
#define PPP_DEFAULT_ACCM
Definition: ppp.h:128
#define FALSE
Definition: os_port.h:44
int bool_t
Definition: compiler_port.h:47
error_t pppHdlcDriverSendAtCommand(NetInterface *interface, const char_t *data)
Send AT command.
Definition: ppp_hdlc.c:386
uint_t chunkCount
Definition: net_mem.h:88
#define TRACE_DEBUG(...)
Definition: debug.h:98