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-2019 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 1.9.6
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  * @return Error code
181  **/
182 
184  const NetBuffer *buffer, size_t offset)
185 {
186  uint_t i;
187  size_t j;
188  size_t n;
189  uint8_t *p;
190  uint16_t protocol;
191  uint32_t accm;
192  PppContext *context;
193 
194  //Point to the PPP context
195  context = interface->pppContext;
196 
197  //Point to the beginning of the frame
198  p = netBufferAt(buffer, offset);
199 
200  //Parse the PPP frame header
202 
203  //Check Protocol field
205  {
206  //Use the ACCM value that has been negotiated
207  accm = context->peerConfig.accm;
208  }
209  else
210  {
211  //Use default ACCM mapping
213  }
214 
215  //Send flag
217 
218  //Loop through data chunks
219  for(i = 0; i < buffer->chunkCount; i++)
220  {
221  //Is there any data to copy from the current chunk?
222  if(offset < buffer->chunk[i].length)
223  {
224  //Point to the first byte to be read
225  p = (uint8_t *) buffer->chunk[i].address + offset;
226  //Compute the number of bytes to copy at a time
227  n = buffer->chunk[i].length - offset;
228 
229  //Copy data to TX queue
230  for(j = 0; j < n; j++)
231  {
232  if(p[j] < PPP_MASK_CHAR)
233  {
234  //Check whether the character is flagged
235  if(accm & (1 << p[j]))
236  {
239  }
240  else
241  {
242  //Enqueue current character
243  pppHdlcDriverWriteTxQueue(context, p[j]);
244  }
245  }
246  else if(p[j] == PPP_ESC_CHAR || p[j] == PPP_FLAG_CHAR)
247  {
250  }
251  else
252  {
253  //Enqueue current character
254  pppHdlcDriverWriteTxQueue(context, p[j]);
255  }
256  }
257 
258  //Process the next block from the start
259  offset = 0;
260  }
261  else
262  {
263  //Skip the current chunk
264  offset -= buffer->chunk[i].length;
265  }
266  }
267 
268  //Send flag
270 
271  //Start transferring data
272  interface->uartDriver->startTx();
273 
274  //Check whether the TX queue is available for writing
275  if(context->txBufferLen <= (PPP_TX_BUFFER_SIZE - 3006))
276  {
277  //The transmitter can accept another packet
278  osSetEvent(&interface->nicTxEvent);
279  }
280 
281  //Data successfully written
282  return NO_ERROR;
283 }
284 
285 
286 /**
287  * @brief Receive a packet
288  * @param[in] interface Underlying network interface
289  * @return Error code
290  **/
291 
293 {
294  size_t n;
295  uint8_t c;
296  bool_t escFlag;
297  uint32_t accm;
298  PppContext *context;
299 
300  //Point to the PPP context
301  context = interface->pppContext;
302  //Retrieve ACCM
303  accm = context->localConfig.accm;
304 
305  //Length of the original PPP frame
306  n = 0;
307  //This flag tells whether the next character is escaped
308  escFlag = FALSE;
309 
310  //The receiver must reverse the octet stuffing procedure
311  while(n < PPP_MAX_FRAME_SIZE && context->rxBufferLen > 0)
312  {
313  //Read a single character
314  c = pppHdlcDriverReadRxQueue(context);
315 
316  if(c < PPP_MASK_CHAR)
317  {
318  //Check whether the character is flagged
319  if(accm & (1 << c))
320  {
321  //The extra characters must be removed from the incoming data stream
322  }
323  else
324  {
325  //Copy current character
326  context->frame[n++] = c;
327  }
328  }
329  else if(c == PPP_ESC_CHAR)
330  {
331  //All occurrences of 0x7D indicate that the next character is escaped
332  escFlag = TRUE;
333  }
334  else if(c == PPP_FLAG_CHAR)
335  {
336  //0x7E flag found
337  break;
338  }
339  else if(escFlag)
340  {
341  //The character is XOR'ed with 0x20
342  context->frame[n++] = c ^ PPP_MASK_CHAR;
343  escFlag = FALSE;
344  }
345  else
346  {
347  //Copy current character
348  context->frame[n++] = c;
349  }
350  }
351 
352  //Check whether a valid PPP frame has been received
353  if(n > 0)
354  {
355  //Debug message
356  TRACE_DEBUG("PPP frame received (%" PRIuSIZE " bytes)...\r\n", n);
357  TRACE_DEBUG_ARRAY(" ", context->frame, n);
358 
359  //Pass the packet to the upper layer
360  nicProcessPacket(interface, context->frame, n);
361  }
362 
363  //Successful read operation
364  return NO_ERROR;
365 }
366 
367 
368 /**
369  * @brief Configure MAC address filtering
370  * @param[in] interface Underlying network interface
371  * @return Error code
372  **/
373 
375 {
376  //Not implemented
377  return NO_ERROR;
378 }
379 
380 
381 /**
382  * @brief Send AT command
383  * @param[in] interface Underlying network interface
384  * @param[in] data NULL-terminated string that contains the AT command to be sent
385  * @return Error code
386  **/
387 
389 {
390  size_t i;
391  PppContext *context;
392 
393  //Point to the PPP context
394  context = interface->pppContext;
395 
396  //Send AT command
397  for(i = 0; data[i] != '\0' && i < 3006; i++)
398  pppHdlcDriverWriteTxQueue(context, data[i]);
399 
400  //Start transferring data
401  interface->uartDriver->startTx();
402 
403  //Check whether the TX queue is available for writing
404  if(context->txBufferLen <= (PPP_TX_BUFFER_SIZE - 3006))
405  {
406  //The transmitter can accept another packet
407  osSetEvent(&interface->nicTxEvent);
408  }
409 
410  //Data successfully written
411  return NO_ERROR;
412 }
413 
414 
415 /**
416  * @brief Wait for an incoming AT command
417  * @param[in] interface Underlying network interface
418  * @param[out] data Buffer where to store the incoming AT command
419  * @param[in] size Size of the buffer, in bytes
420  * @return Error code
421  **/
422 
424 {
425  uint_t i;
426  uint_t k;
427  uint_t n;
428  bool_t valid;
429  PppContext *context;
430 
431  //Point to the PPP context
432  context = interface->pppContext;
433 
434  //Point to the first byte of the receive buffer
435  k = context->rxReadIndex;
436  //Number of characters pending in the receive buffer
437  n = context->rxBufferLen;
438 
439  //Loop through received data
440  for(i = 0, valid = FALSE; i < n && !valid; i++)
441  {
442  //Read current character
443  data[i] = context->rxBuffer[k];
444 
445  //Carriage return?
446  if(data[i] == '\r' || data[i] == '\n')
447  {
448  data[i] = '\0';
449  valid = TRUE;
450  }
451  //Special processing of null-modem connections
452  else if(i >= 5 && !memcmp(data + i - 5, "CLIENT", 6))
453  {
454  data[i + 1] = '\0';
455  valid = TRUE;
456  }
457  else if(i >= 5 && !memcmp(data + i - 5, "SERVER", 6))
458  {
459  data[i + 1] = '\0';
460  valid = TRUE;
461  }
462  //Buffer full?
463  else if(i == (size - 2))
464  {
465  data[i + 1] = '\0';
466  valid = TRUE;
467  }
468 
469  //Increment index and wrap around if necessary
470  if(++k >= PPP_RX_BUFFER_SIZE)
471  k = 0;
472  }
473 
474  //Valid command received?
475  if(valid)
476  {
477  //Advance read index
478  context->rxReadIndex = (context->rxReadIndex + i) % PPP_RX_BUFFER_SIZE;
479 
480  //Enter critical section
481  __disable_irq();
482  //Update the length of the RX buffer
483  context->rxBufferLen -= i;
484  //Exit critical section
485  __enable_irq();
486 
487  //Successful processing
488  return NO_ERROR;
489  }
490  else
491  {
492  //data[i] = '\0';
493  //TRACE_INFO("PPP RX buffer residue (%d bytes)\r\n", i);
494  //TRACE_INFO_ARRAY("# ", data, i);
495  return ERROR_BUFFER_EMPTY;
496  }
497 }
498 
499 
500 /**
501  * @brief Purge TX buffer
502  * @param[in] context Pointer to the PPP context
503  * @return Error code
504  **/
505 
507 {
508  //Enter critical section
509  __disable_irq();
510 
511  //Purge TX buffer
512  context->txBufferLen = 0;
513  context->txWriteIndex = 0;
514  context->txReadIndex = 0;
515 
516  //Exit critical section
517  __enable_irq();
518 
519  //Successful operation
520  return NO_ERROR;
521 }
522 
523 
524 /**
525  * @brief Purge RX buffer
526  * @param[in] context Pointer to the PPP context
527  * @return Error code
528  **/
529 
531 {
532  //Enter critical section
533  __disable_irq();
534 
535  //Purge RX buffer
536  context->rxBufferLen = 0;
537  context->rxWriteIndex = 0;
538  context->rxReadIndex = 0;
539  context->rxFrameCount = 0;
540 
541  //Exit critical section
542  __enable_irq();
543 
544  //Successful operation
545  return NO_ERROR;
546 }
547 
548 
549 /**
550  * @brief Write TX queue
551  * @param[in] context Pointer to the PPP context
552  * @param[in] c Character to be written
553  **/
554 
555 void pppHdlcDriverWriteTxQueue(PppContext *context, uint8_t c)
556 {
557  //Enqueue the character
558  context->txBuffer[context->txWriteIndex] = c;
559 
560  //Increment index and wrap around if necessary
561  if(++context->txWriteIndex >= PPP_TX_BUFFER_SIZE)
562  context->txWriteIndex = 0;
563 
564  //Enter critical section
565  __disable_irq();
566  //Update the length of the queue
567  context->txBufferLen++;
568  //Exit critical section
569  __enable_irq();
570 }
571 
572 
573 /**
574  * @brief Read RX queue
575  * @param[in] context Pointer to the PPP context
576  * @return Character read from the queue
577  **/
578 
580 {
581  uint8_t c;
582 
583  //Read a single character
584  c = context->rxBuffer[context->rxReadIndex];
585 
586  //Increment index and wrap around if necessary
587  if(++context->rxReadIndex >= PPP_RX_BUFFER_SIZE)
588  context->rxReadIndex = 0;
589 
590  //Enter critical section
591  __disable_irq();
592  //Update the length of the queue
593  context->rxBufferLen--;
594  //Exit critical section
595  __enable_irq();
596 
597  //Return the character that has been read
598  return c;
599 }
600 
601 
602 /**
603  * @brief Read TX queue
604  * @param[in] interface Underlying network interface
605  * @param[out] c Character read from the queue
606  * @return TRUE if a context switch is required
607  **/
608 
610 {
611  bool_t flag;
612  PppContext *context;
613 
614  //Point to the PPP context
615  context = interface->pppContext;
616  //This flag will be set if a higher priority task must be woken
617  flag = FALSE;
618 
619  //Any data pending in the TX queue?
620  if(context->txBufferLen > 0)
621  {
622  //Read a single character
623  *c = context->txBuffer[context->txReadIndex];
624 
625  //Increment index and wrap around if necessary
626  if(++context->txReadIndex >= PPP_TX_BUFFER_SIZE)
627  context->txReadIndex = 0;
628 
629  //Update the length of the queue
630  context->txBufferLen--;
631 
632  //Check whether the TX is available for writing
633  if(context->txBufferLen == (PPP_TX_BUFFER_SIZE - 3006))
634  {
635  flag = osSetEventFromIsr(&interface->nicTxEvent);
636  }
637  }
638  else
639  {
640  //The TX queue is empty
641  *c = EOF;
642  }
643 
644  //The return value tells whether a context switch is required
645  return flag;
646 }
647 
648 
649 /**
650  * @brief Write RX queue
651  * @param[in] interface Underlying network interface
652  * @param[in] c Character to be written
653  * @return TRUE if a context switch is required
654  **/
655 
657 {
658  bool_t flag;
659  PppContext *context;
660 
661  //Point to the PPP context
662  context = interface->pppContext;
663  //This flag will be set if a higher priority task must be woken
664  flag = FALSE;
665 
666  //Make sure the RX queue is not full
667  if(context->rxBufferLen < PPP_RX_BUFFER_SIZE)
668  {
669  //Enqueue the character
670  context->rxBuffer[context->rxWriteIndex] = c;
671 
672  //Increment index and wrap around if necessary
673  if(++context->rxWriteIndex >= PPP_RX_BUFFER_SIZE)
674  context->rxWriteIndex = 0;
675 
676  //Update the length of the queue
677  context->rxBufferLen++;
678 
679  //0x7E flag found?
680  if(c == PPP_FLAG_CHAR)
681  {
682  //Increment frame counter
683  context->rxFrameCount++;
684 
685  //A complete HDLC frame has been received
686  interface->nicEvent = TRUE;
687  //Notify the TCP/IP stack of the event
688  flag = osSetEventFromIsr(&netEvent);
689  }
690  }
691 
692  //The return value tells whether a context switch is required
693  return flag;
694 }
695 
696 #endif
697 
bool_t osSetEventFromIsr(OsEvent *event)
Set an event object to the signaled state from an interrupt service routine.
uint8_t length
Definition: dtls_misc.h:149
int bool_t
Definition: compiler_port.h:49
signed int int_t
Definition: compiler_port.h:44
#define PPP_RX_BUFFER_SIZE
Definition: ppp.h:61
error_t pppHdlcDriverReceivePacket(NetInterface *interface)
Receive a packet.
Definition: ppp_hdlc.c:292
uint8_t p
Definition: ndp.h:298
void nicProcessPacket(NetInterface *interface, uint8_t *packet, size_t length)
Handle a packet received by the network controller.
Definition: nic.c:383
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:88
uint_t chunkCount
Definition: net_mem.h:90
void pppHdlcDriverEventHandler(NetInterface *interface)
PPP HDLC driver event handler.
Definition: ppp_hdlc.c:148
#define TRUE
Definition: os_port.h:50
void pppHdlcDriverDisableIrq(NetInterface *interface)
Disable interrupts.
Definition: ppp_hdlc.c:137
bool_t pppHdlcDriverWriteRxQueue(NetInterface *interface, uint8_t c)
Write RX queue.
Definition: ppp_hdlc.c:656
#define PPP_MASK_CHAR
Definition: ppp.h:123
error_t pppHdlcDriverReceiveAtCommand(NetInterface *interface, char_t *data, size_t size)
Wait for an incoming AT command.
Definition: ppp_hdlc.c:423
#define PppContext
Definition: ppp.h:38
#define PPP_TX_BUFFER_SIZE
Definition: ppp.h:54
error_t pppHdlcDriverSendPacket(NetInterface *interface, const NetBuffer *buffer, size_t offset)
Send a packet.
Definition: ppp_hdlc.c:183
uint16_t length
Definition: net_mem.h:79
#define FALSE
Definition: os_port.h:46
#define PPP_ESC_CHAR
Definition: ppp.h:124
error_t
Error codes.
Definition: error.h:42
error_t pppHdlcDriverSendAtCommand(NetInterface *interface, const char_t *data)
Send AT command.
Definition: ppp_hdlc.c:388
uint8_t protocol
#define PPP_DEFAULT_ACCM
Definition: ppp.h:130
void * netBufferAt(const NetBuffer *buffer, size_t offset)
Returns a pointer to the data at the specified position.
Definition: net_mem.c:413
void * address
Definition: net_mem.h:78
#define NetInterface
Definition: net.h:36
error_t pppHdlcDriverPurgeRxBuffer(PppContext *context)
Purge RX buffer.
Definition: ppp_hdlc.c:530
size_t pppParseFrameHeader(const uint8_t *frame, size_t length, uint16_t *protocol)
Parse PPP frame header.
Definition: ppp.c:1108
@ ERROR_BUFFER_EMPTY
Definition: error.h:139
@ PPP_PHASE_DEAD
Link dead.
Definition: ppp.h:166
OsEvent netEvent
Definition: net.c:77
#define PPP_FRAME_HEADER_SIZE
Definition: ppp.h:143
@ PPP_PROTOCOL_IPV6
Internet Protocol version 6.
Definition: ppp.h:200
#define TRACE_INFO(...)
Definition: debug.h:94
error_t pppHdlcDriverUpdateMacAddrFilter(NetInterface *interface)
Configure MAC address filtering.
Definition: ppp_hdlc.c:374
@ NIC_TYPE_PPP
PPP interface.
Definition: nic.h:80
#define TRACE_DEBUG(...)
Definition: debug.h:106
char char_t
Definition: compiler_port.h:43
uint32_t accm
Definition: lcp.h:86
error_t pppHdlcDriverPurgeTxBuffer(PppContext *context)
Purge TX buffer.
Definition: ppp_hdlc.c:506
ChunkDesc chunk[]
Definition: net_mem.h:92
#define TRACE_DEBUG_ARRAY(p, a, n)
Definition: debug.h:107
void pppHdlcDriverEnableIrq(NetInterface *interface)
Enable interrupts.
Definition: ppp_hdlc.c:125
uint8_t n
#define PPP_FLAG_CHAR
Definition: ppp.h:125
const NicDriver pppHdlcDriver
PPP HDLC driver.
Definition: ppp_hdlc.c:49
PPP (Point-to-Point Protocol)
uint8_t pppHdlcDriverReadRxQueue(PppContext *context)
Read RX queue.
Definition: ppp_hdlc.c:579
bool_t pppHdlcDriverReadTxQueue(NetInterface *interface, int_t *c)
Read TX queue.
Definition: ppp_hdlc.c:609
void osSetEvent(OsEvent *event)
Set the specified event object to the signaled state.
error_t pppHdlcDriverInit(NetInterface *interface)
PPP HDLC driver initialization.
Definition: ppp_hdlc.c:76
PPP HDLC driver.
@ PPP_PROTOCOL_IP
Internet Protocol.
Definition: ppp.h:199
#define PRIuSIZE
Definition: compiler_port.h:78
unsigned int uint_t
Definition: compiler_port.h:45
TCP/IP stack core.
uint8_t data[]
Definition: dtls_misc.h:176
NIC driver.
Definition: nic.h:179
void pppHdlcDriverWriteTxQueue(PppContext *context, uint8_t c)
Write TX queue.
Definition: ppp_hdlc.c:555
#define PPP_DEFAULT_MRU
Definition: ppp.h:128
void pppHdlcDriverTick(NetInterface *interface)
PPP HDLC driver timer handler.
Definition: ppp_hdlc.c:115
@ NO_ERROR
Success.
Definition: error.h:44
uint8_t c
Definition: ndp.h:513
Debugging facilities.