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