ksz8895_driver.c
Go to the documentation of this file.
1 /**
2  * @file ksz8895_driver.c
3  * @brief KSZ8895 5-port Ethernet switch driver
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2020 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.8
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL NIC_TRACE_LEVEL
33 
34 //Dependencies
35 #include "core/net.h"
36 #include "core/ethernet_misc.h"
38 #include "debug.h"
39 
40 
41 /**
42  * @brief KSZ8895 Ethernet switch driver
43  **/
44 
46 {
67 };
68 
69 
70 /**
71  * @brief Tail tag rules (host to KSZ8895)
72  **/
73 
74 const uint8_t ksz8895IngressTailTag[5] =
75 {
81 };
82 
83 
84 /**
85  * @brief KSZ8895 Ethernet switch initialization
86  * @param[in] interface Underlying network interface
87  * @return Error code
88  **/
89 
91 {
92  uint_t port;
93  uint8_t temp;
94 
95  //Debug message
96  TRACE_INFO("Initializing KSZ8895...\r\n");
97 
98  //SPI slave mode?
99  if(interface->spiDriver != NULL)
100  {
101  //Initialize SPI interface
102  interface->spiDriver->init();
103  }
104  else if(interface->smiDriver != NULL)
105  {
106  //Initialize serial management interface
107  interface->smiDriver->init();
108  }
109  else
110  {
111  //Just for sanity
112  }
113 
114  //Wait for the serial interface to be ready
115  do
116  {
117  //Read CHIP_ID0 register
118  temp = ksz8895ReadSwitchReg(interface, KSZ8895_CHIP_ID0);
119 
120  //The returned data is invalid until the serial interface is ready
121  } while(temp != KSZ8895_CHIP_ID0_FAMILY_ID_DEFAULT);
122 
123 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
124  //Enable tail tag feature
125  temp = ksz8895ReadSwitchReg(interface, KSZ8895_GLOBAL_CTRL10);
128 #else
129  //Disable tail tag feature
130  temp = ksz8895ReadSwitchReg(interface, KSZ8895_GLOBAL_CTRL10);
133 #endif
134 
135  //Loop through the ports
137  {
138 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
139  //Port separation mode?
140  if(interface->port != 0)
141  {
142  //Disable packet transmission and address learning
144  }
145  else
146 #endif
147  {
148  //Enable transmission, reception and address learning
150  }
151  }
152 
153  //Start switch operation
156 
157  //Dump switch registers for debugging purpose
158  ksz8895DumpSwitchReg(interface);
159 
160  //SMI interface mode?
161  if(interface->spiDriver == NULL)
162  {
163  //Loop through the ports
165  {
166  //Debug message
167  TRACE_DEBUG("Port %u:\r\n", port);
168  //Dump PHY registers for debugging purpose
169  ksz8895DumpPhyReg(interface, port);
170  }
171  }
172 
173  //Force the TCP/IP stack to poll the link state at startup
174  interface->phyEvent = TRUE;
175  //Notify the TCP/IP stack of the event
177 
178  //Successful initialization
179  return NO_ERROR;
180 }
181 
182 
183 /**
184  * @brief KSZ8895 timer handler
185  * @param[in] interface Underlying network interface
186  **/
187 
188 void ksz8895Tick(NetInterface *interface)
189 {
190  uint_t port;
191  bool_t linkState;
192 
193 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
194  //Port separation mode?
195  if(interface->port != 0)
196  {
197  uint_t i;
198  NetInterface *virtualInterface;
199 
200  //Loop through network interfaces
201  for(i = 0; i < NET_INTERFACE_COUNT; i++)
202  {
203  //Point to the current interface
204  virtualInterface = &netInterface[i];
205 
206  //Check whether the current virtual interface is attached to the
207  //physical interface
208  if(virtualInterface == interface ||
209  virtualInterface->parent == interface)
210  {
211  //Retrieve current link state
212  linkState = ksz8895GetLinkState(interface, virtualInterface->port);
213 
214  //Link up or link down event?
215  if(linkState != virtualInterface->linkState)
216  {
217  //Set event flag
218  interface->phyEvent = TRUE;
219  //Notify the TCP/IP stack of the event
221  }
222  }
223  }
224  }
225  else
226 #endif
227  {
228  //Initialize link state
229  linkState = FALSE;
230 
231  //Loop through the ports
233  {
234  //Retrieve current link state
235  if(ksz8895GetLinkState(interface, port))
236  {
237  linkState = TRUE;
238  }
239  }
240 
241  //Link up or link down event?
242  if(linkState != interface->linkState)
243  {
244  //Set event flag
245  interface->phyEvent = TRUE;
246  //Notify the TCP/IP stack of the event
248  }
249  }
250 }
251 
252 
253 /**
254  * @brief Enable interrupts
255  * @param[in] interface Underlying network interface
256  **/
257 
259 {
260 }
261 
262 
263 /**
264  * @brief Disable interrupts
265  * @param[in] interface Underlying network interface
266  **/
267 
269 {
270 }
271 
272 
273 /**
274  * @brief KSZ8895 event handler
275  * @param[in] interface Underlying network interface
276  **/
277 
279 {
280  uint_t port;
281  bool_t linkState;
282 
283 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
284  //Port separation mode?
285  if(interface->port != 0)
286  {
287  uint_t i;
288  NetInterface *virtualInterface;
289 
290  //Loop through network interfaces
291  for(i = 0; i < NET_INTERFACE_COUNT; i++)
292  {
293  //Point to the current interface
294  virtualInterface = &netInterface[i];
295 
296  //Check whether the current virtual interface is attached to the
297  //physical interface
298  if(virtualInterface == interface ||
299  virtualInterface->parent == interface)
300  {
301  //Get the port number associated with the current interface
302  port = virtualInterface->port;
303 
304  //Valid port?
305  if(port >= KSZ8895_PORT1 && port <= KSZ8895_PORT4)
306  {
307  //Retrieve current link state
308  linkState = ksz8895GetLinkState(interface, port);
309 
310  //Link up event?
311  if(linkState && !virtualInterface->linkState)
312  {
313  //Adjust MAC configuration parameters for proper operation
314  interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
315  interface->duplexMode = NIC_FULL_DUPLEX_MODE;
316  interface->nicDriver->updateMacConfig(interface);
317 
318  //Check current speed
319  virtualInterface->linkSpeed = ksz8895GetLinkSpeed(interface,
320  port);
321 
322  //Check current duplex mode
323  virtualInterface->duplexMode = ksz8895GetDuplexMode(interface,
324  port);
325 
326  //Update link state
327  virtualInterface->linkState = TRUE;
328 
329  //Process link state change event
330  nicNotifyLinkChange(virtualInterface);
331  }
332  //Link down event
333  else if(!linkState && virtualInterface->linkState)
334  {
335  //Update link state
336  virtualInterface->linkState = FALSE;
337 
338  //Process link state change event
339  nicNotifyLinkChange(virtualInterface);
340  }
341  }
342  }
343  }
344  }
345  else
346 #endif
347  {
348  //Initialize link state
349  linkState = FALSE;
350 
351  //Loop through the ports
353  {
354  //Retrieve current link state
355  if(ksz8895GetLinkState(interface, port))
356  {
357  linkState = TRUE;
358  }
359  }
360 
361  //Link up event?
362  if(linkState)
363  {
364  //Adjust MAC configuration parameters for proper operation
365  interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
366  interface->duplexMode = NIC_FULL_DUPLEX_MODE;
367  interface->nicDriver->updateMacConfig(interface);
368 
369  //Update link state
370  interface->linkState = TRUE;
371  }
372  else
373  {
374  //Update link state
375  interface->linkState = FALSE;
376  }
377 
378  //Process link state change event
379  nicNotifyLinkChange(interface);
380  }
381 }
382 
383 
384 /**
385  * @brief Add tail tag to Ethernet frame
386  * @param[in] interface Underlying network interface
387  * @param[in] buffer Multi-part buffer containing the payload
388  * @param[in,out] offset Offset to the first payload byte
389  * @param[in] ancillary Additional options passed to the stack along with
390  * the packet
391  * @return Error code
392  **/
393 
395  size_t *offset, NetTxAncillary *ancillary)
396 {
397  error_t error;
398 
399  //Initialize status code
400  error = NO_ERROR;
401 
402 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
403  //Valid port?
404  if(ancillary->port <= KSZ8895_PORT4)
405  {
406  size_t length;
407  const uint8_t *tailTag;
408 
409  //The one byte tail tagging is used to indicate the destination port
410  tailTag = &ksz8895IngressTailTag[ancillary->port];
411 
412  //Retrieve the length of the Ethernet frame
413  length = netBufferGetLength(buffer) - *offset;
414 
415  //The host controller should manually add padding to the packet before
416  //inserting the tail tag
417  error = ethPadFrame(buffer, &length);
418 
419  //Check status code
420  if(!error)
421  {
422  //The tail tag is inserted at the end of the packet, just before
423  //the CRC
424  error = netBufferAppend(buffer, tailTag, sizeof(uint8_t));
425  }
426  }
427  else
428  {
429  //The port number is not valid
430  error = ERROR_INVALID_PORT;
431  }
432 #endif
433 
434  //Return status code
435  return error;
436 }
437 
438 
439 /**
440  * @brief Decode tail tag from incoming Ethernet frame
441  * @param[in] interface Underlying network interface
442  * @param[in,out] frame Pointer to the received Ethernet frame
443  * @param[in,out] length Length of the frame, in bytes
444  * @param[in,out] ancillary Additional options passed to the stack along with
445  * the packet
446  * @return Error code
447  **/
448 
449 error_t ksz8895UntagFrame(NetInterface *interface, uint8_t **frame,
450  size_t *length, NetRxAncillary *ancillary)
451 {
452  error_t error;
453 
454  //Initialize status code
455  error = NO_ERROR;
456 
457 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
458  //Valid Ethernet frame received?
459  if(*length >= (sizeof(EthHeader) + sizeof(uint8_t)))
460  {
461  uint8_t *tailTag;
462 
463  //The tail tag is inserted at the end of the packet, just before
464  //the CRC
465  tailTag = *frame + *length - sizeof(uint8_t);
466 
467  //The one byte tail tagging is used to indicate the source port
468  ancillary->port = (*tailTag & KSZ8895_TAIL_TAG_SRC_PORT) + 1;
469 
470  //Strip tail tag from Ethernet frame
471  *length -= sizeof(uint8_t);
472  }
473  else
474  {
475  //Drop the received frame
476  error = ERROR_INVALID_LENGTH;
477  }
478 #endif
479 
480  //Return status code
481  return error;
482 }
483 
484 
485 /**
486  * @brief Get link state
487  * @param[in] interface Underlying network interface
488  * @param[in] port Port number
489  * @return Link state
490  **/
491 
493 {
494  uint16_t status;
495  bool_t linkState;
496 
497  //Check port number
498  if(port >= KSZ8895_PORT1 && port <= KSZ8895_PORT4)
499  {
500  //Read port status 1 register
501  status = ksz8895ReadSwitchReg(interface, KSZ8895_PORTn_STAT1(port));
502 
503  //Retrieve current link state
504  linkState = (status & KSZ8895_PORTn_STAT1_LINK_GOOD) ? TRUE : FALSE;
505  }
506  else
507  {
508  //The specified port number is not valid
509  linkState = FALSE;
510  }
511 
512  //Return link status
513  return linkState;
514 }
515 
516 
517 /**
518  * @brief Get link speed
519  * @param[in] interface Underlying network interface
520  * @param[in] port Port number
521  * @return Link speed
522  **/
523 
524 uint32_t ksz8895GetLinkSpeed(NetInterface *interface, uint8_t port)
525 {
526  uint16_t status;
527  uint32_t linkSpeed;
528 
529  //Check port number
530  if(port >= KSZ8895_PORT1 && port <= KSZ8895_PORT4)
531  {
532  //Read port status 0 register
533  status = ksz8895ReadSwitchReg(interface, KSZ8895_PORTn_STAT0(port));
534 
535  //Retrieve current link speed
536  if((status & KSZ8895_PORTn_STAT0_OP_SPEED) != 0)
537  {
538  linkSpeed = NIC_LINK_SPEED_100MBPS;
539  }
540  else
541  {
542  linkSpeed = NIC_LINK_SPEED_10MBPS;
543  }
544  }
545  else
546  {
547  //The specified port number is not valid
548  linkSpeed = NIC_LINK_SPEED_UNKNOWN;
549  }
550 
551  //Return link status
552  return linkSpeed;
553 }
554 
555 
556 /**
557  * @brief Get duplex mode
558  * @param[in] interface Underlying network interface
559  * @param[in] port Port number
560  * @return Duplex mode
561  **/
562 
564 {
565  uint16_t status;
566  NicDuplexMode duplexMode;
567 
568  //Check port number
569  if(port >= KSZ8895_PORT1 && port <= KSZ8895_PORT4)
570  {
571  //Read port status 0 register
572  status = ksz8895ReadSwitchReg(interface, KSZ8895_PORTn_STAT0(port));
573 
574  //Retrieve current duplex mode
575  if((status & KSZ8895_PORTn_STAT0_OP_DUPLEX) != 0)
576  {
577  duplexMode = NIC_FULL_DUPLEX_MODE;
578  }
579  else
580  {
581  duplexMode = NIC_HALF_DUPLEX_MODE;
582  }
583  }
584  else
585  {
586  //The specified port number is not valid
587  duplexMode = NIC_UNKNOWN_DUPLEX_MODE;
588  }
589 
590  //Return duplex mode
591  return duplexMode;
592 }
593 
594 
595 /**
596  * @brief Set port state
597  * @param[in] interface Underlying network interface
598  * @param[in] port Port number
599  * @param[in] state Port state
600  * @return Duplex mode
601  **/
602 
603 void ksz8895SetPortState(NetInterface *interface, uint8_t port,
604  SwitchPortState state)
605 {
606  uint16_t temp;
607 
608  //Check port number
609  if(port >= KSZ8895_PORT1 && port <= KSZ8895_PORT4)
610  {
611  //Read port control 2 register
612  temp = ksz8895ReadSwitchReg(interface, KSZ8895_PORTn_CTRL2(port));
613 
614  //Update port state
615  switch(state)
616  {
617  //Listening state
622  break;
623 
624  //Learning state
629  break;
630 
631  //Forwarding state
636  break;
637 
638  //Disabled state
639  default:
643  break;
644  }
645 
646  //Write the value back to port control 2 register
648  }
649 }
650 
651 
652 /**
653  * @brief Get port state
654  * @param[in] interface Underlying network interface
655  * @param[in] port Port number
656  * @return Port state
657  **/
658 
660 {
661  uint16_t temp;
662  SwitchPortState state;
663 
664  //Check port number
665  if(port >= KSZ8895_PORT1 && port <= KSZ8895_PORT4)
666  {
667  //Read port control 2 register
668  temp = ksz8895ReadSwitchReg(interface, KSZ8895_PORTn_CTRL2(port));
669 
670  //Check port state
671  if((temp & KSZ8895_PORTn_CTRL2_TRANSMIT_EN) == 0 &&
672  (temp & KSZ8895_PORTn_CTRL2_RECEIVE_EN) == 0 &&
673  (temp & KSZ8895_PORTn_CTRL2_LEARNING_DIS) != 0)
674  {
675  //Disabled state
677  }
678  else if((temp & KSZ8895_PORTn_CTRL2_TRANSMIT_EN) == 0 &&
679  (temp & KSZ8895_PORTn_CTRL2_RECEIVE_EN) != 0 &&
680  (temp & KSZ8895_PORTn_CTRL2_LEARNING_DIS) != 0)
681  {
682  //Listening state
684  }
685  else if((temp & KSZ8895_PORTn_CTRL2_TRANSMIT_EN) == 0 &&
686  (temp & KSZ8895_PORTn_CTRL2_RECEIVE_EN) == 0 &&
687  (temp & KSZ8895_PORTn_CTRL2_LEARNING_DIS) == 0)
688  {
689  //Learning state
691  }
692  else if((temp & KSZ8895_PORTn_CTRL2_TRANSMIT_EN) != 0 &&
693  (temp & KSZ8895_PORTn_CTRL2_RECEIVE_EN) != 0 &&
694  (temp & KSZ8895_PORTn_CTRL2_LEARNING_DIS) == 0)
695  {
696  //Forwarding state
698  }
699  else
700  {
701  //Unknown state
703  }
704  }
705  else
706  {
707  //The specified port number is not valid
709  }
710 
711  //Return port state
712  return state;
713 }
714 
715 
716 /**
717  * @brief Set aging time for dynamic filtering entries
718  * @param[in] interface Underlying network interface
719  * @param[in] agingTime Aging time, in seconds
720  **/
721 
722 void ksz8895SetAgingTime(NetInterface *interface, uint32_t agingTime)
723 {
724  //The aging period is fixed to 300 seconds
725 }
726 
727 
728 /**
729  * @brief Enable reserved multicast table
730  * @param[in] interface Underlying network interface
731  * @param[in] enable Enable or disable reserved group addresses
732  **/
733 
735 {
736  uint_t i;
737  SwitchFdbEntry entry;
738 
739  //The reserved group addresses are in the range of 01-80-C2-00-00-00 to
740  //01-80-C2-00-00-0F
741  for(i = 0; i <= 15; i++)
742  {
743  //Specify the reserved group address to be added or removed
744  entry.macAddr.b[0] = 0x01;
745  entry.macAddr.b[1] = 0x80;
746  entry.macAddr.b[2] = 0xC2;
747  entry.macAddr.b[3] = 0x00;
748  entry.macAddr.b[4] = 0x00;
749  entry.macAddr.b[5] = i;
750 
751  //Format forwarding database entry
752  entry.srcPort = 0;
754  entry.override = TRUE;
755 
756  //Update the static MAC table
757  if(enable)
758  {
759  ksz8895AddStaticFdbEntry(interface, &entry);
760  }
761  else
762  {
763  ksz8895DeleteStaticFdbEntry(interface, &entry);
764  }
765  }
766 }
767 
768 
769 /**
770  * @brief Add a new entry to the static MAC table
771  * @param[in] interface Underlying network interface
772  * @param[in] entry Pointer to the forwarding database entry
773  * @return Error code
774  **/
775 
777  const SwitchFdbEntry *entry)
778 {
779  error_t error;
780  uint_t i;
781  uint_t j;
782  uint8_t *p;
783  SwitchFdbEntry currentEntry;
784  Ksz8895StaticMacEntryW newEntry;
785 
786  //Keep track of the first free entry
788 
789  //Loop through the static MAC table
790  for(i = 0; i < KSZ8895_STATIC_MAC_TABLE_SIZE; i++)
791  {
792  //Read current entry
793  error = ksz8895GetStaticFdbEntry(interface, i, &currentEntry);
794 
795  //Valid entry?
796  if(!error)
797  {
798  //Check whether the table already contains the specified MAC address
799  if(macCompAddr(&currentEntry.macAddr, &entry->macAddr))
800  {
801  j = i;
802  break;
803  }
804  }
805  else
806  {
807  //Keep track of the first free entry
809  {
810  j = i;
811  }
812  }
813  }
814 
815  //Any entry available?
817  {
818  //Format MAC entry
819  newEntry.fid = 0;
820  newEntry.useFid = 0;
821  newEntry.override = entry->override;
822  newEntry.valid = TRUE;
823  newEntry.macAddr = entry->macAddr;
824 
825  //Set the relevant forward ports
826  if(entry->destPorts == SWITCH_CPU_PORT_MASK)
827  {
828  newEntry.forwardPorts = KSZ8895_PORT5_MASK;
829  }
830  else
831  {
832  newEntry.forwardPorts = entry->destPorts & KSZ8895_PORT_MASK;
833  }
834 
835  //Select the static MAC address table
839 
840  //Point to the MAC entry
841  p = (uint8_t *) &newEntry;
842 
843  //Write indirect data registers
844  for(i = 0; i < sizeof(Ksz8895StaticMacEntryW); i++)
845  {
846  ksz8895WriteSwitchReg(interface, KSZ8895_INDIRECT_DATA7 + i, p[i]);
847  }
848 
849  //Setup a write operation
853 
854  //Trigger the write operation
856 
857  //Successful processing
858  error = NO_ERROR;
859  }
860  else
861  {
862  //The static MAC table is full
863  error = ERROR_TABLE_FULL;
864  }
865 
866  //Return status code
867  return error;
868 }
869 
870 
871 /**
872  * @brief Remove an entry from the static MAC table
873  * @param[in] interface Underlying network interface
874  * @param[in] entry Forwarding database entry to remove from the table
875  * @return Error code
876  **/
877 
879  const SwitchFdbEntry *entry)
880 {
881  error_t error;
882  uint_t i;
883  uint_t j;
884  SwitchFdbEntry currentEntry;
885 
886  //Loop through the static MAC table
887  for(j = 0; j < KSZ8895_STATIC_MAC_TABLE_SIZE; j++)
888  {
889  //Read current entry
890  error = ksz8895GetStaticFdbEntry(interface, j, &currentEntry);
891 
892  //Valid entry?
893  if(!error)
894  {
895  //Check whether the table contains the specified MAC address
896  if(macCompAddr(&currentEntry.macAddr, &entry->macAddr))
897  {
898  break;
899  }
900  }
901  }
902 
903  //Any matching entry?
905  {
906  //Select the static MAC address table
910 
911  //Clear indirect data registers
912  for(i = 0; i < sizeof(Ksz8895StaticMacEntryW); i++)
913  {
915  }
916 
917  //Setup a write operation
921 
922  //Trigger the write operation
924 
925  //Successful processing
926  error = NO_ERROR;
927  }
928  else
929  {
930  //The static MAC table does not contain the specified address
931  error = ERROR_NOT_FOUND;
932  }
933 
934  //Return status code
935  return error;
936 }
937 
938 
939 /**
940  * @brief Read an entry from the static MAC table
941  * @param[in] interface Underlying network interface
942  * @param[in] index Zero-based index of the entry to read
943  * @param[out] entry Pointer to the forwarding database entry
944  * @return Error code
945  **/
946 
948  SwitchFdbEntry *entry)
949 {
950  error_t error;
951  uint_t i;
952  uint8_t *p;
953  Ksz8895StaticMacEntryR currentEntry;
954 
955  //Check index parameter
957  {
958  //Select the static MAC address table
962 
963  //Trigger the read operation
965 
966  //Point to the MAC entry
967  p = (uint8_t *) &currentEntry;
968 
969  //Read indirect data registers
970  for(i = 0; i < sizeof(Ksz8895StaticMacEntryR); i++)
971  {
972  p[i] = ksz8895ReadSwitchReg(interface, KSZ8895_INDIRECT_DATA7 + i);
973  }
974 
975  //Valid entry?
976  if(currentEntry.valid)
977  {
978  //Copy MAC entry
979  entry->macAddr = currentEntry.macAddr;
980  entry->srcPort = 0;
981  entry->destPorts = currentEntry.forwardPorts & KSZ8895_PORT_MASK;
982  entry->override = currentEntry.override;
983 
984  //Successful processing
985  error = NO_ERROR;
986  }
987  else
988  {
989  //The entry is not valid
990  error = ERROR_INVALID_ENTRY;
991  }
992  }
993  else
994  {
995  //The end of the table has been reached
996  error = ERROR_END_OF_TABLE;
997  }
998 
999  //Return status code
1000  return error;
1001 }
1002 
1003 
1004 /**
1005  * @brief Flush static MAC table
1006  * @param[in] interface Underlying network interface
1007  **/
1008 
1010 {
1011  uint_t i;
1012  uint_t temp;
1013  uint8_t state[5];
1014 
1015  //Loop through the ports
1016  for(i = KSZ8895_PORT1; i <= KSZ8895_PORT5; i++)
1017  {
1018  //Save the current state of the port
1019  state[i - 1] = ksz8895ReadSwitchReg(interface, KSZ8895_PORTn_CTRL2(i));
1020 
1021  //Turn off learning capability
1023  state[i - 1] | KSZ8895_PORTn_CTRL2_LEARNING_DIS);
1024  }
1025 
1026  //All the entries associated with a port that has its learning capability
1027  //being turned off will be flushed
1028  temp = ksz8895ReadSwitchReg(interface, KSZ8895_GLOBAL_CTRL0);
1030  ksz8895WriteSwitchReg(interface, KSZ8895_GLOBAL_CTRL0, temp);
1031 
1032  //Loop through the ports
1033  for(i = KSZ8895_PORT1; i <= KSZ8895_PORT5; i++)
1034  {
1035  //Restore the original state of the port
1036  ksz8895WriteSwitchReg(interface, KSZ8895_PORTn_CTRL2(i), state[i - 1]);
1037  }
1038 }
1039 
1040 
1041 /**
1042  * @brief Read an entry from the dynamic MAC table
1043  * @param[in] interface Underlying network interface
1044  * @param[in] index Zero-based index of the entry to read
1045  * @param[out] entry Pointer to the forwarding database entry
1046  * @return Error code
1047  **/
1048 
1050  SwitchFdbEntry *entry)
1051 {
1052  error_t error;
1053  uint_t i;
1054  uint_t n;
1055  uint8_t *p;
1056  Ksz8895DynamicMacEntry currentEntry;
1057 
1058  //Check index parameter
1059  if(index < KSZ8895_DYNAMIC_MAC_TABLE_SIZE)
1060  {
1061  //Read the MAC entry at the specified index
1062  do
1063  {
1064  //Select the dynamic MAC address table
1068  (MSB(index) & KSZ8895_INDIRECT_CTRL0_ADDR_H));
1069 
1070  //Trigger the read operation
1071  ksz8895WriteSwitchReg(interface, KSZ8895_INDIRECT_CTRL1, LSB(index));
1072 
1073  //Point to the MAC entry
1074  p = (uint8_t *) &currentEntry;
1075 
1076  //Read indirect data registers
1077  for(i = 0; i < sizeof(Ksz8895DynamicMacEntry); i++)
1078  {
1079  p[i] = ksz8895ReadSwitchReg(interface, KSZ8895_INDIRECT_DATA8 + i);
1080  }
1081 
1082  //Retry until the entry is ready
1083  } while(currentEntry.dataNotReady);
1084 
1085  //Check whether there are valid entries in the table
1086  if(!currentEntry.macEmpty)
1087  {
1088  //Retrieve the number of valid entries
1089  n = ((currentEntry.numValidEntriesH << 3) |
1090  currentEntry.numValidEntriesL) + 1;
1091  }
1092  else
1093  {
1094  //The table is empty
1095  n = 0;
1096  }
1097 
1098  //Valid entry?
1099  if(index < n)
1100  {
1101  //Copy MAC entry
1102  entry->macAddr = currentEntry.macAddr;
1103  entry->srcPort = currentEntry.sourcePort + 1;
1104  entry->destPorts = 0;
1105  entry->override = FALSE;
1106 
1107  //Successful processing
1108  error = NO_ERROR;
1109  }
1110  else
1111  {
1112  //The end of the table has been reached
1113  error = ERROR_END_OF_TABLE;
1114  }
1115  }
1116  else
1117  {
1118  //The end of the table has been reached
1119  error = ERROR_END_OF_TABLE;
1120  }
1121 
1122  //Return status code
1123  return error;
1124 }
1125 
1126 
1127 /**
1128  * @brief Flush dynamic MAC table
1129  * @param[in] interface Underlying network interface
1130  * @param[in] port Port number
1131  **/
1132 
1134 {
1135  uint_t i;
1136  uint_t temp;
1137  uint8_t state[5];
1138 
1139  //Loop through the ports
1140  for(i = KSZ8895_PORT1; i <= KSZ8895_PORT5; i++)
1141  {
1142  //Matching port number?
1143  if(i == port || port == 0)
1144  {
1145  //Save the current state of the port
1146  state[i - 1] = ksz8895ReadSwitchReg(interface, KSZ8895_PORTn_CTRL2(i));
1147 
1148  //Turn off learning capability
1150  state[i - 1] | KSZ8895_PORTn_CTRL2_LEARNING_DIS);
1151  }
1152  }
1153 
1154  //All the entries associated with a port that has its learning capability
1155  //being turned off will be flushed
1156  temp = ksz8895ReadSwitchReg(interface, KSZ8895_GLOBAL_CTRL0);
1158  ksz8895WriteSwitchReg(interface, KSZ8895_GLOBAL_CTRL0, temp);
1159 
1160  //Loop through the ports
1161  for(i = KSZ8895_PORT1; i <= KSZ8895_PORT5; i++)
1162  {
1163  //Matching port number?
1164  if(i == port || port == 0)
1165  {
1166  //Restore the original state of the port
1167  ksz8895WriteSwitchReg(interface, KSZ8895_PORTn_CTRL2(i), state[i - 1]);
1168  }
1169  }
1170 }
1171 
1172 
1173 /**
1174  * @brief Write PHY register
1175  * @param[in] interface Underlying network interface
1176  * @param[in] port Port number
1177  * @param[in] address PHY register address
1178  * @param[in] data Register value
1179  **/
1180 
1181 void ksz8895WritePhyReg(NetInterface *interface, uint8_t port,
1182  uint8_t address, uint16_t data)
1183 {
1184  //Write the specified PHY register
1185  if(interface->smiDriver != NULL)
1186  {
1187  interface->smiDriver->writePhyReg(SMI_OPCODE_WRITE, port, address, data);
1188  }
1189  else
1190  {
1191  interface->nicDriver->writePhyReg(SMI_OPCODE_WRITE, port, address, data);
1192  }
1193 }
1194 
1195 
1196 /**
1197  * @brief Read PHY register
1198  * @param[in] interface Underlying network interface
1199  * @param[in] port Port number
1200  * @param[in] address PHY register address
1201  * @return Register value
1202  **/
1203 
1204 uint16_t ksz8895ReadPhyReg(NetInterface *interface, uint8_t port,
1205  uint8_t address)
1206 {
1207  uint16_t data;
1208 
1209  //Read the specified PHY register
1210  if(interface->smiDriver != NULL)
1211  {
1212  data = interface->smiDriver->readPhyReg(SMI_OPCODE_READ, port, address);
1213  }
1214  else
1215  {
1216  data = interface->nicDriver->readPhyReg(SMI_OPCODE_READ, port, address);
1217  }
1218 
1219  //Return the value of the PHY register
1220  return data;
1221 }
1222 
1223 
1224 /**
1225  * @brief Dump PHY registers for debugging purpose
1226  * @param[in] interface Underlying network interface
1227  * @param[in] port Port number
1228  **/
1229 
1230 void ksz8895DumpPhyReg(NetInterface *interface, uint8_t port)
1231 {
1232  uint8_t i;
1233 
1234  //Loop through PHY registers
1235  for(i = 0; i < 32; i++)
1236  {
1237  //Display current PHY register
1238  TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i,
1239  ksz8895ReadPhyReg(interface, port, i));
1240  }
1241 
1242  //Terminate with a line feed
1243  TRACE_DEBUG("\r\n");
1244 }
1245 
1246 
1247 /**
1248  * @brief Write switch register
1249  * @param[in] interface Underlying network interface
1250  * @param[in] address Switch register address
1251  * @param[in] data Register value
1252  **/
1253 
1254 void ksz8895WriteSwitchReg(NetInterface *interface, uint8_t address,
1255  uint8_t data)
1256 {
1257  uint8_t phyAddr;
1258  uint8_t regAddr;
1259 
1260  //SPI slave mode?
1261  if(interface->spiDriver != NULL)
1262  {
1263  //Pull the CS pin low
1264  interface->spiDriver->assertCs();
1265 
1266  //Set up a write operation
1267  interface->spiDriver->transfer(KSZ8895_SPI_CMD_WRITE);
1268  //Write register address
1269  interface->spiDriver->transfer(address);
1270 
1271  //Write data
1272  interface->spiDriver->transfer(data);
1273 
1274  //Terminate the operation by raising the CS pin
1275  interface->spiDriver->deassertCs();
1276  }
1277  else
1278  {
1279  //SMI register write access is selected when opcode is set to 10 and
1280  //bits 2:1 of the PHY address are set to 11
1281  phyAddr = 0x06 | ((address >> 3) & 0x18) | ((address >> 5) & 0x01);
1282 
1283  //Register address field forms register address bits 4:0
1284  regAddr = address & 0x1F;
1285 
1286  //Registers are 8 data bits wide. For write operation, data bits 15:8
1287  //are not defined, and hence can be set to either zeroes or ones
1288  if(interface->smiDriver != NULL)
1289  {
1290  interface->smiDriver->writePhyReg(SMI_OPCODE_WRITE, phyAddr, regAddr,
1291  data);
1292  }
1293  else
1294  {
1295  interface->nicDriver->writePhyReg(SMI_OPCODE_WRITE, phyAddr, regAddr,
1296  data);
1297  }
1298  }
1299 }
1300 
1301 
1302 /**
1303  * @brief Read switch register
1304  * @param[in] interface Underlying network interface
1305  * @param[in] address Switch register address
1306  * @return Register value
1307  **/
1308 
1309 uint8_t ksz8895ReadSwitchReg(NetInterface *interface, uint8_t address)
1310 {
1311  uint8_t phyAddr;
1312  uint8_t regAddr;
1313  uint8_t data;
1314 
1315  //SPI slave mode?
1316  if(interface->spiDriver != NULL)
1317  {
1318  //Pull the CS pin low
1319  interface->spiDriver->assertCs();
1320 
1321  //Set up a read operation
1322  interface->spiDriver->transfer(KSZ8895_SPI_CMD_READ);
1323  //Write register address
1324  interface->spiDriver->transfer(address);
1325 
1326  //Read data
1327  data = interface->spiDriver->transfer(0xFF);
1328 
1329  //Terminate the operation by raising the CS pin
1330  interface->spiDriver->deassertCs();
1331  }
1332  else
1333  {
1334  //SMI register read access is selected when opcode is set to 10 and
1335  //bits 2:1 of the PHY address are set to 11
1336  phyAddr = 0x06 | ((address >> 3) & 0x18) | ((address >> 5) & 0x01);
1337 
1338  //Register address field forms register address bits 4:0
1339  regAddr = address & 0x1F;
1340 
1341  //Registers are 8 data bits wide. For read operation, data bits 15:8
1342  //are read back as zeroes
1343  if(interface->smiDriver != NULL)
1344  {
1345  data = interface->smiDriver->readPhyReg(SMI_OPCODE_READ, phyAddr,
1346  regAddr) & 0xFF;
1347  }
1348  else
1349  {
1350  data = interface->nicDriver->readPhyReg(SMI_OPCODE_READ, phyAddr,
1351  regAddr) & 0xFF;
1352  }
1353  }
1354 
1355  //Return register value
1356  return data;
1357 }
1358 
1359 
1360 /**
1361  * @brief Dump switch registers for debugging purpose
1362  * @param[in] interface Underlying network interface
1363  **/
1364 
1366 {
1367  uint16_t i;
1368 
1369  //Loop through switch registers
1370  for(i = 0; i < 256; i++)
1371  {
1372  //Display current switch register
1373  TRACE_DEBUG("0x%02" PRIX16 " (%02" PRIu16 ") : 0x%02" PRIX8 "\r\n",
1374  i, i, ksz8895ReadSwitchReg(interface, i));
1375  }
1376 
1377  //Terminate with a line feed
1378  TRACE_DEBUG("\r\n");
1379 }
void nicNotifyLinkChange(NetInterface *interface)
Process link state change notification.
Definition: nic.c:532
bool_t ksz8895GetLinkState(NetInterface *interface, uint8_t port)
Get link state.
uint8_t length
Definition: coap_common.h:190
KSZ8895 5-port Ethernet switch driver.
#define KSZ8895_INDIRECT_CTRL1
void ksz8895SetAgingTime(NetInterface *interface, uint32_t agingTime)
Set aging time for dynamic filtering entries.
#define KSZ8895_PORTn_CTRL2(port)
int bool_t
Definition: compiler_port.h:49
uint32_t destPorts
Definition: nic.h:149
#define netEvent
Definition: net_legacy.h:267
const uint8_t ksz8895IngressTailTag[5]
Tail tag rules (host to KSZ8895)
error_t ksz8895GetStaticFdbEntry(NetInterface *interface, uint_t index, SwitchFdbEntry *entry)
Read an entry from the static MAC table.
#define KSZ8895_PORT1
#define KSZ8895_INDIRECT_CTRL0_READ
uint8_t data[]
Definition: ethernet.h:209
#define KSZ8895_DYNAMIC_MAC_TABLE_SIZE
#define KSZ8895_CHIP_ID1_START_SWITCH
uint8_t p
Definition: ndp.h:298
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:88
error_t ksz8895GetDynamicFdbEntry(NetInterface *interface, uint_t index, SwitchFdbEntry *entry)
Read an entry from the dynamic MAC table.
#define KSZ8895_CHIP_ID1
#define TRUE
Definition: os_port.h:50
error_t ksz8895TagFrame(NetInterface *interface, NetBuffer *buffer, size_t *offset, NetTxAncillary *ancillary)
Add tail tag to Ethernet frame.
void ksz8895Tick(NetInterface *interface)
KSZ8895 timer handler.
NicDuplexMode ksz8895GetDuplexMode(NetInterface *interface, uint8_t port)
Get duplex mode.
#define KSZ8895_TAIL_TAG_NORMAL_ADDR_LOOKUP
#define KSZ8895_TAIL_TAG_DEST_PORT3
uint8_t numValidEntriesL
#define KSZ8895_GLOBAL_CTRL10_TAIL_TAG_EN
#define KSZ8895_INDIRECT_DATA8
#define NET_INTERFACE_COUNT
Definition: net.h:110
#define KSZ8895_PORTn_STAT0(port)
uint8_t forwardPorts
void ksz8895WriteSwitchReg(NetInterface *interface, uint8_t address, uint8_t data)
Write switch register.
const SwitchDriver ksz8895SwitchDriver
KSZ8895 Ethernet switch driver.
#define KSZ8895_SPI_CMD_WRITE
#define SMI_OPCODE_WRITE
Definition: nic.h:65
uint8_t override
#define KSZ8895_PORTn_CTRL2_LEARNING_DIS
#define KSZ8895_TAIL_TAG_PORT_SEL
MacAddr macAddr
uint8_t fid
void ksz8895EnableIrq(NetInterface *interface)
Enable interrupts.
#define FALSE
Definition: os_port.h:46
SwitchPortState ksz8895GetPortState(NetInterface *interface, uint8_t port)
Get port state.
uint8_t numValidEntriesH
error_t
Error codes.
Definition: error.h:42
#define netInterface
Definition: net_legacy.h:273
#define KSZ8895_PORT4
#define KSZ8895_TAIL_TAG_SRC_PORT
#define KSZ8895_PORTn_STAT1(port)
error_t ksz8895DeleteStaticFdbEntry(NetInterface *interface, const SwitchFdbEntry *entry)
Remove an entry from the static MAC table.
#define NetRxAncillary
Definition: net_misc.h:40
#define NetInterface
Definition: net.h:36
MacAddr macAddr
#define KSZ8895_GLOBAL_CTRL10
void ksz8895DumpSwitchReg(NetInterface *interface)
Dump switch registers for debugging purpose.
void ksz8895SetPortState(NetInterface *interface, uint8_t port, SwitchPortState state)
Set port state.
#define KSZ8895_PORTn_STAT1_LINK_GOOD
#define KSZ8895_PORTn_STAT0_OP_SPEED
#define KSZ8895_GLOBAL_CTRL0_FLUSH_STATIC_MAC_TABLE
error_t ethPadFrame(NetBuffer *buffer, size_t *length)
Ethernet frame padding.
#define NetTxAncillary
Definition: net_misc.h:36
#define KSZ8895_GLOBAL_CTRL0
#define MSB(x)
Definition: os_port.h:58
#define SMI_OPCODE_READ
Definition: nic.h:66
SwitchPortState
Switch port state.
Definition: nic.h:130
uint8_t sourcePort
#define TRACE_INFO(...)
Definition: debug.h:95
#define KSZ8895_TAIL_TAG_DEST_PORT2
#define KSZ8895_INDIRECT_CTRL0_WRITE
size_t netBufferGetLength(const NetBuffer *buffer)
Get the actual length of a multi-part buffer.
Definition: net_mem.c:297
#define LSB(x)
Definition: os_port.h:54
uint8_t forwardPorts
#define KSZ8895_INDIRECT_CTRL0_ADDR_H
void ksz8895EnableRsvdMcastTable(NetInterface *interface, bool_t enable)
Enable reserved multicast table.
void ksz8895FlushStaticFdbTable(NetInterface *interface)
Flush static MAC table.
MacAddr macAddr
#define KSZ8895_PORTn_CTRL2_RECEIVE_EN
uint16_t port
Definition: dns_common.h:223
#define KSZ8895_GLOBAL_CTRL0_FLUSH_DYNAMIC_MAC_TABLE
#define KSZ8895_SPI_CMD_READ
#define TRACE_DEBUG(...)
Definition: debug.h:107
error_t ksz8895UntagFrame(NetInterface *interface, uint8_t **frame, size_t *length, NetRxAncillary *ancillary)
Decode tail tag from incoming Ethernet frame.
#define KSZ8895_INDIRECT_CTRL0_TABLE_SEL_DYNAMIC_MAC
Static MAC table entry (read operation)
uint8_t dataNotReady
__start_packed struct @7 EthHeader
Ethernet frame header.
uint16_t regAddr
uint8_t valid
#define KSZ8895_PORT5
Ethernet switch driver.
Definition: nic.h:306
#define KSZ8895_CHIP_ID0
#define KSZ8895_TAIL_TAG_DEST_PORT1
uint8_t n
error_t ksz8895AddStaticFdbEntry(NetInterface *interface, const SwitchFdbEntry *entry)
Add a new entry to the static MAC table.
error_t netBufferAppend(NetBuffer *dest, const void *src, size_t length)
Append data a multi-part buffer.
Definition: net_mem.c:586
Static MAC table entry (write operation)
#define KSZ8895_PORTn_CTRL2_TRANSMIT_EN
#define KSZ8895_PORT_MASK
NicDuplexMode
Duplex mode.
Definition: nic.h:118
MacAddr macAddr
Definition: nic.h:147
#define KSZ8895_INDIRECT_CTRL0_TABLE_SEL_STATIC_MAC
uint8_t srcPort
Definition: nic.h:148
void ksz8895DisableIrq(NetInterface *interface)
Disable interrupts.
void ksz8895WritePhyReg(NetInterface *interface, uint8_t port, uint8_t address, uint16_t data)
Write PHY register.
#define KSZ8895_INDIRECT_CTRL0
#define macCompAddr(macAddr1, macAddr2)
Definition: ethernet.h:121
void ksz8895EventHandler(NetInterface *interface)
KSZ8895 event handler.
uint8_t macEmpty
Ipv6Addr address
void osSetEvent(OsEvent *event)
Set the specified event object to the signaled state.
uint16_t ksz8895ReadPhyReg(NetInterface *interface, uint8_t port, uint8_t address)
Read PHY register.
#define KSZ8895_STATIC_MAC_TABLE_SIZE
uint8_t useFid
void ksz8895FlushDynamicFdbTable(NetInterface *interface, uint8_t port)
Flush dynamic MAC table.
#define KSZ8895_PORTn_STAT0_OP_DUPLEX
#define SWITCH_CPU_PORT_MASK
Definition: nic.h:59
unsigned int uint_t
Definition: compiler_port.h:45
TCP/IP stack core.
uint32_t ksz8895GetLinkSpeed(NetInterface *interface, uint8_t port)
Get link speed.
error_t ksz8895Init(NetInterface *interface)
KSZ8895 Ethernet switch initialization.
void ksz8895DumpPhyReg(NetInterface *interface, uint8_t port)
Dump PHY registers for debugging purpose.
#define KSZ8895_TAIL_TAG_DEST_PORT4
#define KSZ8895_INDIRECT_DATA7
#define KSZ8895_CHIP_ID0_FAMILY_ID_DEFAULT
Dynamic MAC table entry.
Helper functions for Ethernet.
Success.
Definition: error.h:44
bool_t override
Definition: nic.h:150
uint8_t ksz8895ReadSwitchReg(NetInterface *interface, uint8_t address)
Read switch register.
Debugging facilities.
Forwarding database entry.
Definition: nic.h:145
#define KSZ8895_PORT5_MASK
uint8_t override
uint8_t valid