summaryrefslogtreecommitdiffstats
path: root/meta-facebook
diff options
context:
space:
mode:
Diffstat (limited to 'meta-facebook')
-rw-r--r--meta-facebook/COPYING340
-rw-r--r--meta-facebook/meta-wedge/conf/bblayers.conf.sample21
-rw-r--r--meta-facebook/meta-wedge/conf/conf-notes.txt2
-rw-r--r--meta-facebook/meta-wedge/conf/layer.conf10
-rw-r--r--meta-facebook/meta-wedge/conf/local.conf.sample140
-rw-r--r--meta-facebook/meta-wedge/conf/machine/wedge.conf7
-rw-r--r--meta-facebook/meta-wedge/recipes-core/busybox/busybox/busybox.cfg18
-rw-r--r--meta-facebook/meta-wedge/recipes-core/busybox/busybox_%.bbappend5
-rw-r--r--meta-facebook/meta-wedge/recipes-core/images/wedge-image.bb1
-rw-r--r--meta-facebook/meta-wedge/recipes-core/images/wedge-image.inc77
-rw-r--r--meta-facebook/meta-wedge/recipes-core/init-ifupdown/files/interfaces11
-rw-r--r--meta-facebook/meta-wedge/recipes-core/init-ifupdown/init-ifupdown_%.bbappend2
-rw-r--r--meta-facebook/meta-wedge/recipes-core/sysvinit/sysvinit-inittab_%.bbappend5
-rw-r--r--meta-facebook/meta-wedge/recipes-kernel/linux/linux-aspeed_2.6%.bbappend5
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/bitbang/bitbang_0.1.bb22
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/Makefile13
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/bitbang.c236
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/bitbang.h66
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/gpio.c107
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/gpio.h42
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/mdio_bb.c336
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/spi_bb.c317
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/Makefile10
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/README5
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/fand.cpp851
-rwxr-xr-xmeta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/get_fan_speed.sh59
-rwxr-xr-xmeta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/init_pwm.sh71
-rwxr-xr-xmeta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/set_fan_speed.sh85
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/setup-fan.sh23
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/watchdog.cpp201
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/watchdog.h60
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl_0.1.bb59
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fblibs/files/alert_control/Makefile11
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fblibs/files/alert_control/alert_control.c117
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fblibs/files/alert_control/alert_control.h64
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fblibs/files/ipmi/Makefile11
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fblibs/files/ipmi/ipmi.c81
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fblibs/files/ipmi/ipmi.h35
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fblibs/libalert-control_0.1.bb24
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fblibs/libipmi_0.1.bb25
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fbutils/fbutils_0.1.bb86
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fbutils/files/COPYING340
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fbutils/files/ast-functions222
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fbutils/files/at93c46.py260
-rwxr-xr-xmeta-facebook/meta-wedge/recipes-wedge/fbutils/files/at93c46_util.py178
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fbutils/files/bcm5396.py452
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fbutils/files/bcm5396_util.py260
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fbutils/files/create_vlan_intf37
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fbutils/files/eth0_mac_fixup.sh41
-rwxr-xr-xmeta-facebook/meta-wedge/recipes-wedge/fbutils/files/fcswitcher.sh83
-rwxr-xr-xmeta-facebook/meta-wedge/recipes-wedge/fbutils/files/mdio.py124
-rwxr-xr-xmeta-facebook/meta-wedge/recipes-wedge/fbutils/files/mount_data0.sh61
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fbutils/files/post_led.sh105
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fbutils/files/power-on.sh72
-rwxr-xr-xmeta-facebook/meta-wedge/recipes-wedge/fbutils/files/power_led.sh49
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fbutils/files/rc.early25
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fbutils/files/rc.local26
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fbutils/files/reset_usb.sh36
-rwxr-xr-xmeta-facebook/meta-wedge/recipes-wedge/fbutils/files/setup-gpio.sh349
-rwxr-xr-xmeta-facebook/meta-wedge/recipes-wedge/fbutils/files/setup_rov.sh99
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fbutils/files/setup_switch.py49
-rwxr-xr-xmeta-facebook/meta-wedge/recipes-wedge/fbutils/files/sol.sh37
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fbutils/files/src/include/i2c-dev.h362
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fbutils/files/src/include/log.h59
-rwxr-xr-xmeta-facebook/meta-wedge/recipes-wedge/fbutils/files/us_console.sh47
-rwxr-xr-xmeta-facebook/meta-wedge/recipes-wedge/fbutils/files/watch-fc.sh34
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fbutils/files/wedge_power.sh201
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/fbutils/files/wedge_us_mac.sh29
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/ipmid/files/Makefile13
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/ipmid/files/ipmid.c1490
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/fruid.h28
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/sdr.c412
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/sdr.h132
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/sel.c438
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/sel.h51
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/sensor.h117
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/timestamp.c46
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/timestamp.h30
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/wedge/fruid.c183
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/wedge/sensor.c371
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/ipmid/files/setup-ipmid.sh16
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/ipmid/ipmid_0.1.bb54
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/lm_sensors/files/wedge.conf71
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/lm_sensors/lmsensors_%.bbappend10
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/Makefile13
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/README10
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/etc/oob-nic.sh103
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/hlist.h73
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/i2craw.c245
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/intf.c254
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/intf.h31
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/libnetlink.c717
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/libnetlink.h161
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/ll_map.c225
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/ll_map.h32
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/main.c192
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/nic.c487
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/nic.h44
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/nic_defs.h143
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic_0.1.bb29
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/po-eeprom/files/Makefile10
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/po-eeprom/files/po-eeprom.c80
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/po-eeprom/po-eeprom_0.1.bb25
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/Makefile16
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/gpiowatch.c93
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbus.c183
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbus.h46
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbuscmd.c163
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbussim.c183
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon_0.1.bb48
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/rest-api/files/rest.py102
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/rest-api/files/rest_bmc.py67
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/rest-api/files/rest_fruid.py87
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/rest-api/files/rest_gpios.py56
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/rest-api/files/rest_sensors.py50
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/rest-api/files/rest_server.py68
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/rest-api/files/setup-rest-api.sh16
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/rest-api/rest-api_0.1.bb52
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/sensor-setup/files/sensor-setup.sh52
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/sensor-setup/sensor-setup_0.1.bb23
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/sms-kcsd/sms-kcsd/Makefile10
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/sms-kcsd/sms-kcsd/setup-sms-kcs.sh16
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/sms-kcsd/sms-kcsd/sms-kcsd.c144
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/sms-kcsd/sms-kcsd_0.1.bb46
-rwxr-xr-xmeta-facebook/meta-wedge/recipes-wedge/usb-console/files/usbcons.sh80
-rwxr-xr-xmeta-facebook/meta-wedge/recipes-wedge/usb-console/files/usbmon.sh25
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/usb-console/usb-console_0.1.bb27
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/files/lib/Makefile11
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/files/lib/wedge_eeprom.c403
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/files/lib/wedge_eeprom.h121
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/files/utils/Makefile10
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/files/utils/weutil.c74
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/libwedge-eeprom_0.1.bb26
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/wedge-eeprom_0.1.bb21
134 files changed, 15953 insertions, 0 deletions
diff --git a/meta-facebook/COPYING b/meta-facebook/COPYING
new file mode 100644
index 0000000..3912109
--- /dev/null
+++ b/meta-facebook/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/meta-facebook/meta-wedge/conf/bblayers.conf.sample b/meta-facebook/meta-wedge/conf/bblayers.conf.sample
new file mode 100644
index 0000000..e8b572a
--- /dev/null
+++ b/meta-facebook/meta-wedge/conf/bblayers.conf.sample
@@ -0,0 +1,21 @@
+# LAYER_CONF_VERSION is increased each time build/conf/bblayers.conf
+# changes incompatibly
+LCONF_VERSION = "6"
+
+BBPATH = "${TOPDIR}"
+BBFILES ?= ""
+
+BBLAYERS ?= " \
+ ##OEROOT##/meta \
+ ##OEROOT##/meta-yocto \
+ ##OEROOT##/meta-yocto-bsp \
+ ##OEROOT##/meta-openembedded/meta-oe \
+ ##OEROOT##/meta-openembedded/meta-networking \
+ ##OEROOT##/meta-openbmc \
+ ##OEROOT##/meta-openbmc/meta-aspeed \
+ ##OEROOT##/meta-openbmc/meta-facebook/meta-wedge \
+ "
+BBLAYERS_NON_REMOVABLE ?= " \
+ ##OEROOT##/meta \
+ ##OEROOT##/meta-yocto \
+ "
diff --git a/meta-facebook/meta-wedge/conf/conf-notes.txt b/meta-facebook/meta-wedge/conf/conf-notes.txt
new file mode 100644
index 0000000..ad3d503
--- /dev/null
+++ b/meta-facebook/meta-wedge/conf/conf-notes.txt
@@ -0,0 +1,2 @@
+Common targets are:
+ wedge-image
diff --git a/meta-facebook/meta-wedge/conf/layer.conf b/meta-facebook/meta-wedge/conf/layer.conf
new file mode 100644
index 0000000..630361f
--- /dev/null
+++ b/meta-facebook/meta-wedge/conf/layer.conf
@@ -0,0 +1,10 @@
+# We have a conf and classes directory, add to BBPATH
+BBPATH .= ":${LAYERDIR}"
+
+# We have recipes-* directories, add to BBFILES
+BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \
+ ${LAYERDIR}/recipes-*/*/*.bbappend"
+
+BBFILE_COLLECTIONS += "wedge"
+BBFILE_PATTERN_wedge = "^${LAYERDIR}/"
+BBFILE_PRIORITY_wedge = "8"
diff --git a/meta-facebook/meta-wedge/conf/local.conf.sample b/meta-facebook/meta-wedge/conf/local.conf.sample
new file mode 100644
index 0000000..078ba23
--- /dev/null
+++ b/meta-facebook/meta-wedge/conf/local.conf.sample
@@ -0,0 +1,140 @@
+#
+# Local configuration file for building the OpenBMC image.
+#
+
+# Always look for packages first in our own local package mirror
+SOURCE_MIRROR_URL ?= "file://${TOPDIR}/../meta-openbmc/source_mirror/"
+INHERIT += "own-mirrors"
+
+# Save local tarballs for all packages we download.
+# This can be used to update our mirror directory above.
+BB_GENERATE_MIRROR_TARBALLS = "1"
+
+# The following setting will prevent bitbake from downloading anything over the
+# network. This can be used to ensure that we get everything from a local
+# file:// mirror.
+#
+# Comment this out if you do need to download new packages from the internet.
+# However, once you have downloaded the package you should check them into our
+# mirror repository so that other developers will always get it from the mirror
+# repo.
+BB_NO_NETWORK = "fb-only"
+
+# Parallelism Options
+#
+# How many tasks bitbake should run in parallel:
+BB_NUMBER_THREADS ?= "${@oe.utils.cpu_count()}"
+# How many processes make should run in parallel:
+PARALLEL_MAKE ?= "-j ${@oe.utils.cpu_count()}"
+
+# Machine Selection
+MACHINE ??= "wedge"
+
+# Build directory locationds.
+#
+#DL_DIR ?= "${TOPDIR}/downloads"
+#SSTATE_DIR ?= "${TOPDIR}/sstate-cache"
+#TMPDIR = "${TOPDIR}/tmp"
+
+#
+# Default policy config
+# We could eventually create our own distro config if desired,
+# but for now we use the standard poky distro settings.
+#
+DISTRO ?= "poky"
+
+# Use RPM packages
+PACKAGE_CLASSES ?= "package_rpm"
+
+# Extra image features.
+# Currently we do not enable anything extra here.
+#EXTRA_IMAGE_FEATURES = ""
+
+# We build on CentOS 6.3.
+# Don't complain about it, even though it isn't in poky's default
+# list of supported distros.
+SANITY_TESTED_DISTROS_append ?= " CentOS-6.3 \n "
+
+#
+# Additional image features
+#
+# The following is a list of additional classes to use when building images which
+# enable extra features. Some available options which can be included in this variable
+# are:
+# - 'buildstats' collect build statistics
+# - 'image-mklibs' to reduce shared library files size for an image
+# - 'image-prelink' in order to prelink the filesystem image
+# - 'image-swab' to perform host system intrusion detection
+# NOTE: if listing mklibs & prelink both, then make sure mklibs is before prelink
+# NOTE: mklibs also needs to be explicitly enabled for a given image, see local.conf.extended
+USER_CLASSES ?= "buildstats image-mklibs image-prelink"
+
+#
+# Interactive shell configuration
+#
+# Under certain circumstances the system may need input from you and to do this it
+# can launch an interactive shell. It needs to do this since the build is
+# multithreaded and needs to be able to handle the case where more than one parallel
+# process may require the user's attention. The default is iterate over the available
+# terminal types to find one that works.
+#
+# Examples of the occasions this may happen are when resolving patches which cannot
+# be applied, to use the devshell or the kernel menuconfig
+#
+# Supported values are auto, gnome, xfce, rxvt, screen, konsole (KDE 3.x only), none
+# Note: currently, Konsole support only works for KDE 3.x due to the way
+# newer Konsole versions behave
+#OE_TERMINAL = "auto"
+# By default disable interactive patch resolution (tasks will just fail instead):
+PATCHRESOLVE = "noop"
+
+#
+# Disk Space Monitoring during the build
+#
+# Monitor the disk space during the build. If there is less that 1GB of space or less
+# than 100K inodes in any key build location (TMPDIR, DL_DIR, SSTATE_DIR), gracefully
+# shutdown the build. If there is less that 100MB or 1K inodes, perform a hard abort
+# of the build. The reason for this is that running completely out of space can corrupt
+# files and damages the build in ways which may not be easily recoverable.
+BB_DISKMON_DIRS = "\
+ STOPTASKS,${TMPDIR},1G,100K \
+ STOPTASKS,${DL_DIR},1G,100K \
+ STOPTASKS,${SSTATE_DIR},1G,100K \
+ ABORT,${TMPDIR},100M,1K \
+ ABORT,${DL_DIR},100M,1K \
+ ABORT,${SSTATE_DIR},100M,1K"
+
+#
+# Shared-state files from other locations
+#
+# As mentioned above, shared state files are prebuilt cache data objects which can
+# used to accelerate build time. This variable can be used to configure the system
+# to search other mirror locations for these objects before it builds the data itself.
+#
+# This can be a filesystem directory, or a remote url such as http or ftp. These
+# would contain the sstate-cache results from previous builds (possibly from other
+# machines). This variable works like fetcher MIRRORS/PREMIRRORS and points to the
+# cache locations to check for the shared objects.
+# NOTE: if the mirror uses the same structure as SSTATE_DIR, you need to add PATH
+# at the end as shown in the examples below. This will be substituted with the
+# correct path within the directory structure.
+#SSTATE_MIRRORS ?= "\
+#file://.* http://someserver.tld/share/sstate/PATH;downloadfilename=PATH \n \
+#file://.* file:///some/local/dir/sstate/PATH"
+
+
+# CONF_VERSION is increased each time build/conf/ changes incompatibly and is used to
+# track the version of this file when it was generated. This can safely be ignored if
+# this doesn't mean anything to you.
+CONF_VERSION = "1"
+
+
+# Update root password to '0penBmc' and change the root shell back to bash.
+# This default root password is used at the ODM and system integrator. It will be
+# changed during provisioning at the datacenter.
+INHERIT += "extrausers"
+
+EXTRA_USERS_PARAMS = " \
+ usermod -s /bin/bash root; \
+ usermod -p '\$1\$UGMqyqdG\$FZiylVFmRRfl9Z0Ue8G7e/' root; \
+ "
diff --git a/meta-facebook/meta-wedge/conf/machine/wedge.conf b/meta-facebook/meta-wedge/conf/machine/wedge.conf
new file mode 100644
index 0000000..69f3a90
--- /dev/null
+++ b/meta-facebook/meta-wedge/conf/machine/wedge.conf
@@ -0,0 +1,7 @@
+#@TYPE: Machine
+#@NAME: Wedge
+#@DESCRIPTION: Machine configuration for Facebook Wedge
+
+UBOOT_MACHINE_wedge = "wedge_config"
+
+require conf/machine/include/ast1250.inc
diff --git a/meta-facebook/meta-wedge/recipes-core/busybox/busybox/busybox.cfg b/meta-facebook/meta-wedge/recipes-core/busybox/busybox/busybox.cfg
new file mode 100644
index 0000000..66da117
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-core/busybox/busybox/busybox.cfg
@@ -0,0 +1,18 @@
+CONFIG_SH_MATH_SUPPORT_64=y
+CONFIG_DEVMEM=y
+CONFIG_LSUSB=y
+CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS=y
+CONFIG_RX=y
+CONFIG_FLASHCP=y
+CONFIG_FLASH_LOCK=y
+CONFIG_FLASH_UNLOCK=y
+CONFIG_FLASH_ERASEALL=y
+CONFIG_TRACEROUTE6=y
+CONFIG_VCONFIG=y
+# we use the standalone ip util
+CONFIG_IP=n
+# use dhclient, as udhcpc will flush all v6 link local addresses during renew
+CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP=y
+CONFIG_UDHCPD=n
+CONFIG_UDHCPC=n
+CONFIG_UDHCPC6=n
diff --git a/meta-facebook/meta-wedge/recipes-core/busybox/busybox_%.bbappend b/meta-facebook/meta-wedge/recipes-core/busybox/busybox_%.bbappend
new file mode 100644
index 0000000..b8641ee
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-core/busybox/busybox_%.bbappend
@@ -0,0 +1,5 @@
+FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"
+
+SRC_URI += " \
+ file://busybox.cfg \
+ " \ No newline at end of file
diff --git a/meta-facebook/meta-wedge/recipes-core/images/wedge-image.bb b/meta-facebook/meta-wedge/recipes-core/images/wedge-image.bb
new file mode 100644
index 0000000..04c31f6
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-core/images/wedge-image.bb
@@ -0,0 +1 @@
+include wedge-image.inc
diff --git a/meta-facebook/meta-wedge/recipes-core/images/wedge-image.inc b/meta-facebook/meta-wedge/recipes-core/images/wedge-image.inc
new file mode 100644
index 0000000..15a4252
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-core/images/wedge-image.inc
@@ -0,0 +1,77 @@
+inherit aspeed_uboot_image
+
+# /dev
+require recipes-core/images/aspeed-dev.inc
+
+# Base this image on core-image-minimal
+include recipes-core/images/core-image-minimal.bb
+
+# Changing the image compression from gz to lzma achieves 30% saving (~3M).
+# However, the current u-boot does not have lzma enabled. Stick to gz
+# until we generate a new u-boot image.
+IMAGE_FSTYPES += "cpio.lzma.u-boot"
+UBOOT_IMAGE_ENTRYPOINT = "0x40800000"
+
+PYTHON_PKGS = " \
+ python-core \
+ python-io \
+ python-json \
+ python-shell \
+ python-subprocess \
+ python-argparse \
+ python-ctypes \
+ python-datetime \
+ python-email \
+ python-threading \
+ python-mime \
+ python-pickle \
+ python-misc \
+ python-netserver \
+ "
+
+NTP_PKGS = " \
+ ntp \
+ ntp-utils \
+ sntp \
+ ntpdate \
+ "
+
+# Include modules in rootfs
+IMAGE_INSTALL += " \
+ kernel-modules \
+ u-boot \
+ u-boot-fw-utils \
+ fbutils \
+ fan-ctrl \
+ rackmon \
+ watchdog-ctrl \
+ i2c-tools \
+ sensor-setup \
+ usb-console \
+ oob-nic \
+ lmsensors-sensors \
+ wedge-eeprom \
+ sms-kcsd \
+ rest-api \
+ bottle \
+ ipmid \
+ po-eeprom \
+ bitbang \
+ ${PYTHON_PKGS} \
+ ${NTP_PKGS} \
+ iproute2 \
+ dhcp-client \
+ "
+
+IMAGE_FEATURES += " \
+ ssh-server-openssh \
+ tools-debug \
+ "
+
+DISTRO_FEATURES += " \
+ ext2 \
+ ipv6 \
+ nfs \
+ usbgadget \
+ usbhost \
+ "
diff --git a/meta-facebook/meta-wedge/recipes-core/init-ifupdown/files/interfaces b/meta-facebook/meta-wedge/recipes-core/init-ifupdown/files/interfaces
new file mode 100644
index 0000000..36e342e
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-core/init-ifupdown/files/interfaces
@@ -0,0 +1,11 @@
+# The loopback interface
+auto lo
+iface lo inet loopback
+
+auto eth0
+iface eth0 inet dhcp
+
+auto usb0
+iface usb0 inet6 static
+ address fe80::1
+ netmask 64
diff --git a/meta-facebook/meta-wedge/recipes-core/init-ifupdown/init-ifupdown_%.bbappend b/meta-facebook/meta-wedge/recipes-core/init-ifupdown/init-ifupdown_%.bbappend
new file mode 100644
index 0000000..7d74521
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-core/init-ifupdown/init-ifupdown_%.bbappend
@@ -0,0 +1,2 @@
+
+FILESEXTRAPATHS_prepend := "${THISDIR}/files:"
diff --git a/meta-facebook/meta-wedge/recipes-core/sysvinit/sysvinit-inittab_%.bbappend b/meta-facebook/meta-wedge/recipes-core/sysvinit/sysvinit-inittab_%.bbappend
new file mode 100644
index 0000000..0a6d604
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-core/sysvinit/sysvinit-inittab_%.bbappend
@@ -0,0 +1,5 @@
+# ttyS0 is UART5, connected to the debug board, selected by GPIOB2
+# ttyS1 is UART1, connected to uS, selected by GPIOB2
+# ttyS2 is UART3, connected to the front panel Console port
+# ttyS3 is UART4, used as RS485 connected as rackmon
+SERIAL_CONSOLES += "57600;ttyS0 9600;ttyS2"
diff --git a/meta-facebook/meta-wedge/recipes-kernel/linux/linux-aspeed_2.6%.bbappend b/meta-facebook/meta-wedge/recipes-kernel/linux/linux-aspeed_2.6%.bbappend
new file mode 100644
index 0000000..6bf04da
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-kernel/linux/linux-aspeed_2.6%.bbappend
@@ -0,0 +1,5 @@
+LINUX_VERSION_EXTENSION = "-wedge"
+
+COMPATIBLE_MACHINE = "wedge"
+
+KERNEL_DEFCONFIG_aspeed = "wedge_defconfig"
diff --git a/meta-facebook/meta-wedge/recipes-wedge/bitbang/bitbang_0.1.bb b/meta-facebook/meta-wedge/recipes-wedge/bitbang/bitbang_0.1.bb
new file mode 100644
index 0000000..443d81d
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/bitbang/bitbang_0.1.bb
@@ -0,0 +1,22 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+SUMMARY = "Device driver using GPIO bitbang"
+DESCRIPTION = "Various device driver using GPIO bitbang"
+SECTION = "base"
+PR = "r1"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://bitbang.c;beginline=4;endline=16;md5=da35978751a9d71b73679307c4d296ec"
+
+SRC_URI = "file://src \
+ "
+
+DEPENDS += "fbutils"
+
+S = "${WORKDIR}/src"
+
+do_install() {
+ install -d ${D}${bindir}
+ install -m 755 spi-bb ${D}${bindir}/spi-bb
+ install -m 755 mdio-bb ${D}${bindir}/mdio-bb
+}
+
+FILES_${PN} = "${bindir}"
diff --git a/meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/Makefile b/meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/Makefile
new file mode 100644
index 0000000..102ac4e
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/Makefile
@@ -0,0 +1,13 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+all: spi-bb mdio-bb
+
+spi-bb: spi_bb.o bitbang.o gpio.o
+ $(CC) -o $@ $^ $(LDFLAGS)
+
+mdio-bb: mdio_bb.o bitbang.o gpio.o
+ $(CC) -o $@ $^ $(LDFLAGS)
+
+.PHONY: clean
+
+clean:
+ rm -rf *.o spi-bb mdio-bb
diff --git a/meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/bitbang.c b/meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/bitbang.c
new file mode 100644
index 0000000..cf7dcd3
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/bitbang.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+//#define DEBUG
+//#define VERBOSE
+
+#include "bitbang.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "facebook/log.h"
+
+#define NANOSEC_IN_SEC (1000 * 1000 * 1000)
+
+#define BITBANG_FREQ_MAX (500 * 1000 * 1000) /* 500M Hz */
+#define BITBANG_FREQ_DEFAULT (1 * 1000 * 1000) /* 1M Hz */
+
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+
+struct bitbang_handle {
+ bitbang_init_st bbh_init;
+ uint32_t bbh_half_clk; /* ns per clock cycle */
+};
+
+void bitbang_init_default(bitbang_init_st *init)
+{
+ memset(init, sizeof(*init), 0);
+ init->bbi_clk_start = BITBANG_PIN_HIGH;
+ init->bbi_data_out = BITBANG_CLK_EDGE_FALLING;
+ init->bbi_data_in = BITBANG_CLK_EDGE_RISING;
+ init->bbi_freq = BITBANG_FREQ_DEFAULT;
+}
+
+bitbang_handle_st* bitbang_open(const bitbang_init_st *init)
+{
+ bitbang_handle_st *hdl;
+
+ if (!init || !init->bbi_pin_f
+ || !init->bbi_freq || init->bbi_freq > BITBANG_FREQ_MAX) {
+ LOG_ERR(EINVAL, "Invalid init structure");
+ return NULL;
+ }
+
+ hdl = calloc(1, sizeof(*hdl));
+ if (!hdl) {
+ return NULL;
+ }
+
+ hdl->bbh_init = *init;
+ hdl->bbh_half_clk = NANOSEC_IN_SEC / init->bbi_freq / 2;
+
+ LOG_DBG("Bitbang open with initial %s, data out at %s, data in at %s, "
+ "freq at %uHz, half clk %uns",
+ (init->bbi_clk_start == BITBANG_PIN_LOW) ? "LOW" : "HIGH",
+ (init->bbi_data_out == BITBANG_CLK_EDGE_RISING)
+ ? "RISING" : "FALLING",
+ (init->bbi_data_in == BITBANG_CLK_EDGE_RISING)
+ ? "RISING" : "FALLING",
+ init->bbi_freq, hdl->bbh_half_clk);
+
+ return hdl;
+}
+
+void bitbang_close(bitbang_handle_st *hdl)
+{
+ free(hdl);
+}
+
+/*
+ * The threshold (ns) to use spin instead of nanosleep().
+ * Before adding the high resolution timer support, either spin or nanosleep()
+ * will not bring the process wakeup within 10ms. It turns out the system time
+ * update is also controlled by HZ (100).
+ * After I added the high resolution timer support, the spin works as the
+ * system time is updated more frequently. However, nanosleep() solution is
+ * still noticable slower comparing with spin. There could be some kernel
+ * scheduling tweak missing. Did not get time on that yet.
+ * For now, use 10ms as the threshold to determine if spin or nanosleep()
+ * is used.
+ */
+#define BITBANG_SPIN_THRESHOLD (10 * 1000 * 1000)
+
+static int sleep_ns(uint32_t clk)
+{
+ struct timespec req, rem;
+ int rc = 0;
+ if (clk <= BITBANG_SPIN_THRESHOLD) {
+ struct timespec orig;
+ rc = clock_gettime(CLOCK_MONOTONIC, &req);
+ orig = req;
+ while (!rc && clk) {
+ uint32_t tmp;
+ rc = clock_gettime(CLOCK_MONOTONIC, &rem);
+ tmp = (rem.tv_sec - req.tv_sec) * NANOSEC_IN_SEC;
+ if (rem.tv_nsec >= req.tv_nsec) {
+ tmp += rem.tv_nsec - req.tv_nsec;
+ } else {
+ tmp -= req.tv_nsec - rem.tv_nsec;
+ }
+ if (tmp >= clk) {
+ break;
+ }
+ clk -= tmp;
+ req = rem;
+ }
+ } else {
+ req.tv_sec = 0;
+ req.tv_nsec = clk;
+ while ((rc = nanosleep(&req, &rem)) == -1 && errno == EINTR) {
+ req = rem;
+ }
+ }
+ if (rc == -1) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to sleep %u nanoseconds", clk);
+ }
+ return rc;
+}
+
+int bitbang_io(const bitbang_handle_st *hdl, bitbang_io_st *io)
+{
+ int rc = 0;
+ uint32_t clk = hdl->bbh_half_clk;
+ const struct {
+ bitbang_pin_value_en value;
+ bitbang_clk_edge_en edge;
+ } clks[] = {
+ {BITBANG_PIN_HIGH, BITBANG_CLK_EDGE_FALLING},
+ {BITBANG_PIN_LOW, BITBANG_CLK_EDGE_RISING},
+ };
+ int clk_idx;
+ int n_clk = 0;
+ int n_bits = 0;
+ const uint8_t *dout = io->bbio_dout;
+ uint8_t *din = io->bbio_din;
+ int bit_pos = 7;
+ bitbang_pin_func pin_f = hdl->bbh_init.bbi_pin_f;
+ void *context = hdl->bbh_init.bbi_context;
+
+ if ((io->bbio_in_bits == 0 && io->bbio_din)
+ || (io->bbio_in_bits > 0 && !io->bbio_din)) {
+ rc = EINVAL;
+ LOG_ERR(rc, "Incorrect in bits and in buffer");
+ goto out;
+ }
+
+ if ((io->bbio_out_bits == 0 && io->bbio_dout)
+ || (io->bbio_out_bits > 0 && !io->bbio_dout)) {
+ rc = EINVAL;
+ LOG_ERR(rc, "Incorrect out bits and out buffer");
+ goto out;
+ }
+
+ if (io->bbio_in_bits == 0 && io->bbio_out_bits == 0) {
+ rc = EINVAL;
+ LOG_ERR(rc, "Both in and out bits are 0");
+ goto out;
+ }
+
+ if (hdl->bbh_init.bbi_clk_start == BITBANG_PIN_HIGH) {
+ clk_idx = 0;
+ } else {
+ clk_idx = 1;
+ }
+
+ /* set the CLK pin start position */
+ pin_f(BITBANG_CLK_PIN, clks[clk_idx].value, context);
+
+ /* clear the first byte of din */
+ if (din && io->bbio_in_bits) {
+ memset(din, 0, (io->bbio_in_bits + 7) / 8);
+ }
+
+ do {
+ if ((rc = sleep_ns(clk))) {
+ goto out;
+ }
+
+ /* output first */
+ if (hdl->bbh_init.bbi_data_out == clks[clk_idx].edge) {
+ if (dout && n_bits < io->bbio_out_bits) {
+ pin_f(BITBANG_DATA_OUT, (*dout >> bit_pos) & 0x1, context);
+ }
+ }
+
+ /* then, input */
+ if (hdl->bbh_init.bbi_data_in == clks[clk_idx].edge) {
+ if (din && n_bits < io->bbio_in_bits) {
+ *din |= (pin_f(BITBANG_DATA_IN, 0, context) & 0x1) << bit_pos;
+ }
+ }
+
+ if (++n_clk % 2 == 0) {
+ /* one bit for every 2 half clks */
+ n_bits ++;
+ if (bit_pos == 0) {
+ if (dout) {
+ dout++;
+ }
+ if (din) {
+ din++;
+ }
+ bit_pos = 7;
+ } else {
+ bit_pos --;
+ }
+ }
+ clk_idx = 1 - clk_idx;
+ pin_f(BITBANG_CLK_PIN, clks[clk_idx].value, context);
+ } while (n_bits < MAX(io->bbio_in_bits, io->bbio_out_bits));
+
+ out:
+
+ return -rc;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/bitbang.h b/meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/bitbang.h
new file mode 100644
index 0000000..0f21a49
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/bitbang.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef BITBANG_H
+#define BITBANG_H
+
+#include <stdint.h>
+
+typedef enum {
+ BITBANG_CLK_PIN,
+ BITBANG_DATA_IN,
+ BITBANG_DATA_OUT,
+} bitbang_pin_type_en;
+
+typedef enum {
+ BITBANG_PIN_LOW = 0,
+ BITBANG_PIN_HIGH = 1,
+} bitbang_pin_value_en;
+
+typedef enum {
+ BITBANG_CLK_EDGE_RISING,
+ BITBANG_CLK_EDGE_FALLING,
+} bitbang_clk_edge_en;
+
+typedef bitbang_pin_value_en (* bitbang_pin_func)(
+ bitbang_pin_type_en pin, bitbang_pin_value_en value, void *context);
+
+typedef struct {
+ bitbang_pin_value_en bbi_clk_start;
+ bitbang_clk_edge_en bbi_data_out;
+ bitbang_clk_edge_en bbi_data_in;
+ uint32_t bbi_freq;
+ bitbang_pin_func bbi_pin_f;
+ void *bbi_context;
+} bitbang_init_st;
+
+typedef struct bitbang_handle bitbang_handle_st;
+
+void bitbang_init_default(bitbang_init_st *init);
+bitbang_handle_st* bitbang_open(const bitbang_init_st *init);
+void bitbang_close(bitbang_handle_st *hdl);
+
+typedef struct {
+ uint32_t bbio_in_bits;
+ uint32_t bbio_out_bits;
+ uint8_t *bbio_dout;
+ uint8_t *bbio_din;
+} bitbang_io_st;
+
+int bitbang_io(const bitbang_handle_st *hdl, bitbang_io_st *io);
+
+#endif
diff --git a/meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/gpio.c b/meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/gpio.c
new file mode 100644
index 0000000..026aebc
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/gpio.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+//#define DEBUG
+//#define VERBOSE
+
+#include "gpio.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+
+#include "facebook/log.h"
+
+void gpio_init_default(gpio_st *g) {
+ g->gs_gpio = -1;
+ g->gs_fd = -1;
+}
+
+int gpio_open(gpio_st *g, int gpio)
+{
+ char buf[128];
+ int rc;
+
+ snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%u/value", gpio);
+ rc = open(buf, O_RDWR);
+ if (rc == -1) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to open %s", buf);
+ return -rc;
+ }
+ g->gs_fd = rc;
+ g->gs_gpio = gpio;
+ return 0;
+}
+
+void gpio_close(gpio_st *g)
+{
+ if (g && g->gs_fd != -1) {
+ close(g->gs_fd);
+ }
+ gpio_init_default(g);
+}
+
+gpio_value_en gpio_read(gpio_st *g)
+{
+ char buf[32] = {0};
+ gpio_value_en v;
+ lseek(g->gs_fd, 0, SEEK_SET);
+ read(g->gs_fd, buf, sizeof(buf));
+ v = atoi(buf) ? GPIO_VALUE_HIGH : GPIO_VALUE_LOW;
+ LOG_VER("read gpio=%d value=%d %d", g->gs_gpio, atoi(buf), v);
+ return v;
+}
+
+void gpio_write(gpio_st *g, gpio_value_en v)
+{
+ lseek(g->gs_fd, 0, SEEK_SET);
+ write(g->gs_fd, (v == GPIO_VALUE_HIGH) ? "1" : "0", 1);
+ LOG_VER("write gpio=%d value=%d", g->gs_gpio, v);
+}
+
+int gpio_change_direction(gpio_st *g, gpio_direction_en dir)
+{
+ char buf[128];
+ char *val;
+ int fd = -1;
+ int rc = 0;
+
+ snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%u/direction", g->gs_gpio);
+ fd = open(buf, O_WRONLY);
+ if (fd == -1) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to open %s", buf);
+ return -rc;
+ }
+
+ val = (dir == GPIO_DIRECTION_IN) ? "in" : "out";
+ write(fd, val, strlen(val));
+
+ LOG_VER("change gpio=%d direction=%s", g->gs_gpio, val);
+
+ out:
+ if (fd != -1) {
+ close(fd);
+ }
+ return -rc;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/gpio.h b/meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/gpio.h
new file mode 100644
index 0000000..3303986
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/gpio.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef GPIO_H
+#define GPIO_H
+
+typedef struct {
+ int gs_gpio;
+ int gs_fd;
+} gpio_st;
+
+typedef enum {
+ GPIO_DIRECTION_IN,
+ GPIO_DIRECTION_OUT,
+} gpio_direction_en;
+
+typedef enum {
+ GPIO_VALUE_LOW = 0,
+ GPIO_VALUE_HIGH = 1,
+} gpio_value_en;
+
+int gpio_open(gpio_st* g, int gpio);
+void gpio_close(gpio_st *g);
+gpio_value_en gpio_read(gpio_st *g);
+void gpio_write(gpio_st *g, gpio_value_en v);
+int gpio_change_direction(gpio_st *g, gpio_direction_en dir);
+
+#endif
diff --git a/meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/mdio_bb.c b/meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/mdio_bb.c
new file mode 100644
index 0000000..ef0c567
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/mdio_bb.c
@@ -0,0 +1,336 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+//#define DEBUG
+//#define VERBOSE
+
+#include "bitbang.h"
+#include "gpio.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "facebook/log.h"
+
+
+typedef struct {
+ gpio_st m_mdc;
+ gpio_st m_mdio;
+} mdio_context_st;
+
+/*
+ * 32b preamble, 2b start of frame, 2b operation code,
+ * 5b phy addr, 5b register addr, 2b turnaround, 16b data
+ */
+#define N_BITS (32 + 2 + 2 + 5 + 5 + 2 + 16)
+#define N_BYTES (N_BITS + 7 / 8)
+
+#define START_OF_FRAME 0x1
+#define OP_READ 0x2
+#define OP_WRITE 0x1
+#define TURNAROUND 0x2 /* TA for write, for read, phy sends out TA */
+
+void usage()
+{
+ fprintf(stderr,
+ "Usage:\n"
+ "mdio-bb: -c <GPIO for MDC> [-C <HIGH|low>]\n"
+ " -d <GPIO for MDIO> [-O <rising|FALLING>]\n"
+ " [-I <RISING|falling>] [-p] [-b]\n"
+ " <read|write> <phy address> <register address>\n"
+ " [value to write]\n");
+}
+
+bitbang_pin_value_en mdio_pin_f(
+ bitbang_pin_type_en pin, bitbang_pin_value_en value, void *context)
+{
+ mdio_context_st *ctx = (mdio_context_st *)context;
+ gpio_st *gpio;
+ bitbang_pin_value_en res;
+
+ switch (pin) {
+ case BITBANG_CLK_PIN:
+ gpio = &ctx->m_mdc;
+ break;
+ case BITBANG_DATA_IN:
+ case BITBANG_DATA_OUT:
+ gpio = &ctx->m_mdio;
+ break;
+ }
+ if (pin == BITBANG_DATA_IN) {
+ res = gpio_read(gpio) ? BITBANG_PIN_HIGH : BITBANG_PIN_LOW;
+ } else {
+ res = value;
+ gpio_write(gpio, ((res == BITBANG_PIN_HIGH)
+ ? GPIO_VALUE_HIGH : GPIO_VALUE_LOW));
+ }
+ return res;
+}
+
+int main(int argc, char* const argv[])
+{
+ int opt;
+ int mdc = -1, mdio = -1;
+ bitbang_pin_value_en mdc_start = BITBANG_PIN_HIGH;
+ bitbang_clk_edge_en out_edge = BITBANG_CLK_EDGE_FALLING;
+ bitbang_clk_edge_en in_edge = BITBANG_CLK_EDGE_RISING;
+ int is_write;
+ uint32_t phy_addr;
+ uint32_t reg_addr;
+ uint32_t data; /* data to write/read*/
+ uint8_t buf[N_BYTES];
+ uint8_t *buf_p;
+ mdio_context_st ctx;
+ bitbang_init_st init;
+ bitbang_handle_st *hdl = NULL;
+ bitbang_io_st io;
+ int n_bits;
+ int i;
+ int rc = 0;
+ int preamble = 0;
+ int binary = 0;
+
+ while ((opt = getopt(argc, argv, "bc:C:d:D:p")) != -1) {
+ switch (opt) {
+ case 'b':
+ binary = 1;
+ break;
+ case 'c':
+ mdc = atoi(optarg);
+ break;
+ case 'C':
+ if (!strcasecmp(optarg, "high")) {
+ mdc_start = BITBANG_PIN_HIGH;
+ } else if (!strcasecmp(optarg, "low")) {
+ mdc_start = BITBANG_PIN_LOW;
+ } else {
+ usage();
+ exit(-1);
+ }
+ break;
+ case 'd':
+ mdio = atoi(optarg);
+ break;
+ case 'I':
+ if (!strcasecmp(optarg, "rising")) {
+ in_edge = BITBANG_CLK_EDGE_RISING;
+ } if (!strcasecmp(optarg, "falling")) {
+ in_edge = BITBANG_CLK_EDGE_FALLING;
+ } else {
+ usage();
+ exit(-1);
+ }
+ break;
+ case 'O':
+ if (!strcasecmp(optarg, "rising")) {
+ out_edge = BITBANG_CLK_EDGE_RISING;
+ } if (!strcasecmp(optarg, "falling")) {
+ out_edge = BITBANG_CLK_EDGE_FALLING;
+ } else {
+ usage();
+ exit(-1);
+ }
+ break;
+ case 'p':
+ preamble = 1;
+ break;
+ default:
+ usage();
+ exit(-1);
+ }
+ }
+
+ if (mdc < 0 || mdio < 0) {
+ usage();
+ exit(-1);
+ }
+
+ if (optind + 2 >= argc) {
+ usage();
+ exit(-1);
+ }
+
+ /* read or write */
+ if (!strcasecmp(argv[optind], "read")) {
+ is_write = 0;
+ } else if (!strcasecmp(argv[optind], "write")) {
+ is_write = 1;
+ } else {
+ usage();
+ exit(-1);
+ }
+
+ /* phy address, 5 bits only, so must be <= 0x1f */
+ phy_addr = strtoul(argv[optind + 1], NULL, 0);
+ if (phy_addr > 0x1f) {
+ usage();
+ exit(-1);
+ }
+
+ /* register address, 5 bits only, so must be <= 0x1f */
+ reg_addr = strtoul(argv[optind + 2], NULL, 0);
+ if (reg_addr > 0x1f) {
+ usage();
+ exit(-1);
+ }
+
+ /* data */
+ if (is_write) {
+ if ((!binary && (optind + 4 != argc)) ||
+ (binary && (optind + 3 != argc))) {
+ usage();
+ exit(-1);
+ }
+ if (binary) {
+ uint16_t temp = 0;
+ if (fread(&temp, sizeof(temp), 1, stdin) != 1) {
+ usage();
+ exit(-1);
+ }
+ data = htons(temp);
+ } else {
+ data = strtoul(argv[optind + 3], NULL, 0);
+ }
+ if (data > 0xFFFF) {
+ usage();
+ exit(-1);
+ }
+ } else {
+ if ((!binary && (optind + 3 != argc)) ||
+ (binary && (optind + 2 != argc))) {
+ usage();
+ exit(-1);
+ }
+ }
+
+ /* open all gpio */
+ memset(&ctx, sizeof(ctx), 0);
+ gpio_init_default(&ctx.m_mdc);
+ gpio_init_default(&ctx.m_mdio);
+ if (gpio_open(&ctx.m_mdc, mdc) || gpio_open(&ctx.m_mdio, mdio)) {
+ goto out;
+ }
+
+ if (gpio_change_direction(&ctx.m_mdc, GPIO_DIRECTION_OUT)
+ || gpio_change_direction(&ctx.m_mdio, GPIO_DIRECTION_OUT)) {
+ goto out;
+ }
+
+ bitbang_init_default(&init);
+ init.bbi_clk_start = mdc_start;
+ init.bbi_data_out = out_edge;
+ init.bbi_data_in = in_edge;
+ init.bbi_freq = 1000 * 1000; /* 1M Hz */
+ init.bbi_pin_f = mdio_pin_f;
+ init.bbi_context = &ctx;
+ hdl = bitbang_open(&init);
+ if (!hdl) {
+ goto out;
+ }
+
+ if (is_write) {
+ buf[0] = (data >> 8) & 0xFF;
+ buf[1] = data & 0xFF;
+ io.bbio_out_bits = 16;
+ io.bbio_dout = buf;
+ io.bbio_in_bits = 0;
+ io.bbio_din = NULL;
+ } else {
+ io.bbio_in_bits = 16;
+ io.bbio_din = buf;
+ io.bbio_out_bits = 0;
+ io.bbio_dout = NULL;
+ }
+
+ /* preamble, 32b */
+ buf_p = buf;
+ n_bits = 0;
+ if (preamble) {
+ /* 32 bit of 1 for preamble */
+ for (i = 0; i < 4; i++) {
+ *buf_p++ = 0xFF;
+ }
+ n_bits += 32;
+ }
+
+ /*
+ * MDIO transaction header is:
+ * 2b START, 2b OPER CODE, 5b PHY ADDR, 5b register addr, 2b TURNROUND
+ */
+ *buf_p++ = (START_OF_FRAME << 6) | (((is_write) ? OP_WRITE : OP_READ) << 4)
+ | ((phy_addr >> 1) & 0xF);
+ *buf_p++ = ((phy_addr & 0x1) << 7) | ((reg_addr & 0x1F) << 2) | TURNAROUND;
+ if (is_write) {
+ *buf_p++ = (data >> 8) & 0xFF;
+ *buf_p++ = data & 0xFF;
+ /* total # of bits is transaction header + 2 bytes to write */
+ n_bits += 2 + 2 + 5 + 5 + 2 + 16;
+ } else {
+ /* for read, master does not send TR, so, n_bits should not include TR */
+ n_bits += 2 + 2 + 5 + 5;
+ }
+
+ memset(&io, sizeof(io), 0);
+ io.bbio_out_bits = n_bits;
+ io.bbio_dout = buf;
+ io.bbio_in_bits = 0;
+ io.bbio_din = NULL;
+
+ rc = bitbang_io(hdl, &io);
+ if (rc != 0) {
+ goto out;
+ }
+
+ /* for read, need to do another io for (2b TR + 16b data) reading */
+ if (!is_write) {
+ /* first, change the MDIO to input */
+ gpio_change_direction(&ctx.m_mdio, GPIO_DIRECTION_IN);
+ /* then, run the clock for read */
+ memset(&io, sizeof(io), 0);
+ io.bbio_out_bits = 0;
+ io.bbio_dout = NULL;;
+ io.bbio_in_bits = 18;
+ io.bbio_din = buf;
+
+ rc = bitbang_io(hdl, &io);
+ if (rc != 0) {
+ goto out;
+ }
+
+ data = ((buf[0] << 2) | (buf[1] >> 6)) & 0xFF;
+ data <<= 8;
+ data |= ((buf[1] << 2) | (buf[2] >> 6)) & 0xFF;
+ }
+
+ if (binary) {
+ if (!is_write) {
+ uint16_t temp = ntohs(data);
+ fwrite(&temp, sizeof(temp), 1, stdout);
+ }
+ } else {
+ printf("%s: 0x%02x\n", (is_write) ? "Wrote" : "Read", data);
+ }
+
+ out:
+ if (hdl) {
+ bitbang_close(hdl);
+ }
+ gpio_close(&ctx.m_mdc);
+ gpio_close(&ctx.m_mdio);
+
+ return 0;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/spi_bb.c b/meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/spi_bb.c
new file mode 100644
index 0000000..ce366a5
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/bitbang/files/src/spi_bb.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+//#define DEBUG
+//#define VERBOSE
+
+#include "bitbang.h"
+#include "gpio.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "facebook/log.h"
+
+void usage()
+{
+ fprintf(stderr,
+ "Usage:\n"
+ "spi-bb: -s <GPIO for CS> [-S <HIGH|low>]\n"
+ " -c <GPIO for CLK> [-C <HIGH|low>]\n"
+ " -o <GPIO for MOSI> [-O <rising|FALLING>]\n"
+ " -i <GPIO for MISO> [-I <RISING|falling>]\n"
+ " [-b]\n"
+ " < [-r <number of bits to read>]\n"
+ " [-w <number of bits to write> <byte 1> [... byte N]>\n\n"
+ "Note: If both '-r' and '-w' are provided, 'write' will be performed\n"
+ " before 'read'.\n");
+}
+
+typedef struct {
+ gpio_st sc_clk;
+ gpio_st sc_mosi;
+ gpio_st sc_miso;
+} spi_context_st;
+
+bitbang_pin_value_en spi_pin_f(
+ bitbang_pin_type_en pin, bitbang_pin_value_en value, void *context)
+{
+ spi_context_st *ctx = (spi_context_st *)context;
+ gpio_st *gpio;
+ bitbang_pin_value_en res;
+
+ switch (pin) {
+ case BITBANG_CLK_PIN:
+ gpio = &ctx->sc_clk;
+ break;
+ case BITBANG_DATA_IN:
+ gpio = &ctx->sc_miso;
+ break;
+ case BITBANG_DATA_OUT:
+ gpio = &ctx->sc_mosi;
+ break;
+ }
+ if (pin == BITBANG_DATA_IN) {
+ res = gpio_read(gpio) ? BITBANG_PIN_HIGH : BITBANG_PIN_LOW;
+ } else {
+ res = value;
+ gpio_write(gpio, ((res == BITBANG_PIN_HIGH)
+ ? GPIO_VALUE_HIGH : GPIO_VALUE_LOW));
+ }
+ return res;
+}
+
+int main(int argc, char * const argv[])
+{
+ bitbang_init_st init;
+ bitbang_handle_st *hdl = NULL;
+ int cs = -1, clk = -1, in = -1, out = -1;
+ gpio_st cs_gpio;
+ int opt;
+ int is_write = 0;
+ int is_read = 0;
+ int read_bits = 0;
+ int write_bits = 0;
+ int read_bytes = 0;
+ int write_bytes = 0;
+ int i;
+ uint8_t *read_buf = NULL;;
+ uint8_t *write_buf = NULL;;
+ bitbang_clk_edge_en dout_edge = BITBANG_CLK_EDGE_FALLING;
+ bitbang_clk_edge_en din_edge = BITBANG_CLK_EDGE_RISING;
+ bitbang_pin_value_en clk_start = BITBANG_PIN_HIGH;
+ bitbang_pin_value_en cs_value = BITBANG_PIN_HIGH;
+ spi_context_st ctx;
+ bitbang_io_st io;
+ int rc = 0;
+ int binary = 0;
+
+ memset(&ctx, sizeof(ctx), 0);
+ gpio_init_default(&ctx.sc_clk);
+ gpio_init_default(&ctx.sc_mosi);
+ gpio_init_default(&ctx.sc_miso);
+ gpio_init_default(&cs_gpio);
+
+ while ((opt = getopt(argc, argv, "bs:S:c:C:o:O:i:I:w:r:")) != -1) {
+ switch (opt) {
+ case 'b':
+ binary = 1;
+ break;
+ case 's':
+ cs = atoi(optarg);
+ break;
+ case 'S':
+ if (!strcmp(optarg, "high")) {
+ cs_value = BITBANG_PIN_HIGH;
+ } else if (!strcmp(optarg, "low")) {
+ cs_value = BITBANG_PIN_LOW;
+ } else {
+ usage();
+ exit(-1);
+ }
+ break;
+ case 'c':
+ clk = atoi(optarg);
+ break;
+ case 'C':
+ if (!strcasecmp(optarg, "high")) {
+ clk_start = BITBANG_PIN_HIGH;
+ } else if (!strcasecmp(optarg, "low")) {
+ clk_start = BITBANG_PIN_LOW;
+ } else {
+ usage();
+ exit(-1);
+ }
+ break;
+ case 'o':
+ out = atoi(optarg);
+ break;
+ case 'O':
+ if (!strcasecmp(optarg, "rising")) {
+ dout_edge = BITBANG_CLK_EDGE_RISING;
+ } else if (!strcasecmp(optarg, "falling")) {
+ dout_edge = BITBANG_CLK_EDGE_FALLING;
+ } else {
+ usage();
+ exit(-1);
+ }
+ break;
+ case 'i':
+ in = atoi(optarg);
+ break;
+ case 'I':
+ if (!strcasecmp(optarg, "rising")) {
+ din_edge = BITBANG_CLK_EDGE_RISING;
+ } else if (!strcasecmp(optarg, "falling")) {
+ din_edge = BITBANG_CLK_EDGE_FALLING;
+ } else {
+ usage();
+ exit(-1);
+ }
+ break;
+ case 'w':
+ is_write = 1;
+ write_bits = atoi(optarg);
+ if (write_bits <= 0) {
+ usage();
+ exit(-1);
+ }
+ break;
+ case 'r':
+ is_read = 1;
+ read_bits = atoi(optarg);
+ if (read_bits <= 0) {
+ usage();
+ exit(-1);
+ }
+ break;
+ default:
+ usage();
+ exit(-1);
+ }
+ }
+
+ if (clk < 0 || in < 0 || out < 0) {
+ usage();
+ exit(-1);
+ }
+
+ if ((!is_read && !is_write)) {
+ usage();
+ exit(-1);
+ }
+
+ write_bytes = ((write_bits + 7) / 8);
+ if (write_bytes) {
+ write_buf = calloc(write_bytes, sizeof(uint8_t));
+ if (!write_buf) {
+ goto out;
+ }
+ if (binary) {
+ size_t written_bytes;
+ written_bytes = fread(write_buf, sizeof(*write_buf), write_bytes, stdin);
+ if( written_bytes != write_bytes ) {
+ goto out;
+ }
+ } else {
+ for (i = 0; i < write_bytes && i + optind < argc; i++) {
+ write_buf[i] = strtoul(argv[i + optind], NULL, 0);
+ }
+ }
+ }
+
+ read_bytes = ((read_bits + 7) / 8);
+ if (read_bytes) {
+ read_buf = calloc(read_bytes, sizeof(uint8_t));
+ if (!read_buf) {
+ goto out;
+ }
+ }
+
+ if (gpio_open(&ctx.sc_clk, clk) || gpio_open(&ctx.sc_miso, in)
+ || gpio_open(&ctx.sc_mosi, out)) {
+ goto out;
+ }
+
+ /* change GPIO directions, only MISO is input, all others are output */
+ if (gpio_change_direction(&ctx.sc_clk, GPIO_DIRECTION_OUT)
+ || gpio_change_direction(&ctx.sc_miso, GPIO_DIRECTION_IN)
+ || gpio_change_direction(&ctx.sc_mosi, GPIO_DIRECTION_OUT)) {
+ goto out;
+ }
+
+ if (cs != -1) {
+ if (gpio_open(&cs_gpio, cs)) {
+ goto out;
+ }
+ if (gpio_change_direction(&cs_gpio, GPIO_DIRECTION_OUT)) {
+ goto out;
+ }
+ }
+
+ bitbang_init_default(&init);
+ init.bbi_clk_start = clk_start;
+ init.bbi_data_out = dout_edge;
+ init.bbi_data_in = din_edge;
+ init.bbi_freq = 1000 * 1000; /* 1M Hz */
+ init.bbi_pin_f = spi_pin_f;
+ init.bbi_context = &ctx;
+
+ hdl = bitbang_open(&init);
+ if (!hdl) {
+ goto out;
+ }
+
+ if (cs != -1) {
+ /* have chip select */
+ gpio_write(&cs_gpio, ((cs_value == BITBANG_PIN_HIGH)
+ ? GPIO_VALUE_HIGH : GPIO_VALUE_LOW));
+ }
+
+ memset(&io, sizeof(io), 0);
+ io.bbio_in_bits = read_bits;
+ io.bbio_din = read_buf;
+ io.bbio_out_bits = write_bits;
+ io.bbio_dout = write_buf;
+
+ rc = bitbang_io(hdl, &io);
+ if (rc != 0) {
+ goto out;
+ }
+
+ if (binary) {
+ fwrite(read_buf, sizeof(*read_buf), read_bytes, stdout);
+ } else {
+ if (write_bits) {
+ printf("Wrote %u bits:", write_bits);
+ for (i = 0; i < write_bytes; i++) {
+ printf(" %02x", write_buf[i]);
+ }
+ printf("\n");
+ }
+
+ if (read_bits) {
+ printf("Read %u bits:", read_bits);
+ for (i = 0; i < read_bytes; i++) {
+ printf(" %02x", read_buf[i]);
+ }
+ printf("\n");
+ }
+ }
+
+ out:
+ if (hdl) {
+ bitbang_close(hdl);
+ }
+ gpio_close(&ctx.sc_clk);
+ gpio_close(&ctx.sc_miso);
+ gpio_close(&ctx.sc_mosi);
+ if (cs != -1) {
+ /* reset have chip select */
+ gpio_write(&cs_gpio, ((cs_value == BITBANG_PIN_HIGH)
+ ? GPIO_VALUE_LOW : GPIO_VALUE_HIGH));
+ gpio_close(&cs_gpio);
+ }
+
+ if (read_buf) {
+ free(read_buf);
+ }
+ if (write_buf) {
+ free(write_buf);
+ }
+ return rc;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/Makefile b/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/Makefile
new file mode 100644
index 0000000..fa9a8f4
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/Makefile
@@ -0,0 +1,10 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+all: fand
+
+fand: fand.cpp watchdog.cpp
+ $(CXX) -pthread -o $@ $^ $(LDFLAGS) -lwedge_eeprom
+
+.PHONY: clean
+
+clean:
+ rm -rf *.o fand
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/README b/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/README
new file mode 100644
index 0000000..2a92b9d
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/README
@@ -0,0 +1,5 @@
+The AST PWM/Tach driver is in the kernel sources at drivers/hwmon/ast_pwm_fan.c
+
+There are 7 PWM output pins. Each PWM can be configured in one of 3 types (M,
+N, or O). The clock settings for each type are configurable. See init_pwm.sh
+for more comments about how we configure the settings.
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/fand.cpp b/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/fand.cpp
new file mode 100644
index 0000000..24e107c
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/fand.cpp
@@ -0,0 +1,851 @@
+/*
+ * fand
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Daemon to manage the fan speed to ensure that we stay within a reasonable
+ * temperature range. We're using a simplistic algorithm to get started:
+ *
+ * If the fan is already on high, we'll move it to medium if we fall below
+ * a top temperature. If we're on medium, we'll move it to high
+ * if the temperature goes over the top value, and to low if the
+ * temperature falls to a bottom level. If the fan is on low,
+ * we'll increase the speed if the temperature rises to the top level.
+ *
+ * To ensure that we're not just turning the fans up, then back down again,
+ * we'll require an extra few degrees of temperature drop before we lower
+ * the fan speed.
+ *
+ * We check the RPM of the fans against the requested RPMs to determine
+ * whether the fans are failing, in which case we'll turn up all of
+ * the other fans and report the problem..
+ *
+ * TODO: Implement a PID algorithm to closely track the ideal temperature.
+ * TODO: Determine if the daemon is already started.
+ */
+
+/* Yeah, the file ends in .cpp, but it's a C program. Deal. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <syslog.h>
+#include "watchdog.h"
+
+#include "facebook/wedge_eeprom.h"
+
+/* Sensor definitions */
+
+#define INTERNAL_TEMPS(x) ((x) * 1000) // stored a C * 1000
+#define EXTERNAL_TEMPS(x) ((x) / 1000)
+
+#define I2C_BUS_3_DIR "/sys/class/i2c-adapter/i2c-3/"
+#define I2C_BUS_4_DIR "/sys/class/i2c-adapter/i2c-4/"
+
+#define INTAKE_TEMP_DEVICE I2C_BUS_3_DIR "3-0048"
+#define T2_TEMP_DEVICE I2C_BUS_3_DIR "3-0049"
+#define EXHAUST_TEMP_DEVICE I2C_BUS_3_DIR "3-004a"
+#define USERVER_TEMP_DEVICE I2C_BUS_4_DIR "4-0040"
+
+/*
+ * The sensor for the uServer CPU is not on the CPU itself, so it reads
+ * a little low. We are special casing this, but we should obviously
+ * be thinking about a way to generalize these tweaks, and perhaps
+ * the entire configuration. JSON file?
+ */
+
+#define USERVER_TEMP_FUDGE INTERNAL_TEMPS(10)
+
+#define BAD_TEMP INTERNAL_TEMPS(-60)
+
+#define BAD_READ_THRESHOLD 4 /* How many times can reads fail */
+#define FAN_FAILURE_THRESHOLD 4 /* How many times can a fan fail */
+
+#define PWM_DIR "/sys/devices/platform/ast_pwm_tacho.0"
+
+#define PWM_UNIT_MAX 96
+
+#define LARGEST_DEVICE_NAME 120
+
+#define GPIO_USERVER_POWER_DIRECTION "/sys/class/gpio/gpio25/direction"
+#define GPIO_USERVER_POWER "/sys/class/gpio/gpio25/value"
+#define GPIO_T2_POWER_DIRECTION "/sys/class/gpio/gpio41/direction"
+#define GPIO_T2_POWER "/sys/class/gpio/gpio41/value"
+
+#define GPIO_FAN0_LED "/sys/class/gpio/gpio53/value"
+#define GPIO_FAN1_LED "/sys/class/gpio/gpio54/value"
+#define GPIO_FAN2_LED "/sys/class/gpio/gpio55/value"
+#define GPIO_FAN3_LED "/sys/class/gpio/gpio72/value"
+
+const char *fan_led[] = {GPIO_FAN0_LED, GPIO_FAN1_LED,
+ GPIO_FAN2_LED, GPIO_FAN3_LED};
+
+#define FAN_LED_RED "0"
+#define FAN_LED_BLUE "1"
+
+#define GPIO_PIN_ID "/sys/class/gpio/gpio%d/value"
+#define REV_IDS 3
+#define GPIO_REV_ID_START 192
+
+#define BOARD_IDS 4
+#define GPIO_BOARD_ID_START 160
+
+/*
+ * With hardware revisions after 3, we use a different set of pins for
+ * the BOARD_ID.
+ */
+
+#define REV_ID_NEW_BOARD_ID 3
+#define GPIO_BOARD_ID_START_NEW 166
+
+#define REPORT_TEMP 720 /* Report temp every so many cycles */
+
+/* Sensor limits and tuning parameters */
+
+#define INTAKE_LIMIT INTERNAL_TEMPS(60)
+#define T2_LIMIT INTERNAL_TEMPS(95)
+#define USERVER_LIMIT INTERNAL_TEMPS(75)
+
+#define TEMP_TOP INTERNAL_TEMPS(70)
+#define TEMP_BOTTOM INTERNAL_TEMPS(40)
+
+/*
+ * Toggling the fan constantly will wear it out (and annoy anyone who
+ * can hear it), so we'll only turn down the fan after the temperature
+ * has dipped a bit below the point at which we'd otherwise switch
+ * things up.
+ */
+
+#define COOLDOWN_SLOP INTERNAL_TEMPS(6)
+
+#define FAN_LOW 35
+#define FAN_MEDIUM 50
+#define FAN_HIGH 70
+#define FAN_MAX 99
+
+/*
+ * Mapping physical to hardware addresses for fans; it's different for
+ * RPM measuring and PWM setting, naturally. Doh.
+ */
+
+int fan_to_rpm_map[] = {3, 2, 0, 1};
+int fan_to_pwm_map[] = {7, 6, 0, 1};
+
+#define FANS 4
+
+/*
+ * The measured RPM of the fans doesn't match linearly to the requested
+ * rate. In addition, there are coaxially mounted fans, so the rear fans
+ * feed into the front fans. The rear fans will run slower since they're
+ * grabbing still air, and the front fants are getting an extra boost.
+ *
+ * We'd like to measure the fan RPM and compare it to the expected RPM
+ * so that we can detect failed fans, so we have a table (derived from
+ * hardware testing):
+ */
+
+struct rpm_to_pct_map {
+ ushort pct;
+ ushort rpm;
+};
+
+struct rpm_to_pct_map rpm_front_map[] = {{30, 6150},
+ {35, 7208},
+ {40, 8195},
+ {45, 9133},
+ {50, 10017},
+ {55, 10847},
+ {60, 11612},
+ {65, 12342},
+ {70, 13057},
+ {75, 13717},
+ {80, 14305},
+ {85, 14869},
+ {90, 15384},
+ {95, 15871},
+ {100, 16095}};
+
+struct rpm_to_pct_map rpm_rear_map[] = {{30, 3911},
+ {35, 4760},
+ {40, 5587},
+ {45, 6434},
+ {50, 7295},
+ {55, 8187},
+ {60, 9093},
+ {65, 10008},
+ {70, 10949},
+ {75, 11883},
+ {80, 12822},
+ {85, 13726},
+ {90, 14690},
+ {95, 15516},
+ {100, 15897}};
+
+#define FAN_FAILURE_OFFSET 30
+
+int fan_low = FAN_LOW;
+int fan_medium = FAN_MEDIUM;
+int fan_high = FAN_HIGH;
+int fan_max = FAN_MAX;
+int total_fans = FANS;
+int fan_offset = 0;
+
+int temp_bottom = TEMP_BOTTOM;
+int temp_top = TEMP_TOP;
+
+int report_temp = REPORT_TEMP;
+bool verbose = false;
+
+void usage() {
+ fprintf(stderr,
+ "fand [-v] [-l <low-pct>] [-m <medium-pct>] "
+ "[-h <high-pct>]\n"
+ "\t[-b <temp-bottom>] [-t <temp-top>] [-r <report-temp>]\n\n"
+ "\tlow-pct defaults to %d%% fan\n"
+ "\tmedium-pct defaults to %d%% fan\n"
+ "\thigh-pct defaults to %d%% fan\n"
+ "\ttemp-bottom defaults to %dC\n"
+ "\ttemp-top defaults to %dC\n"
+ "\treport-temp defaults to every %d measurements\n\n"
+ "fand compensates for uServer temperature reading %d degrees low\n"
+ "kill with SIGUSR1 to stop watchdog\n",
+ fan_low,
+ fan_medium,
+ fan_high,
+ EXTERNAL_TEMPS(temp_bottom),
+ EXTERNAL_TEMPS(temp_top),
+ report_temp,
+ EXTERNAL_TEMPS(USERVER_TEMP_FUDGE));
+ exit(1);
+}
+
+/* We need to open the device each time to read a value */
+
+int read_device(const char *device, int *value) {
+ FILE *fp;
+ int rc;
+
+ fp = fopen(device, "r");
+ if (!fp) {
+ int err = errno;
+
+ syslog(LOG_INFO, "failed to open device %s", device);
+ return err;
+ }
+
+ rc = fscanf(fp, "%d", value);
+ fclose(fp);
+
+ if (rc != 1) {
+ syslog(LOG_INFO, "failed to read device %s", device);
+ return ENOENT;
+ } else {
+ return 0;
+ }
+}
+
+/* We need to open the device again each time to write a value */
+
+int write_device(const char *device, const char *value) {
+ FILE *fp;
+ int rc;
+
+ fp = fopen(device, "w");
+ if (!fp) {
+ int err = errno;
+
+ syslog(LOG_INFO, "failed to open device for write %s", device);
+ return err;
+ }
+
+ rc = fputs(value, fp);
+ fclose(fp);
+
+ if (rc < 0) {
+ syslog(LOG_INFO, "failed to write device %s", device);
+ return ENOENT;
+ } else {
+ return 0;
+ }
+}
+
+int read_temp(const char *device, int *value) {
+ char full_name[LARGEST_DEVICE_NAME + 1];
+
+ /* We set an impossible value to check for errors */
+ *value = BAD_TEMP;
+ snprintf(
+ full_name, LARGEST_DEVICE_NAME, "%s/temp1_input", device);
+ return read_device(full_name, value);
+}
+
+int read_gpio_value(const int id, const char *device, int *value) {
+ char full_name[LARGEST_DEVICE_NAME];
+
+ snprintf(full_name, LARGEST_DEVICE_NAME, device, id);
+ return read_device(full_name, value);
+}
+
+int read_gpio_values(const int start, const int count,
+ const char *device, int *result) {
+ int status = 0;
+ int value;
+
+ *result = 0;
+ for (int i = 0; i < count; i++) {
+ status |= read_gpio_value(start + i, GPIO_PIN_ID, &value);
+ *result |= value << i;
+ }
+ return status;
+}
+
+int read_ids(int *rev_id, int *board_id) {
+ int status = 0;
+ int value;
+
+ status = read_gpio_values(GPIO_REV_ID_START, REV_IDS, GPIO_PIN_ID, rev_id);
+ if (status != 0) {
+ syslog(LOG_INFO, "failed to read rev_id");
+ return status;
+ }
+
+ int board_id_start;
+ if (*rev_id >= REV_ID_NEW_BOARD_ID) {
+ board_id_start = GPIO_BOARD_ID_START_NEW;
+ } else {
+ board_id_start = GPIO_BOARD_ID_START;
+ }
+
+ status = read_gpio_values(board_id_start, BOARD_IDS, GPIO_PIN_ID, board_id);
+ if (status != 0) {
+ syslog(LOG_INFO, "failed to read board_id");
+ }
+ return status;
+}
+
+bool is_two_fan_board(bool verbose) {
+ struct wedge_eeprom_st eeprom;
+ /* Retrieve the board type from EEPROM */
+ if (wedge_eeprom_parse(NULL, &eeprom) == 0) {
+ /* able to parse EEPROM */
+ if (verbose) {
+ syslog(LOG_INFO, "board type is %s", eeprom.fbw_location);
+ }
+ /* only WEDGE is NOT two-fan board */
+ return strncasecmp(eeprom.fbw_location, "wedge",
+ sizeof(eeprom.fbw_location));
+ } else {
+ int status;
+ int board_id = 0;
+ int rev_id = 0;
+ /*
+ * Could not parse EEPROM. Most likely, it is an old HW without EEPROM.
+ * In this case, use board ID to distinguish if it is wedge or 6-pack.
+ */
+ status = read_ids(&rev_id, &board_id);
+ if (verbose) {
+ syslog(LOG_INFO, "rev ID %d, board id %d", rev_id, board_id);
+ }
+ if (status == 0 && board_id != 0xf) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
+
+int read_fan_value(const int fan, const char *device, int *value) {
+ char device_name[LARGEST_DEVICE_NAME];
+ char output_value[LARGEST_DEVICE_NAME];
+ char full_name[LARGEST_DEVICE_NAME];
+
+ snprintf(device_name, LARGEST_DEVICE_NAME, device, fan);
+ snprintf(full_name, LARGEST_DEVICE_NAME, "%s/%s", PWM_DIR, device_name);
+ return read_device(full_name, value);
+}
+
+int write_fan_value(const int fan, const char *device, const int value) {
+ char full_name[LARGEST_DEVICE_NAME];
+ char device_name[LARGEST_DEVICE_NAME];
+ char output_value[LARGEST_DEVICE_NAME];
+
+ snprintf(device_name, LARGEST_DEVICE_NAME, device, fan);
+ snprintf(full_name, LARGEST_DEVICE_NAME, "%s/%s", PWM_DIR, device_name);
+ snprintf(output_value, LARGEST_DEVICE_NAME, "%d", value);
+ return write_device(full_name, output_value);
+}
+
+/* Return fan speed as a percentage of maximum -- not necessarily linear. */
+
+int fan_rpm_to_pct(const struct rpm_to_pct_map *table,
+ const int table_len,
+ int rpm) {
+ int i;
+
+ for (i = 0; i < table_len; i++) {
+ if (table[i].rpm > rpm) {
+ break;
+ }
+ }
+
+ /*
+ * If the fan RPM is lower than the lowest value in the table,
+ * we may have a problem -- fans can only go so slow, and it might
+ * have stopped. In this case, we'll return an interpolated
+ * percentage, as just returning zero is even more problematic.
+ */
+
+ if (i == 0) {
+ return (rpm * table[i].pct) / table[i].rpm;
+ } else if (i == table_len) { // Fell off the top?
+ return table[i - 1].pct;
+ }
+
+ // Interpolate the right percentage value:
+
+ int percent_diff = table[i].pct - table[i - 1].pct;
+ int rpm_diff = table[i].rpm - table[i - 1].rpm;
+ int fan_diff = table[i].rpm - rpm;
+
+ return table[i].pct - (fan_diff * percent_diff / rpm_diff);
+}
+
+int fan_speed_okay(const int fan, const int speed, const int slop) {
+ int front_fan, rear_fan;
+ int front_pct, rear_pct;
+ int real_fan;
+ int okay;
+
+ /*
+ * The hardware fan numbers are different from the physical order
+ * in the box, so we have to map them:
+ */
+
+ real_fan = fan_to_rpm_map[fan];
+
+ front_fan = 0;
+ rear_fan = 0;
+ read_fan_value(real_fan, "tacho%d_rpm", &front_fan);
+ read_fan_value(real_fan + 4, "tacho%d_rpm", &rear_fan);
+
+ front_pct =
+ fan_rpm_to_pct(rpm_front_map,
+ sizeof(rpm_front_map) / sizeof(struct rpm_to_pct_map),
+ front_fan);
+ rear_pct =
+ fan_rpm_to_pct(rpm_rear_map,
+ sizeof(rpm_rear_map) / sizeof(struct rpm_to_pct_map),
+ rear_fan);
+
+ /*
+ * If the fans are broken, the measured rate will be rather
+ * different from the requested rate, and we can turn up the
+ * rest of the fans to compensate. The slop is the percentage
+ * of error that we'll tolerate.
+ *
+ * XXX: I suppose that we should only measure negative values;
+ * running too fast isn't really a problem.
+ */
+
+ okay = (abs(front_pct - speed) * 100 / speed < slop &&
+ abs(rear_pct - speed) * 100 / speed < slop);
+
+ if (!okay || verbose) {
+ syslog(!okay ? LOG_ALERT : LOG_INFO,
+ "fan %d rear %d (%d%%), front %d (%d%%), expected %d",
+ fan,
+ rear_fan,
+ rear_pct,
+ front_fan,
+ front_pct,
+ speed);
+ }
+
+ return okay;
+}
+
+/* Set fan speed as a percentage */
+
+int write_fan_speed(const int fan, const int value) {
+ /*
+ * The hardware fan numbers for pwm control are different from
+ * both the physical order in the box, and the mapping for reading
+ * the RPMs per fan, above.
+ */
+
+ int real_fan = fan_to_pwm_map[fan];
+
+ if (value == 0) {
+ return write_fan_value(real_fan, "pwm%d_en", 0);
+ } else {
+ int unit = value * PWM_UNIT_MAX / 100;
+ int status;
+
+ if (unit == PWM_UNIT_MAX)
+ unit = 0;
+
+ if ((status = write_fan_value(real_fan, "pwm%d_type", 0)) != 0 ||
+ (status = write_fan_value(real_fan, "pwm%d_rising", 0)) != 0 ||
+ (status = write_fan_value(real_fan, "pwm%d_falling", unit)) != 0 ||
+ (status = write_fan_value(real_fan, "pwm%d_en", 1)) != 0) {
+ return status;
+ }
+ }
+}
+
+/* Set up fan LEDs */
+
+int write_fan_led(const int fan, const char *color)
+{
+ return write_device(fan_led[fan], color);
+}
+
+int server_shutdown(const char *why) {
+ int fan;
+ for (fan = 0; fan < total_fans; fan++) {
+ write_fan_speed(fan + fan_offset, fan_max);
+ }
+
+ syslog(LOG_EMERG, "Shutting down: %s", why);
+ write_device(GPIO_USERVER_POWER_DIRECTION, "out");
+ write_device(GPIO_USERVER_POWER, "0");
+ /*
+ * Putting T2 in reset generating a non-maskable interrupt to uS,
+ * the kernel running on uS might panic depending on its version.
+ * sleep 5s here to make sure uS is completely down.
+ */
+ sleep(5);
+ write_device(GPIO_T2_POWER_DIRECTION, "out");
+ write_device(GPIO_T2_POWER, "0");
+
+ /*
+ * We have to stop the watchdog, or the system will be automatically
+ * rebooted some seconds after fand exits (and stops kicking the
+ * watchdog).
+ */
+
+ stop_watchdog();
+
+ sleep(2);
+ exit(2);
+}
+
+/* Gracefully shut down on receipt of a signal */
+
+void fand_interrupt(int sig)
+{
+ int fan;
+ for (fan = 0; fan < total_fans; fan++) {
+ write_fan_speed(fan + fan_offset, fan_max);
+ }
+
+ syslog(LOG_ALERT, "Shutting down fand on signal %s", strsignal(sig));
+ if (sig == SIGUSR1) {
+ stop_watchdog();
+ }
+ exit(3);
+}
+
+int main(int argc, char **argv) {
+ /* Sensor values */
+
+ int intake_temp;
+ int exhaust_temp;
+ int t2_temp;
+ int userver_temp;
+
+ int fan_speed = FAN_HIGH;
+ int bad_reads = 0;
+ int fan_failure = 0;
+ int fan_speed_changes = 0;
+ int old_speed;
+
+ int fan_bad[FANS];
+ int fan;
+
+ unsigned log_count = 0; // How many times have we logged our temps?
+ int opt;
+ int prev_fans_bad = 0;
+
+ struct sigaction sa;
+
+ sa.sa_handler = fand_interrupt;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGUSR1, &sa, NULL);
+
+ while ((opt = getopt(argc, argv, "l:m:h:b:t:r:v")) != -1) {
+ switch (opt) {
+ case 'l':
+ fan_low = atoi(optarg);
+ break;
+ case 'm':
+ fan_medium = atoi(optarg);
+ break;
+ case 'h':
+ fan_high = atoi(optarg);
+ break;
+ case 'b':
+ temp_bottom = INTERNAL_TEMPS(atoi(optarg));
+ break;
+ case 't':
+ temp_top = INTERNAL_TEMPS(atoi(optarg));
+ break;
+ case 'r':
+ report_temp = atoi(optarg);
+ break;
+ case 'v':
+ verbose = true;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if (optind > argc) {
+ usage();
+ }
+
+ if (temp_bottom > temp_top) {
+ fprintf(stderr,
+ "Should temp-bottom (%d) be higher than "
+ "temp-top (%d)? Starting anyway.\n",
+ EXTERNAL_TEMPS(temp_bottom),
+ EXTERNAL_TEMPS(temp_top));
+ }
+
+ if (fan_low > fan_medium || fan_low > fan_high || fan_medium > fan_high) {
+ fprintf(stderr,
+ "fan RPMs not strictly increasing "
+ "-- %d, %d, %d, starting anyway\n",
+ fan_low,
+ fan_medium,
+ fan_high);
+ }
+
+ daemon(1, 0);
+ openlog("fand", LOG_CONS, LOG_DAEMON);
+
+ /* Start watchdog in manual mode */
+ start_watchdog(0);
+
+ /* Set watchdog to persistent mode so timer expiry will happen independent
+ * of this process's liveliness. */
+ set_persistent_watchdog(WATCHDOG_SET_PERSISTENT);
+
+ if (is_two_fan_board(verbose)) {
+ /* Alternate, two fan configuration */
+ total_fans = 2;
+ fan_offset = 2; /* fan 3 is the first */
+ }
+
+ for (fan = 0; fan < total_fans; fan++) {
+ fan_bad[fan] = 0;
+ write_fan_speed(fan + fan_offset, fan_speed);
+ write_fan_led(fan + fan_offset, FAN_LED_BLUE);
+ }
+
+ sleep(5); /* Give the fans time to come up to speed */
+
+ while (1) {
+ int max_temp;
+ old_speed = fan_speed;
+
+ /* Read sensors */
+
+ read_temp(INTAKE_TEMP_DEVICE, &intake_temp);
+ read_temp(EXHAUST_TEMP_DEVICE, &exhaust_temp);
+ read_temp(T2_TEMP_DEVICE, &t2_temp);
+ read_temp(USERVER_TEMP_DEVICE, &userver_temp);
+
+ /*
+ * uServer can be powered down, but all of the rest of the sensors
+ * should be readable at any time.
+ */
+
+ if ((intake_temp == BAD_TEMP || exhaust_temp == BAD_TEMP ||
+ t2_temp == BAD_TEMP)) {
+ bad_reads++;
+ }
+
+ if (bad_reads > BAD_READ_THRESHOLD) {
+ server_shutdown("Some sensors couldn't be read");
+ }
+
+ if (log_count++ % report_temp == 0) {
+ syslog(LOG_DEBUG,
+ "Temp intake %d, t2 %d, userver %d, exhaust %d, "
+ "fan speed %d, speed changes %d",
+ intake_temp,
+ t2_temp,
+ userver_temp,
+ exhaust_temp,
+ fan_speed,
+ fan_speed_changes);
+ }
+
+ /* Protection heuristics */
+
+ if (intake_temp > INTAKE_LIMIT) {
+ server_shutdown("Intake temp limit reached");
+ }
+
+ if (t2_temp > T2_LIMIT) {
+ server_shutdown("T2 temp limit reached");
+ }
+
+ if (userver_temp + USERVER_TEMP_FUDGE > USERVER_LIMIT) {
+ server_shutdown("uServer temp limit reached");
+ }
+
+ /*
+ * Calculate change needed -- we should eventually
+ * do something more sophisticated, like PID.
+ *
+ * We should use the intake temperature to adjust this
+ * as well.
+ */
+
+ if (t2_temp > userver_temp + USERVER_TEMP_FUDGE) {
+ max_temp = t2_temp;
+ } else {
+ max_temp = userver_temp + USERVER_TEMP_FUDGE;
+ }
+
+ /*
+ * If recovering from a fan problem, spin down fans gradually in case
+ * temperatures are still high. Gradual spin down also reduces wear on
+ * the fans.
+ */
+ if (fan_speed == fan_max) {
+ if (fan_failure == 0) {
+ fan_speed = fan_high;
+ }
+ } else if (fan_speed == fan_high) {
+ if (max_temp + COOLDOWN_SLOP < temp_top) {
+ fan_speed = fan_medium;
+ }
+ } else if (fan_speed == fan_medium) {
+ if (max_temp > temp_top) {
+ fan_speed = fan_high;
+ } else if (max_temp + COOLDOWN_SLOP < temp_bottom) {
+ fan_speed = fan_low;
+ }
+ } else {/* low */
+ if (max_temp > temp_bottom) {
+ fan_speed = fan_medium;
+ }
+ }
+
+ /*
+ * Update fans only if there are no failed ones. If any fans failed
+ * earlier, all remaining fans should continue to run at max speed.
+ */
+
+ if (fan_failure == 0 && fan_speed != old_speed) {
+ syslog(LOG_NOTICE,
+ "Fan speed changing from %d to %d",
+ old_speed,
+ fan_speed);
+ fan_speed_changes++;
+ for (fan = 0; fan < total_fans; fan++) {
+ write_fan_speed(fan + fan_offset, fan_speed);
+ }
+ }
+
+ /*
+ * Wait for some change. Typical I2C temperature sensors
+ * only provide a new value every second and a half, so
+ * checking again more quickly than that is a waste.
+ *
+ * We also have to wait for the fan changes to take effect
+ * before measuring them.
+ */
+
+ sleep(5);
+
+ /* Check fan RPMs */
+
+ for (fan = 0; fan < total_fans; fan++) {
+ /*
+ * Make sure that we're within some percentage
+ * of the requested speed.
+ */
+ if (fan_speed_okay(fan + fan_offset, fan_speed, FAN_FAILURE_OFFSET)) {
+ if (fan_bad[fan] > FAN_FAILURE_THRESHOLD) {
+ write_fan_led(fan + fan_offset, FAN_LED_BLUE);
+ syslog(LOG_NOTICE,
+ "Fan %d has recovered",
+ fan);
+ }
+ fan_bad[fan] = 0;
+ } else {
+ fan_bad[fan]++;
+ }
+ }
+
+ fan_failure = 0;
+ for (fan = 0; fan < total_fans; fan++) {
+ if (fan_bad[fan] > FAN_FAILURE_THRESHOLD) {
+ fan_failure++;
+ write_fan_led(fan + fan_offset, FAN_LED_RED);
+ }
+ }
+
+ if (fan_failure > 0) {
+ if (prev_fans_bad != fan_failure) {
+ syslog(LOG_ALERT, "%d fans failed", fan_failure);
+ }
+
+ /*
+ * If fans are bad, we need to blast all of the
+ * fans at 100%; we don't bother to turn off
+ * the bad fans, in case they are all that is left.
+ *
+ * Note that we have a temporary bug with setting fans to
+ * 100% so we only do fan_max = 99%.
+ */
+
+ fan_speed = fan_max;
+ for (fan = 0; fan < total_fans; fan++) {
+ write_fan_speed(fan + fan_offset, fan_speed);
+ }
+
+ /*
+ * Fans can be hot swapped and replaced; in which case the fan daemon
+ * will automatically detect the new fan and (assuming the new fan isn't
+ * itself faulty), automatically readjust the speeds for all fans down
+ * to a more suitable rpm. The fan daemon does not need to be restarted.
+ */
+ }
+
+ /* Suppress multiple warnings for similar number of fan failures. */
+ prev_fans_bad = fan_failure;
+
+ /* if everything is fine, restart the watchdog countdown. If this process
+ * is terminated, the persistent watchdog setting will cause the system
+ * to reboot after the watchdog timeout. */
+ kick_watchdog();
+ }
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/get_fan_speed.sh b/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/get_fan_speed.sh
new file mode 100755
index 0000000..4e36a7c
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/get_fan_speed.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+#
+# Copyright 2004-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+usage() {
+ echo "Usage: $0 [Fan Unit (0..3)]" >&2
+}
+
+PWM_DIR=/sys/devices/platform/ast_pwm_tacho.0
+set -e
+
+# refer to the comments in init_pwn.sh regarding
+# the fan unit and tacho mapping
+if [ "$#" -eq 0 ]; then
+ TACHOS="0:3 1:2 2:0 3:1"
+elif [ "$#" -eq 1 ]; then
+ case "$1" in
+ "0")
+ TACHOS="0:3"
+ ;;
+ "1")
+ TACHOS="1:2"
+ ;;
+ "2")
+ TACHOS="2:0"
+ ;;
+ "3")
+ TACHOS="3:1"
+ ;;
+ *)
+ usage
+ exit 1
+ ;;
+ esac
+else
+ usage
+ exit 1
+fi
+
+for fan_tacho in $TACHOS; do
+ fan=${fan_tacho%%:*}
+ tacho=${fan_tacho##*:}
+ echo "Fan $fan RPMs: $(cat $PWM_DIR/tacho${tacho}_rpm), $(cat $PWM_DIR/tacho$((tacho+4))_rpm)"
+done
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/init_pwm.sh b/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/init_pwm.sh
new file mode 100755
index 0000000..cf545be
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/init_pwm.sh
@@ -0,0 +1,71 @@
+#!/bin/sh
+#
+# Copyright 2004-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+PWM_DIR=/sys/devices/platform/ast_pwm_tacho.0
+
+set -e
+
+# The PWM frequency is
+
+# clk_source / ((2 ^ division_) * (2 * division_l) * (unit + 1))
+#
+# Our clk_source is 24Mhz. 4-pin fans are generally supposed to be driven with
+# a 25Khz PWM control signal. Therefore we want the divisor to equal 960.
+#
+# We also want the unit to be as large as possible, since this controls the
+# granularity with which we can modulate the PWM signal. The following
+# settings allow us to set the fan from 0 to 100% in increments of 1/96th.
+#
+# The AST chip supports 3 different PWM clock configurations, but we only use
+# type M for now.
+echo 0 > $PWM_DIR/pwm_type_m_division_h
+echo 5 > $PWM_DIR/pwm_type_m_division_l
+echo 95 > $PWM_DIR/pwm_type_m_unit
+
+# On wedge, there are 4 fans connected.
+# Each fan has one PWM input and 2 tacho outputs.
+# Here is the mapping between the fan and PWN/Tacho,
+# staring from the one from the edge
+# Fan 0: PWM 7, Tacho3, Tacho7
+# Fan 1: PWM 6, Tacho2, Tacho6
+# Fan 2: PWM 0, Tacho0, Tacho4
+# Fan 3: PWM 1, Tacho1, Tacho5
+
+# For each fan, setting the type, and 100% initially
+for pwm in 0 1 6 7; do
+ echo 0 > $PWM_DIR/pwm${pwm}_type
+ echo 0 > $PWM_DIR/pwm${pwm}_rising
+ echo 0 > $PWM_DIR/pwm${pwm}_falling
+ echo 1 > $PWM_DIR/pwm${pwm}_en
+done
+
+# Enable Tach 0..7
+echo 0 > $PWM_DIR/tacho0_source
+echo 0 > $PWM_DIR/tacho4_source
+echo 1 > $PWM_DIR/tacho1_source
+echo 1 > $PWM_DIR/tacho5_source
+echo 6 > $PWM_DIR/tacho2_source
+echo 6 > $PWM_DIR/tacho6_source
+echo 7 > $PWM_DIR/tacho3_source
+echo 7 > $PWM_DIR/tacho7_source
+t=0
+while [ $t -le 7 ]; do
+ echo 1 > $PWM_DIR/tacho${t}_en
+ t=$((t+1))
+done
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/set_fan_speed.sh b/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/set_fan_speed.sh
new file mode 100755
index 0000000..a71850f
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/set_fan_speed.sh
@@ -0,0 +1,85 @@
+#!/bin/sh
+#
+# Copyright 2004-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+usage() {
+ echo "Usage: $0 <PERCENT (0..100)> <Fan Unit (0..3)> " >&2
+}
+
+PWM_DIR=/sys/devices/platform/ast_pwm_tacho.0
+
+# The maximum unit setting.
+# This should be the value in pwm_type_m_unit plus 1
+PWM_UNIT_MAX=96
+
+set -e
+
+if [ "$#" -ne 2 ] && [ "$#" -ne 1 ]; then
+ usage
+ exit 1
+fi
+
+# refer to the comments in init_pwn.sh regarding
+# the fan unit and PWM mapping
+if [ "$#" -eq 1 ]; then
+ PWMS="0:7 1:6 2:0 3:1"
+else
+ case "$2" in
+ "0")
+ PWMS="0:7"
+ ;;
+ "1")
+ PWMS="1:6"
+ ;;
+ "2")
+ PWMS="2:0"
+ ;;
+ "3")
+ PWMS="3:1"
+ ;;
+ *)
+ usage
+ exit 1
+ ;;
+ esac
+fi
+
+# Convert the percentage to our 1/96th unit.
+unit=$(( ( $1 * $PWM_UNIT_MAX ) / 100 ))
+
+for FAN_PWM in $PWMS; do
+ FAN_N=${FAN_PWM%%:*}
+ PWM_N=${FAN_PWM##*:}
+ if [ "$unit" -eq 0 ]; then
+ # For 0%, turn off the PWM entirely
+ echo 0 > $PWM_DIR/pwm${PWM_N}_en
+ else
+ if [ "$unit" -eq $PWM_UNIT_MAX ]; then
+ # For 100%, set falling and rising to the same value
+ unit=0
+ fi
+
+ # always use type M. refer to the comments in init_pwm.sh
+ echo 0 > $PWM_DIR/pwm${PWM_N}_type
+ echo 0 > $PWM_DIR/pwm${PWM_N}_rising
+ echo "$unit" > $PWM_DIR/pwm${PWM_N}_falling
+ echo 1 > $PWM_DIR/pwm${PWM_N}_en
+ fi
+
+ echo "Successfully set fan ${FAN_N} (PWM: $PWM_N) speed to $1%"
+done
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/setup-fan.sh b/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/setup-fan.sh
new file mode 100644
index 0000000..0cbdb24
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/setup-fan.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+### BEGIN INIT INFO
+# Provides: setup-fan
+# Required-Start: board-id
+# Required-Stop:
+# Default-Start: S
+# Default-Stop:
+# Short-Description: Set fan speed
+### END INIT INFO
+
+. /usr/local/fbpackages/utils/ast-functions
+
+# Enable the isolation buffer
+wedge_iso_buf_enable
+
+echo -n "Setup fan speed... "
+/usr/local/bin/init_pwm.sh
+/usr/local/bin/set_fan_speed.sh 50
+/usr/local/bin/fand
+echo "done."
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/watchdog.cpp b/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/watchdog.cpp
new file mode 100644
index 0000000..ebb390a
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/watchdog.cpp
@@ -0,0 +1,201 @@
+/*
+ * watchdog
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "watchdog.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#define WATCHDOG_START_KEY "x"
+#define WATCHDOG_STOP_KEY "X"
+/* The "magic close" character (as defined in Linux watchdog specs). */
+#define WATCHDOG_PERSISTENT_KEY "V"
+#define WATCHDOG_NON_PERSISTENT_KEY "a"
+
+static int watchdog_dev = -1;
+
+/* This is needed to prevent rapid consecutive stop/start watchdog calls from
+ * generating multiple threads. */
+static int watchdog_started = 0;
+
+static const char* watchdog_kick_key = WATCHDOG_PERSISTENT_KEY;
+static pthread_t watchdog_tid;
+static pthread_mutex_t watchdog_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* Forward declarations. */
+static void* watchdog_thread(void* args);
+static int kick_watchdog_unsafe();
+
+/*
+ * When started, this background thread will constantly reset the watchdog
+ * at every 5 second interval.
+ */
+
+static void* watchdog_thread(void* args) {
+
+ pthread_detach(pthread_self());
+
+ /* Make sure another instance of the thread hasn't already been started. */
+ pthread_mutex_lock(&watchdog_lock);
+ if (watchdog_started) {
+ goto done;
+ } else {
+ watchdog_started = 1;
+ }
+ pthread_mutex_unlock(&watchdog_lock);
+
+ /* Actual loop for refreshing the watchdog timer. */
+ while (1) {
+ pthread_mutex_lock(&watchdog_lock);
+ if (watchdog_dev != -1) {
+ kick_watchdog_unsafe();
+ } else {
+ break;
+ }
+ pthread_mutex_unlock(&watchdog_lock);
+ sleep(5);
+ }
+
+ /* Broke out of loop because watchdog was stopped. */
+ watchdog_started = 0;
+done:
+ pthread_mutex_unlock(&watchdog_lock);
+ return NULL;
+}
+
+/*
+ * Starts the watchdog timer. timer counts down and restarts the ARM chip
+ * upon timeout. use kick_watchdog() to restart the timer.
+ *
+ * Returns: 1 on success; 0 otherwise.
+ */
+
+int start_watchdog(const int auto_mode) {
+ int status;
+
+ pthread_mutex_lock(&watchdog_lock);
+
+ /* Don't start the watchdog again if it has already been started. */
+ if (watchdog_dev != -1) {
+ while ((status = write(watchdog_dev, WATCHDOG_START_KEY, 1)) == 0
+ && errno == EINTR);
+ pthread_mutex_unlock(&watchdog_lock);
+ syslog(LOG_ALERT, "system watchdog already started.\n");
+ return 0;
+ }
+
+ while (((watchdog_dev = open("/dev/watchdog", O_WRONLY)) == -1) &&
+ errno == EINTR);
+
+ /* Fail if watchdog device is invalid or if the user asked for auto
+ * mode and the thread failed to spawn. */
+ if ((watchdog_dev == -1) ||
+ (auto_mode == 1 && watchdog_started == 0 &&
+ pthread_create(&watchdog_tid, NULL, watchdog_thread, NULL) != 0)) {
+ goto fail;
+ }
+
+ while ((status = write(watchdog_dev, WATCHDOG_START_KEY, 1)) == 0
+ && errno == EINTR);
+ pthread_mutex_unlock(&watchdog_lock);
+ syslog(LOG_INFO, "system watchdog started.\n");
+ return 1;
+
+fail:
+ if (watchdog_dev != -1) {
+ close(watchdog_dev);
+ watchdog_dev = -1;
+ }
+
+ pthread_mutex_unlock(&watchdog_lock);
+ syslog(LOG_ALERT, "system watchdog failed to start!\n");
+ return 0;
+}
+
+/*
+ * Toggles between watchdog persistent modes. In persistent mode, the watchdog
+ * timer will continue to tick even after process shutdown. Under non-
+ * persistent mode, the watchdog timer will automatically be disabled when the
+ * process shuts down.
+ */
+void set_persistent_watchdog(enum watchdog_persistent_en persistent) {
+ switch (persistent) {
+ case WATCHDOG_SET_PERSISTENT:
+ watchdog_kick_key = WATCHDOG_PERSISTENT_KEY;
+ break;
+ default:
+ watchdog_kick_key = WATCHDOG_NON_PERSISTENT_KEY;
+ break;
+ }
+ kick_watchdog();
+}
+
+/*
+ * Restarts the countdown timer on the watchdog, delaying restart by another
+ * timeout period (default: 11 seconds as configured in the device driver).
+ *
+ * This function assumes the watchdog lock has already been acquired and is
+ * only used internally within the watchdog code.
+ *
+ * Returns 1 on success; 0 or -1 indicates failure (check errno).
+ */
+
+static int kick_watchdog_unsafe() {
+ int status = 0;
+ if (watchdog_dev != -1) {
+ while ((status = write(watchdog_dev, watchdog_kick_key, 1)) == 0
+ && errno == EINTR);
+ }
+ return status;
+}
+
+/*
+ * Acquires the watchdog lock and resets the watchdog atomically. For use by
+ * library users.
+ */
+
+int kick_watchdog() {
+ int result;
+ pthread_mutex_lock(&watchdog_lock);
+ result = kick_watchdog_unsafe();
+ pthread_mutex_unlock(&watchdog_lock);
+
+ return result;
+}
+
+/* Shuts down the watchdog gracefully and disables the watchdog timer so that
+ * restarts no longer happen.
+ */
+
+void stop_watchdog() {
+ int status;
+ pthread_mutex_lock(&watchdog_lock);
+ if (watchdog_dev != -1) {
+ while ((status = write(watchdog_dev, WATCHDOG_STOP_KEY, 1)) == 0
+ && errno == EINTR);
+ close(watchdog_dev);
+ watchdog_dev = -1;
+ syslog(LOG_INFO, "system watchdog stopped.\n");
+ }
+ pthread_mutex_unlock(&watchdog_lock);
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/watchdog.h b/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/watchdog.h
new file mode 100644
index 0000000..19b9944
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl/watchdog.h
@@ -0,0 +1,60 @@
+/*
+ * watchdog
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Utility library to handle the aspeed watchdog. Only one watchdog
+ * should be in use at any time throughout the entire system (multiple users
+ * will not cause adverse effects but the behavior of the watchdog becomes
+ * undefined).
+ *
+ * The watchdog can be started in either manual or automatic mode. In manual
+ * mode, the watchdog has to be constantly reset by the user via the
+ * kick_watchdog() function. Under automatic mode, the watchdog will
+ * run in a separate thread and reset the timer on its own so no intervention
+ * is required from the user.
+ *
+ * In both modes, the watchdog timer will not stop when the process is
+ * terminated, unless a call to stop_watchdog() has been made beforehand, or
+ * if the user runs in manual mode and uses a non persistent watchdog kick.
+ *
+ * The default timeout for the watchdog is 11 seconds. When this time period
+ * elapses, the ARM chip is restarted and the kernel is rebooted. Other
+ * hardware state is not reset, so this may introduce strange behavior on
+ * reboot (example: an I2C bus may be left in the open state, triggering
+ * constant interrupts). In rare cases, this could result in the kernel
+ * failing to fully restart itself and thus preclude the possibility of
+ * reinitializing the watchdog timer. Someone will then have to go over and
+ * physically restart the machine.
+ *
+ * The alternative to the soft reset is to request the watchdog device driver
+ * for a hard reset on timeout. However this will stop the fans. If the
+ * kernel fails to fully boot and restart the fan daemon, the system could
+ * overheat. For this reason, we've chosen to take the risk of a stuck soft
+ * reset instead.
+ *
+ */
+
+/* Forward declarations. */
+int start_watchdog(const int auto_mode);
+enum watchdog_persistent_en {
+ WATCHDOG_SET_PERSISTENT,
+ WATCHDOG_SET_NONPERSISTENT,
+};
+void set_persistent_watchdog(enum watchdog_persistent_en persistent);
+int kick_watchdog();
+void stop_watchdog();
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl_0.1.bb b/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl_0.1.bb
new file mode 100644
index 0000000..6e0c980
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fan-ctrl/fan-ctrl_0.1.bb
@@ -0,0 +1,59 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+SUMMARY = "Fan controller"
+DESCRIPTION = "The utilities to control fan."
+SECTION = "base"
+PR = "r1"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://fand.cpp;beginline=6;endline=18;md5=da35978751a9d71b73679307c4d296ec"
+
+DEPENDS_append = "libwedge-eeprom update-rc.d-native"
+
+SRC_URI = "file://get_fan_speed.sh \
+ file://init_pwm.sh \
+ file://set_fan_speed.sh \
+ file://README \
+ file://setup-fan.sh \
+ file://Makefile \
+ file://fand.cpp \
+ file://watchdog.h \
+ file://watchdog.cpp \
+ "
+
+S = "${WORKDIR}"
+
+binfiles = "get_fan_speed.sh \
+ init_pwm.sh \
+ set_fan_speed.sh \
+ fand \
+ "
+
+otherfiles = "README"
+
+pkgdir = "fan_ctrl"
+
+do_install() {
+ dst="${D}/usr/local/fbpackages/${pkgdir}"
+ bin="${D}/usr/local/bin"
+ install -d $dst
+ install -d $bin
+ for f in ${binfiles}; do
+ install -m 755 $f ${dst}/$f
+ ln -snf ../fbpackages/${pkgdir}/$f ${bin}/$f
+ done
+ for f in ${otherfiles}; do
+ install -m 644 $f ${dst}/$f
+ done
+ install -d ${D}${sysconfdir}/init.d
+ install -d ${D}${sysconfdir}/rcS.d
+ install -m 755 setup-fan.sh ${D}${sysconfdir}/init.d/setup-fan.sh
+ update-rc.d -r ${D} setup-fan.sh start 91 S .
+}
+
+FBPACKAGEDIR = "${prefix}/local/fbpackages"
+
+FILES_${PN} = "${FBPACKAGEDIR}/fan_ctrl ${prefix}/local/bin ${sysconfdir} "
+
+# Inhibit complaints about .debug directories for the fand binary:
+
+INHIBIT_PACKAGE_DEBUG_SPLIT = "1"
+INHIBIT_PACKAGE_STRIP = "1"
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fblibs/files/alert_control/Makefile b/meta-facebook/meta-wedge/recipes-wedge/fblibs/files/alert_control/Makefile
new file mode 100644
index 0000000..74ce8f3
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fblibs/files/alert_control/Makefile
@@ -0,0 +1,11 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+lib: libalert_control.so
+
+libalert_control.so: alert_control.c
+ $(CC) $(CCFLAGS) -fPIC -c -o alert_control.o alert_control.c
+ $(CC) -shared -o libalert_control.so alert_control.o -lc
+
+.PHONY: clean
+
+clean:
+ rm -rf *.o libalert_control.so
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fblibs/files/alert_control/alert_control.c b/meta-facebook/meta-wedge/recipes-wedge/fblibs/files/alert_control/alert_control.c
new file mode 100644
index 0000000..81b0329
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fblibs/files/alert_control/alert_control.c
@@ -0,0 +1,117 @@
+/*
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <syslog.h>
+#include "alert_control.h"
+
+#define PATH_ALERT_STATUS "/sys/bus/i2c/drivers/panther_plus/4-0040/alert_status"
+#define PATH_ALERT_CONTROL "/sys/bus/i2c/drivers/panther_plus/4-0040/alert_control"
+
+#define MASK_ALERT_SMS_KCS 0x01
+
+/*
+ * Function to enable/disable alert signal generation for a given Function Block
+ */
+int
+alert_control(e_fbid_t id, e_flag_t cflag) {
+ FILE *fp;
+ unsigned char rbuf[5] = {0};
+ unsigned char tbuf[3] = {0};
+ int count = 0;
+
+ fp = fopen(PATH_ALERT_CONTROL, "r+");
+ if (!fp) {
+ return -1;
+ }
+
+ count = fread(rbuf, sizeof(unsigned char), sizeof(rbuf), fp);
+ if (count == 0x0) {
+ fclose(fp);
+ return -1;
+ }
+
+ // Size of the request
+ tbuf[0] = 0x02;
+
+ switch(id) {
+ case FBID_SMS_KCS:
+ if (cflag == FLAG_ENABLE)
+ tbuf[1] = rbuf[2] | (0x01 << FBID_SMS_KCS);
+ else
+ tbuf[1] = rbuf[2] & (~(0x01 << FBID_SMS_KCS));
+
+ tbuf[2] = rbuf[3];
+ break;
+ // TODO: Add logic for other Function Blocks here
+ default:
+ tbuf[0] = rbuf[2];
+ tbuf[1] = rbuf[3];
+ break;
+ }
+
+ count = fwrite(tbuf, sizeof(unsigned char), sizeof(tbuf), fp);
+ if (count != sizeof(tbuf)) {
+ fclose(fp);
+ return (-1);
+ }
+
+ fclose(fp);
+
+ return 0;
+}
+
+/*
+ * Function to check if the alert for a given Function Block is asserted or not
+ */
+bool
+is_alert_present(e_fbid_t id) {
+ FILE *fp;
+ unsigned char buf[5] = {0};
+ int count = 0;
+
+ fp = fopen(PATH_ALERT_STATUS, "r");
+
+ if (!fp) {
+ return false;
+ }
+
+ count = fread(buf, sizeof(unsigned char), sizeof(buf), fp);
+ if (count == 0x0) {
+ fclose(fp);
+ sleep(2);
+ return false;
+ }
+
+ fclose(fp);
+
+ switch(id) {
+ case FBID_SMS_KCS:
+ if (buf[2] & (0x01 << FBID_SMS_KCS))
+ return true;
+ else
+ return false;
+ //TODO: Add logic for other Function Blocks here
+ default:
+ return false;
+ }
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fblibs/files/alert_control/alert_control.h b/meta-facebook/meta-wedge/recipes-wedge/fblibs/files/alert_control/alert_control.h
new file mode 100644
index 0000000..ca72591
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fblibs/files/alert_control/alert_control.h
@@ -0,0 +1,64 @@
+/*
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ALERT_CONTROL_H__
+#define __ALERT_CONTROL_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdbool.h>
+
+typedef enum {
+ FLAG_DISABLE = 0x00,
+ FLAG_ENABLE,
+} e_flag_t;
+
+typedef enum {
+ FBID_SMS_KCS = 0x00,
+ FBID_SMM_KCS,
+ FBID_COM1_DATA,
+ FBID_COM2_DATA,
+ FBID_COM1_STAT_CTRL,
+ FBID_COM2_STAT_CTRL,
+ FBID_POST,
+ FBID_I2C_PROXY1_MASTER,
+ FBID_I2C_PROXY1_STAT,
+ FBID_I2C_PROXY2_MASTER,
+ FBID_I2C_PROXY2_STAT,
+ FBID_GPIO_CONFIG,
+ FBID_GPIO_OUTPUT,
+ FBID_GPIO_INPUT,
+ FBID_GPIO_INTR,
+ FBID_GPIO_EVENT,
+ FBID_REG_READ,
+ FBID_ALERT_CTRL = 0xFD,
+ FBID_AERT_STAT = 0xFE,
+ FBID_DISCOVERY = 0xFF,
+} e_fbid_t;
+
+int alert_control(e_fbid_t id, e_flag_t cflag);
+bool is_alert_present(e_fbid_t id);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* __ALERT_CONTROL_H__ */
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fblibs/files/ipmi/Makefile b/meta-facebook/meta-wedge/recipes-wedge/fblibs/files/ipmi/Makefile
new file mode 100644
index 0000000..8cb69e7
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fblibs/files/ipmi/Makefile
@@ -0,0 +1,11 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+lib: libipmi.so
+
+libipmi.so: ipmi.c
+ $(CC) $(CCFLAGS) -fPIC -c -o ipmi.o ipmi.c
+ $(CC) -shared -o libipmi.so ipmi.o -lc
+
+.PHONY: clean
+
+clean:
+ rm -rf *.o libipmi.so
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fblibs/files/ipmi/ipmi.c b/meta-facebook/meta-wedge/recipes-wedge/fblibs/files/ipmi/ipmi.c
new file mode 100644
index 0000000..b5a3e19
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fblibs/files/ipmi/ipmi.c
@@ -0,0 +1,81 @@
+/*
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This file contains code to support IPMI2.0 Specificaton available @
+ * http://www.intel.com/content/www/us/en/servers/ipmi/ipmi-specifications.html
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "ipmi.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#define SOCK_PATH "/tmp/ipmi_socket"
+#define MAX_IPMI_RES_LEN 100
+
+/*
+ * Function to handle IPMI messages
+ */
+void
+ipmi_handle(unsigned char *request, unsigned char req_len,
+ unsigned char *response, unsigned char *res_len) {
+
+ int s, t, len;
+ struct sockaddr_un remote;
+
+ // TODO: Need to update to reuse the socket instead of creating new
+ if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+ syslog(LOG_ALERT, "ipmi_handle: socket() failed\n");
+ return;
+ }
+
+ remote.sun_family = AF_UNIX;
+ strcpy(remote.sun_path, SOCK_PATH);
+ len = strlen(remote.sun_path) + sizeof(remote.sun_family);
+
+ if (connect(s, (struct sockaddr *)&remote, len) == -1) {
+ syslog(LOG_ALERT, "ipmi_handle: connect() failed\n");
+ return;
+ }
+
+ if (send(s, request, req_len, 0) == -1) {
+ syslog(LOG_ALERT, "ipmi_handle: send() failed\n");
+ return;
+ }
+
+ if ((t=recv(s, response, MAX_IPMI_RES_LEN, 0)) > 0) {
+ *res_len = t;
+ } else {
+ if (t < 0) {
+ syslog(LOG_ALERT, "ipmi_handle: recv() failed\n");
+ } else {
+ printf("Server closed connection");
+ }
+
+ return;
+ }
+
+ close(s);
+
+ return;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fblibs/files/ipmi/ipmi.h b/meta-facebook/meta-wedge/recipes-wedge/fblibs/files/ipmi/ipmi.h
new file mode 100644
index 0000000..4c6ed62
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fblibs/files/ipmi/ipmi.h
@@ -0,0 +1,35 @@
+/*
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __IPMI_H__
+#define __IPMI_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void ipmi_handle(unsigned char *request, unsigned char req_len,
+ unsigned char *response, unsigned char *res_len);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* __IPMI_H__ */
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fblibs/libalert-control_0.1.bb b/meta-facebook/meta-wedge/recipes-wedge/fblibs/libalert-control_0.1.bb
new file mode 100644
index 0000000..2ae4ea7
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fblibs/libalert-control_0.1.bb
@@ -0,0 +1,24 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+SUMMARY = "Wedge Alert Control Library"
+DESCRIPTION = "library for Wedge Alert Control"
+SECTION = "base"
+PR = "r1"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://alert_control.c;beginline=5;endline=17;md5=da35978751a9d71b73679307c4d296ec"
+
+SRC_URI = "file://alert_control \
+ "
+
+S = "${WORKDIR}/alert_control"
+
+do_install() {
+ install -d ${D}${libdir}
+ install -m 0644 libalert_control.so ${D}${libdir}/libalert_control.so
+
+ install -d ${D}${includedir}/facebook
+ install -m 0644 alert_control.h ${D}${includedir}/facebook/alert_control.h
+}
+
+FILES_${PN} = "${libdir}/libalert_control.so"
+FILES_${PN}-dbg = "${libdir}/.debug"
+FILES_${PN}-dev = "${includedir}/facebook/alert_control.h"
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fblibs/libipmi_0.1.bb b/meta-facebook/meta-wedge/recipes-wedge/fblibs/libipmi_0.1.bb
new file mode 100644
index 0000000..0b6f3d3
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fblibs/libipmi_0.1.bb
@@ -0,0 +1,25 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+SUMMARY = "Wedge IPMI Client Library"
+DESCRIPTION = "library for Wedge IPMI Client"
+SECTION = "base"
+PR = "r1"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://ipmi.c;beginline=8;endline=20;md5=da35978751a9d71b73679307c4d296ec"
+
+
+SRC_URI = "file://ipmi \
+ "
+
+S = "${WORKDIR}/ipmi"
+
+do_install() {
+ install -d ${D}${libdir}
+ install -m 0644 libipmi.so ${D}${libdir}/libipmi.so
+
+ install -d ${D}${includedir}/facebook
+ install -m 0644 ipmi.h ${D}${includedir}/facebook/ipmi.h
+}
+
+FILES_${PN} = "${libdir}/libipmi.so"
+FILES_${PN}-dbg = "${libdir}/.debug"
+FILES_${PN}-dev = "${includedir}/facebook/ipmi.h"
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/fbutils_0.1.bb b/meta-facebook/meta-wedge/recipes-wedge/fbutils/fbutils_0.1.bb
new file mode 100644
index 0000000..753adbe
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/fbutils_0.1.bb
@@ -0,0 +1,86 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+SUMMARY = "Utilities"
+DESCRIPTION = "Various utilities"
+SECTION = "base"
+PR = "r1"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://COPYING;md5=eb723b61539feef013de476e68b5c50a"
+
+SRC_URI = "file://ast-functions \
+ file://us_console.sh \
+ file://sol.sh \
+ file://power_led.sh \
+ file://post_led.sh \
+ file://reset_usb.sh \
+ file://setup-gpio.sh \
+ file://setup_rov.sh \
+ file://mdio.py \
+ file://bcm5396.py \
+ file://bcm5396_util.py \
+ file://mount_data0.sh \
+ file://eth0_mac_fixup.sh \
+ file://wedge_power.sh \
+ file://power-on.sh \
+ file://wedge_us_mac.sh \
+ file://setup_switch.py \
+ file://create_vlan_intf \
+ file://watch-fc.sh \
+ file://fcswitcher.sh \
+ file://rc.early \
+ file://rc.local \
+ file://src \
+ file://COPYING \
+ "
+
+pkgdir = "utils"
+
+S = "${WORKDIR}"
+
+binfiles = "us_console.sh sol.sh power_led.sh post_led.sh \
+ reset_usb.sh mdio.py setup_rov.sh wedge_power.sh wedge_us_mac.sh \
+ bcm5396.py bcm5396_util.py setup_switch.py watch-fc.sh"
+
+DEPENDS_append = "update-rc.d-native"
+
+do_install() {
+ dst="${D}/usr/local/fbpackages/${pkgdir}"
+ install -d $dst
+ install -m 644 ast-functions ${dst}/ast-functions
+ localbindir="${D}/usr/local/bin"
+ install -d ${localbindir}
+ for f in ${binfiles}; do
+ install -m 755 $f ${dst}/${f}
+ ln -s ../fbpackages/${pkgdir}/${f} ${localbindir}/${f}
+ done
+
+ # common lib and include files
+ install -d ${D}${includedir}/facebook
+ install -m 0644 src/include/log.h ${D}${includedir}/facebook/log.h
+ install -m 0644 src/include/i2c-dev.h ${D}${includedir}/facebook/i2c-dev.h
+
+ # init
+ install -d ${D}${sysconfdir}/init.d
+ install -d ${D}${sysconfdir}/rcS.d
+ # the script to mount /mnt/data
+ install -m 0755 ${WORKDIR}/mount_data0.sh ${D}${sysconfdir}/init.d/mount_data0.sh
+ update-rc.d -r ${D} mount_data0.sh start 03 S .
+ install -m 0755 ${WORKDIR}/rc.early ${D}${sysconfdir}/init.d/rc.early
+ update-rc.d -r ${D} rc.early start 04 S .
+ install -m 755 setup-gpio.sh ${D}${sysconfdir}/init.d/setup-gpio.sh
+ update-rc.d -r ${D} setup-gpio.sh start 59 S .
+ # create VLAN intf automatically
+ install -d ${D}/${sysconfdir}/network/if-up.d
+ install -m 755 create_vlan_intf ${D}${sysconfdir}/network/if-up.d/create_vlan_intf
+ # networking is done after rcS, any start level within rcS
+ # for mac fixup should work
+ install -m 755 eth0_mac_fixup.sh ${D}${sysconfdir}/init.d/eth0_mac_fixup.sh
+ update-rc.d -r ${D} eth0_mac_fixup.sh start 70 S .
+ install -m 755 power-on.sh ${D}${sysconfdir}/init.d/power-on.sh
+ update-rc.d -r ${D} power-on.sh start 85 S .
+ install -m 755 fcswitcher.sh ${D}${sysconfdir}/init.d/fcswitcher.sh
+ update-rc.d -r ${D} fcswitcher.sh start 90 S .
+ install -m 0755 ${WORKDIR}/rc.local ${D}${sysconfdir}/init.d/rc.local
+ update-rc.d -r ${D} rc.local start 99 2 3 4 5 .
+}
+
+FILES_${PN} += "/usr/local ${sysconfdir}"
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/COPYING b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/COPYING
new file mode 100644
index 0000000..3912109
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/ast-functions b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/ast-functions
new file mode 100644
index 0000000..dd53ad1
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/ast-functions
@@ -0,0 +1,222 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+DEVMEM=/sbin/devmem
+
+devmem_set_bit() {
+ local addr
+ local val
+ addr=$1
+ val=$($DEVMEM $addr)
+ val=$((val | (0x1 << $2)))
+ $DEVMEM $addr 32 $val
+}
+
+devmem_clear_bit() {
+ local addr
+ local val
+ addr=$1
+ val=$($DEVMEM $addr)
+ val=$((val & ~(0x1 << $2)))
+ $DEVMEM $addr 32 $val
+}
+
+scu_addr() {
+ echo $((0x1E6E2000 + 0x$1))
+}
+
+GPIODIR="/sys/class/gpio"
+GPIOEXPORT="$GPIODIR/export"
+
+gpio_dir() {
+ echo "$GPIODIR/gpio$1"
+}
+
+gpio_name2value() {
+ local first remaining base val
+ remaining=$1
+ val=0
+ while [ -n "$remaining" ]; do
+ first=${remaining:0:1}
+ case "$first" in
+ [[:lower:]])
+ base=$(printf "%d" "'$first'")
+ base=$((base - 96))
+ val=$((val * 26 + base))
+ ;;
+ [[:upper:]])
+ base=$(printf "%d" "'$first'")
+ base=$((base - 64))
+ val=$((val * 26 + base))
+ ;;
+ *)
+ if [ $val -gt 0 ]; then
+ val=$((val-1))
+ fi
+ val=$((val * 8 + $remaining))
+ break
+ ;;
+ esac
+ remaining=${remaining:1}
+ done
+ echo "$val"
+}
+
+gpio_export() {
+ local gpio
+ gpio=$(gpio_name2value $1)
+ dir=$(gpio_dir $gpio)
+ if [ ! -d ${dir} ]; then
+ echo $gpio > $GPIOEXPORT
+ fi
+}
+
+gpio_set() {
+ local gpio
+ local val
+ gpio=$(gpio_name2value $1)
+ val=$2
+ dir=$(gpio_dir $gpio)
+ if [ ! -d ${dir} ]; then
+ echo $gpio > $GPIOEXPORT
+ fi
+ echo out > ${dir}/direction
+ echo $val > ${dir}/value
+}
+
+gpio_get() {
+ local gpio
+ local val
+ gpio=$(gpio_name2value $1)
+ dir=$(gpio_dir $gpio)
+ if [ ! -d ${dir} ]; then
+ echo $gpio > $GPIOEXPORT
+ fi
+ echo in > ${dir}/direction
+ cat ${dir}/value
+}
+
+wedge_iso_buf_enable() {
+ # GPIOC2 (18) to low, SCU90[0] and SCU90[24] must be 0
+ devmem_clear_bit $(scu_addr 90) 0
+ devmem_clear_bit $(scu_addr 90) 24
+ gpio_set 18 0
+}
+
+wedge_iso_buf_disable() {
+ # GPIOC2 (18) to low, SCU90[0] and SCU90[24] must be 0
+ devmem_clear_bit $(scu_addr 90) 0
+ devmem_clear_bit $(scu_addr 90) 24
+ gpio_set 18 1
+}
+
+wedge_is_us_on() {
+ local val n retries prog
+ if [ $# -gt 0 ]; then
+ retries="$1"
+ else
+ retries=1
+ fi
+ if [ $# -gt 1 ]; then
+ prog="$2"
+ else
+ prog=""
+ fi
+ n=1
+ while true; do
+ val=$(cat /sys/class/i2c-adapter/i2c-4/4-0040/gpio_inputs 2>/dev/null)
+ if [ -n "$val" ]; then
+ break
+ fi
+ n=$((n+1))
+ if [ $n -gt $retries ]; then
+ echo -n " failed to read GPIO. "
+ val=0
+ break
+ fi
+ echo -n "$prog"
+ sleep 1
+ done
+ if [ "$((val & (0x1 << 14)))" != "0" ]; then
+ # powered on already
+ return 0
+ else
+ return 1
+ fi
+}
+
+
+# Return the board type, 'LC', 'FC-LEFT', 'FC-RIGHT', or, 'WEDGE'
+wedge_board_type() {
+ local pn
+ pn=$(/usr/bin/weutil 2> /dev/null | grep -i '^Location on Fabric:')
+ case "$pn" in
+ *LEFT*)
+ echo 'FC-LEFT'
+ ;;
+ *RIGHT*)
+ echo 'FC-RIGHT'
+ ;;
+ *LC*)
+ echo 'LC'
+ ;;
+ *)
+ echo 'WEDGE'
+ ;;
+ esac
+}
+
+# On FC, FAB_SLOT_ID (GPIOU0), low == FC0; high == FC1
+# On LC, Wedge,
+# board rev < 3:
+# GPIOU0(ID0), GPIOU1(ID1), GPIOU2(ID2), GPIOU3(ID3)
+# else:
+# GPIOU6(ID0), GPIOU7(ID1), GPIOV0(ID2), GPIOV1(ID3)
+#
+# ID[2:0] ID3 Slot#
+# 000 0 1
+# 000 1 2
+# 001 0 3
+# 001 1 4
+# 010 0 5
+# 010 1 6
+# 011 0 7
+# 011 1 8
+
+wedge_slot_id() {
+ local type slot id3 id2 id1 id0 FC_CARD_BASE board_rev
+ FC_CARD_BASE=65
+ if [ "$1" = "FC-LEFT" ]; then
+ # fabric card left
+ slot=$(gpio_get U0)
+ slot=$((FC_CARD_BASE + slot * 2))
+ elif [ "$1" = "FC-RIGHT" ]; then
+ # fabric card right
+ slot=$(gpio_get U0)
+ slot=$((FC_CARD_BASE + slot * 2 + 1))
+ else
+ # either edge or LC
+ # need to check the board rev
+ board_rev=$(wedge_board_rev)
+ if [ $board_rev -lt 3 ]; then
+ id0=$(gpio_get U0)
+ id1=$(gpio_get U1)
+ id2=$(gpio_get U2)
+ id3=$(gpio_get U3)
+ else
+ id0=$(gpio_get U6)
+ id1=$(gpio_get U7)
+ id2=$(gpio_get V0)
+ id3=$(gpio_get V1)
+ fi
+ slot=$(((id2 * 4 + id1 * 2 + id0) * 2 + id3 + 1))
+ fi
+ echo "$slot"
+}
+
+# wedge_board_rev() is only valid after GPIO Y0, Y1, and Y2 are enabled
+wedge_board_rev() {
+ local val0 val1 val2
+ val0=$(cat /sys/class/gpio/gpio192/value 2>/dev/null)
+ val1=$(cat /sys/class/gpio/gpio193/value 2>/dev/null)
+ val2=$(cat /sys/class/gpio/gpio194/value 2>/dev/null)
+ echo $((val0 | (val1 << 1) | (val2 << 2)))
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/at93c46.py b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/at93c46.py
new file mode 100644
index 0000000..368b807
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/at93c46.py
@@ -0,0 +1,260 @@
+# Copyright 2004-present Facebook. All rights reserved.
+
+import subprocess
+import struct
+import sys
+
+class VerboseLogger:
+ def __init__(self, verbose=False):
+ self.verbose = verbose
+
+ def _verbose_print(self, caption, bytestream=None):
+ '''
+ Print a bytestream to stdout if verbose is enabled.
+ '''
+ if self.verbose:
+ if bytestream is not None:
+ sys.stderr.write("{}: {}\n".format(caption, " ".join(['{:02X}'.format(ord(x)) for x in bytestream])))
+ else:
+ sys.stderr.write("{}\n".format(caption))
+
+class AT93C46SPI(VerboseLogger):
+ '''The class to access AT93C46 through SPI intf'''
+ SPI_CMD = 'spi-bb'
+
+ def __init__(self, bus_width, gpio_cs, gpio_ck, gpio_do, gpio_di,
+ verbose=False):
+ if bus_width != 8 and bus_width != 16:
+ raise Exception("Invalid bus width for AT93C46!")
+
+ self.bus_width = bus_width
+ self.gpio_cs = gpio_cs
+ self.gpio_ck = gpio_ck
+ self.gpio_do = gpio_do
+ self.gpio_di = gpio_di
+ self.verbose = verbose
+
+ self.addr_bits = 6 if self.bus_width == 16 else 7
+ self.addr_mask = 0x3F if self.bus_width == 16 else 0x7F
+
+ def __shift(self, bytestream, value):
+ '''
+ Shift an entire byte stream by value bits.
+ '''
+ binary = "".join(['{:08b}'.format(ord(x)) for x in bytestream])
+ if value > 0:
+ binary = binary[value:] + '0' * value
+ else:
+ binary = '0' * (-value) + binary[:value]
+ return "".join([chr(int(binary[x:x+8],2)) for x in range(0, len(binary), 8)])
+
+ def __io(self, op, addr, data=None):
+ '''
+ Perform an IO operation against the EEPROM
+ '''
+ write_bits = self.addr_bits + 3
+ if data is not None:
+ # If giving data, we are doing a write command so
+ # no need to read any data.
+ write_bits = write_bits + self.bus_width
+ read_bits = 0
+ else:
+ # If not giving data, we are doing either a read
+ # command or a set command, so read the result.
+ # We pad with an extra bit due to a dummy bit introduced
+ # by a delay for address decoding on chip.
+ read_bits = self.addr_bits + 4 + self.bus_width
+
+ # Format the command itself
+ cmd_loc = 6 if self.bus_width == 16 else 7
+ instruction = addr & self.addr_mask
+ instruction = instruction | ((0x4 | (op & 0x3)) << cmd_loc)
+ if data is not None:
+ if self.bus_width == 16:
+ write_data = struct.pack(">HH", instruction, data & 0xFFFF)
+ else:
+ write_data = struct.pack(">HB", instruction, data & 0xFF)
+ else:
+ write_data = struct.pack(">H", instruction)
+ write_data = self.__shift(write_data, 16 - (cmd_loc + 3))
+
+ self._verbose_print("Write data", write_data)
+
+ # Run the command with the bitbang driver
+ if read_bits > 0:
+ data_portion = "-r {} -w {}".format(read_bits, write_bits)
+ else:
+ data_portion = "-w {}".format(write_bits)
+
+ cmd = "{} -s {} -c {} -o {} -i {} -b {}".format(
+ self.SPI_CMD, self.gpio_cs, self.gpio_ck, self.gpio_do,
+ self.gpio_di, data_portion
+ )
+
+ self._verbose_print("Command: {}".format(cmd))
+
+ out = subprocess.Popen(cmd.split(),
+ stdout=subprocess.PIPE,
+ stdin = subprocess.PIPE)\
+ .communicate(input=write_data)
+
+ # Format the response
+ read_data = self.__shift(out[0], cmd_loc + 4)
+ if self.bus_width == 16:
+ read_data = read_data[:2]
+ self._verbose_print("Read data", read_data)
+ return struct.unpack(">H", read_data)[0]
+ else:
+ read_data = read_data[:1]
+ self._verbose_print("Read data", read_data)
+ return struct.unpack(">B", read_data)[0]
+
+ def read(self, addr):
+ return self.__io(0x2, addr)
+
+ def ewen(self):
+ self.__io(0x0, 0x3 << (self.addr_bits - 2))
+
+ def erase(self, addr):
+ self.__io(0x3, addr)
+
+ def write(self, addr, data):
+ self.__io(0x1, addr, data)
+
+ def eral(self):
+ self.__io(0x0, 0x2 << (self.addr_bits - 2))
+
+ def wral(self, data):
+ self.__io(0x0, 0x1 << (self.addr_bits - 2), data)
+
+ def ewds(self):
+ self.__io(0x0, 0x0)
+
+class AT93C46(VerboseLogger):
+ '''
+ The class which handles accessing memory on the AT93C46 chip.
+ '''
+ AT93C46_MEMORY_SIZE = 128
+
+ def __init__(self, bus_width, gpio_cs, gpio_ck, gpio_do, gpio_di,
+ byte_swap, verbose=False):
+ self.bus_width = bus_width
+ self.verbose = verbose
+ self.byte_swap = byte_swap
+
+ self.spi = AT93C46SPI(bus_width=bus_width, gpio_cs=gpio_cs,
+ gpio_ck=gpio_ck, gpio_do=gpio_do,
+ gpio_di=gpio_di, verbose=verbose)
+
+ def __swap(self, value):
+ '''
+ Swap bytes for a 16-bit integer if instructed to do so.
+ '''
+ if self.bus_width == 16:
+ if self.byte_swap:
+ return ((value >> 8) & 0xFF) | ((value << 8) & 0xFF00)
+ else:
+ return value
+ else:
+ return value
+
+ def erase(self, offset=None, limit=None):
+ '''
+ Erase the chip.
+ '''
+ if offset is None:
+ offset = 0
+ if limit is None:
+ limit = self.AT93C46_MEMORY_SIZE
+
+ if offset < 0 or offset + limit > self.AT93C46_MEMORY_SIZE:
+ raise Exception("Erase would be out of bounds!")
+ if self.bus_width == 16 and \
+ ((offset & 1) != 0 or ((offset + limit) & 1) != 0):
+ raise Exception("Erase can't start or end on odd boundary in 16-bit mode!")
+
+ if offset == 0 and limit == self.AT93C46_MEMORY_SIZE:
+ # Special case when we are erasing the entire chip
+ self.spi.ewen()
+ self.spi.eral()
+ self.spi.ewds()
+
+ self._verbose_print("Erased entire chip")
+ else:
+ # Regular case
+ if self.bus_width == 16:
+ real_offset = offset / 2
+ real_limit = limit / 2
+ else:
+ real_offset = offset
+ real_limit = limit
+
+ self.spi.ewen()
+ for addr in range(real_offset, real_offset + real_limit):
+ self.spi.erase(addr)
+ self.spi.ewds()
+
+ self._verbose_print("Erased {} bytes from offset {}".format(limit, offset))
+
+ def read(self, offset=None, limit=None):
+ '''
+ Read the chip into a memory buffer.
+ '''
+ if offset is None:
+ offset = 0
+ if limit is None:
+ limit = self.AT93C46_MEMORY_SIZE
+
+ if offset < 0 or offset + limit > self.AT93C46_MEMORY_SIZE:
+ raise Exception("Read would be out of bounds!")
+ if self.bus_width == 16 and \
+ ((offset & 1) != 0 or ((offset + limit) & 1) != 0):
+ raise Exception("Read can't start or end on odd boundary in 16-bit mode!")
+
+ output = ""
+ if self.bus_width == 16:
+ real_offset = offset / 2
+ real_limit = limit / 2
+ pack_instruction = "=H"
+ else:
+ real_offset = offset
+ real_offset
+ pack_instruction = "=B"
+
+ for addr in range(real_offset, real_offset + real_limit):
+ output = output + struct.pack(pack_instruction, self.__swap(self.spi.read(addr)))
+
+ self._verbose_print("Read {} bytes from offset {}".format(limit, offset), output)
+
+ return output
+
+ def write(self, data, offset=None):
+ '''
+ Write a memory buffer to the chip.
+ '''
+ if offset is None:
+ offset = 0
+
+ if offset < 0 or offset + len(data) > self.AT93C46_MEMORY_SIZE:
+ raise Exception("Write would be out of bounds!")
+ if self.bus_width == 16 and \
+ ((offset & 1) != 0 or ((offset + len(data)) & 1) != 0):
+ raise Exception("Write can't start or end on odd boundary in 16-bit mode!")
+
+ if self.bus_width == 16:
+ offset_divisor = 2
+ pack_instruction = "=H"
+ else:
+ offset_divisor = 1
+ pack_instruction = "=B"
+
+ self.spi.ewen()
+ for addr in range(offset, offset + len(data), offset_divisor):
+ actual_addr = addr / offset_divisor
+ value = self.__swap(struct.unpack(pack_instruction, data[(addr - offset):(addr - offset) + offset_divisor])[0])
+
+ self.spi.erase(actual_addr)
+ self.spi.write(actual_addr, value)
+ self.spi.ewds()
+
+ self._verbose_print("Wrote {} bytes from offset {}".format(len(data), offset), data)
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/at93c46_util.py b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/at93c46_util.py
new file mode 100755
index 0000000..8910002
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/at93c46_util.py
@@ -0,0 +1,178 @@
+#!/usr/bin/python -S
+# Copyright 2004-present Facebook. All rights reserved.
+
+from argparse import ArgumentParser
+from at93c46 import AT93C46, AT93C46SPI
+import sys
+
+def get_raw(args):
+ return AT93C46SPI(args.bus_width, args.cs, args.clk, args.mosi, args.miso,
+ args.verbose)
+
+def get_chip(args):
+ return AT93C46(args.bus_width, args.cs, args.clk, args.mosi, args.miso,
+ args.byte_swap, args.verbose)
+
+def access_parser(ap):
+ # Default, based on currenct HW configuration
+ SPI_CS_DEFAULT = 68
+ SPI_CLK_DEFAULT = 69
+ SPI_MOSI_DEFAULT = 70
+ SPI_MISO_DEFAULT = 71
+
+ spi_group = ap.add_argument_group('SPI Access')
+ spi_group.add_argument('--cs', type=int, default=SPI_CS_DEFAULT,
+ help='The GPIO number for SPI CS pin (default: %s)'
+ % SPI_CS_DEFAULT)
+ spi_group.add_argument('--clk', type=int, default=SPI_CLK_DEFAULT,
+ help='The GPIO number for SPI CLK pin (default: %s)'
+ % SPI_CLK_DEFAULT)
+ spi_group.add_argument('--mosi', type=int, default=SPI_MOSI_DEFAULT,
+ help='The GPIO number for SPI MOSI pin (default: %s)'
+ % SPI_MOSI_DEFAULT)
+ spi_group.add_argument('--miso', type=int, default=SPI_MISO_DEFAULT,
+ help='The GPIO number for SPI MISO pin (default: %s)'
+ % SPI_MISO_DEFAULT)
+
+def bus_width_parser(ap):
+ # Default, based on currenct HW configuration
+ AT83C46_BUS_WIDTH = 16
+
+ bus_group = ap.add_argument_group('Bus Width')
+ bus_group.add_argument('--bus-width', type=int, default=AT83C46_BUS_WIDTH,
+ help='The configured bus width (default: %s)'
+ % AT83C46_BUS_WIDTH)
+
+def read_raw(args):
+ raw = get_raw(args)
+ val = raw.read(args.address)
+
+ if args.int:
+ print "{}".format(val)
+ else:
+ if args.bus_width == 16:
+ print "0x{:04X}".format(val)
+ else:
+ print "0x{:02X}".format(val)
+
+def write_raw(args):
+ if args.value[:2] == "0x":
+ value = int(args.value, 16)
+ else:
+ value = int(args.value)
+
+ raw = get_raw(args)
+ raw.ewen()
+ raw.erase(args.address)
+ raw.write(args.address, value)
+ raw.ewds()
+
+def erase_raw(args):
+ raw = get_raw(args)
+ raw.ewen()
+ raw.erase(args.address)
+ raw.ewds()
+
+def raw_subparser(subparsers):
+ raw_parser = subparsers.add_parser('raw', help='Raw memory access')
+ raw_sub = raw_parser.add_subparsers()
+
+ read_parser = raw_sub.add_parser('read', help='Read a single memory address')
+ read_parser.add_argument('address', type=int, help='The memory address')
+ read_parser.add_argument('--int', action='store_true',
+ help='Display output as an integer')
+ read_parser.set_defaults(func=read_raw)
+
+ write_parser = raw_sub.add_parser('write', help='Write a single memory address')
+ write_parser.add_argument('address', type=int, help='The memory address')
+ write_parser.add_argument('value', type=str, help='The value to write, either integer or hex')
+ write_parser.set_defaults(func=write_raw)
+
+ erase_parser = raw_sub.add_parser('erase', help='Erase a single memory address')
+ erase_parser.add_argument('address', type=int, help='The memory address')
+ erase_parser.set_defaults(func=erase_raw)
+
+def read_chip(args):
+ chip = get_chip(args)
+ data = chip.read(args.start, args.length)
+
+ if args.file is None:
+ sys.stdout.write(data)
+ else:
+ fp = open(args.file, "wb")
+ fp.write(data)
+
+def write_chip(args):
+ chip = get_chip(args)
+
+ # Either way, limit reads to the size of the chip
+ if args.file is None:
+ data = sys.stdin.read(AT93C46.AT93C46_MEMORY_SIZE)
+ else:
+ fp = open(args.file, "rb")
+ data = fp.read(AT93C46.AT93C46_MEMORY_SIZE)
+
+ if args.length is not None:
+ # Make sure length is correct
+ if len(data) < args.length:
+ data = data + '\x00' * (args.length - len(data))
+ if len(data) > args.length:
+ data = data[:args.length]
+
+ chip.write(data, args.start)
+
+def erase_chip(args):
+ chip = get_chip(args)
+ chip.erase(args.start, args.length)
+
+def chip_subparser(subparsers):
+ chip_parser = subparsers.add_parser('chip', help='Chip-level access')
+ chip_sub = chip_parser.add_subparsers()
+
+ read_parser = chip_sub.add_parser('read', help='Read from the chip')
+ read_parser.add_argument('--start', type=int,
+ help='The memory address to start at (default: 0)')
+ read_parser.add_argument('--length', type=int,
+ help='The number of bytes to read (default: whole chip)')
+ read_parser.add_argument('--file', type=str,
+ help='File to operate on (default: stdout)')
+ read_parser.add_argument('--byte-swap', action='store_true',
+ help='Byte swap values for 16-bit reads/writes')
+ read_parser.set_defaults(func=read_chip)
+
+ write_parser = chip_sub.add_parser('write', help='Write to the chip')
+ write_parser.add_argument('--start', type=int,
+ help='The memory address to start at (default: 0)')
+ write_parser.add_argument('--length', type=int,
+ help='The number of bytes to write (default: file length)')
+ write_parser.add_argument('--file', type=str,
+ help='File to operate on (default: stdin)')
+ write_parser.add_argument('--byte-swap', action='store_true',
+ help='Byte swap values for 16-bit reads/writes')
+ write_parser.set_defaults(func=write_chip)
+
+ erase_parser = chip_sub.add_parser('erase', help='Erase the chip')
+ erase_parser.add_argument('--start', type=int,
+ help='The memory address to start at (default: 0)')
+ erase_parser.add_argument('--length', type=int,
+ help='The number of bytes to erase (default: whole chip)')
+ erase_parser.set_defaults(func=erase_chip)
+
+if __name__ == "__main__":
+ # General arguments
+ ap = ArgumentParser()
+ ap.add_argument('--verbose', action='store_true',
+ help='Print verbose debugging information')
+
+ # SPI and bus width arguments
+ access_parser(ap)
+ bus_width_parser(ap)
+
+ # Functionality
+ subparsers = ap.add_subparsers()
+ raw_subparser(subparsers)
+ chip_subparser(subparsers)
+
+ # Command runner
+ args = ap.parse_args()
+ args.func(args)
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/bcm5396.py b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/bcm5396.py
new file mode 100644
index 0000000..e1aba47
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/bcm5396.py
@@ -0,0 +1,452 @@
+#
+# Copyright 2004-present Facebook. All rights reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+import subprocess
+import time
+
+class Bcm5396MDIO:
+ '''The class to access BCM5396 through MDIO intf'''
+ MDIO_CMD = 'mdio-bb'
+
+ PHYADDR = 0x1E
+
+ ACCESS_CTRL_REG = 16
+ IO_CTRL_REG = 17
+ STATUS_REG = 18
+ DATA0_REG = 24
+ DATA1_REG = 25
+ DATA2_REG = 26
+ DATA3_REG = 27
+
+ def __init__(self, mdc, mdio):
+ self.mdc = mdc
+ self.mdio = mdio
+ self.page = -1
+
+ def __io(self, op, reg, val=0):
+ cmd = '%s -p -c %s -d %s %s %s %s' \
+ % (self.MDIO_CMD, self.mdc, self.mdio, op, str(self.PHYADDR),
+ str(reg))
+ if op == 'write':
+ cmd += ' %s' % val
+ out = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)\
+ .communicate()[0]
+ if op == 'write':
+ return val
+ # need to parse the result for read
+ rc = 0
+ for line in out.split('\n'):
+ if not line.startswith('Read:'):
+ continue
+ rc = int(line.split(':')[1], 0)
+ return rc
+
+ def __read_mdio(self, reg):
+ return self.__io('read', reg)
+
+ def __write_mdio(self, reg, val):
+ return self.__io('write', reg, val)
+
+ def __set_page(self, page):
+ if self.page == page:
+ return
+ # Write MII register ACCESS_CTRL_REG:
+ # set bit 0 as "1" to enable MDIO access
+ # set "page number to bit 15:8
+ val = 0x1 | ((page & 0xff) << 8)
+ self.__write_mdio(self.ACCESS_CTRL_REG, val)
+ self.page = page
+
+ def __wait_for_done(self):
+ # Read MII register IO_CTRL_REG:
+ # Check op_code = "00"
+ while (self.__read_mdio(self.IO_CTRL_REG) & 0x3):
+ time.sleep(0.010) # 10ms
+
+ def read(self, page, reg, n_bytes):
+ self.__set_page(page)
+ # Write MII register IO_CTRL_REG:
+ # set "Operation Code as "00"
+ # set "Register Address" to bit 15:8
+ val = 0x00 | ((reg & 0xff) << 8)
+ self.__write_mdio(self.IO_CTRL_REG, val)
+ # Write MII register IO_CTRL_REG:
+ # set "Operation Code as "10"
+ # set "Register Address" to bit 15:8
+ val = 0x2 | ((reg & 0xff) << 8)
+ self.__write_mdio(self.IO_CTRL_REG, val)
+ self.__wait_for_done()
+ # Read MII register DATA0_REG for bit 15:0
+ val = long(self.__read_mdio(self.DATA0_REG))
+ # Read MII register DATA1_REG for bit 31:16
+ val |= self.__read_mdio(self.DATA1_REG) << 16
+ # Read MII register DATA2_REG for bit 47:32
+ val |= self.__read_mdio(self.DATA2_REG) << 32
+ # Read MII register DATA3_REG for bit 63:48
+ val |= self.__read_mdio(self.DATA3_REG) << 48
+ return val
+
+ def write(self, page, reg, val, n_bytes):
+ self.__set_page(page)
+ # Write MII register DATA0_REG for bit 15:0
+ self.__write_mdio(self.DATA0_REG, val & 0xFFFF)
+ # Write MII register DATA1_REG for bit 31:16
+ self.__write_mdio(self.DATA1_REG, (val >> 16) & 0xFFFF)
+ # Write MII register DATA2_REG for bit 47:32
+ self.__write_mdio(self.DATA2_REG, (val >> 32) & 0xFFFF)
+ # Write MII register DATA3_REG for bit 63:48
+ self.__write_mdio(self.DATA3_REG, (val >> 48) & 0xFFFF)
+ # Write MII register IO_CTRL_REG:
+ # set "Operation Code as "00"
+ # set "Register Address" to bit 15:8
+ val = 0x00 | ((reg & 0xff) << 8)
+ self.__write_mdio(self.IO_CTRL_REG, val)
+ # Write MII register IO_CTRL_REG:
+ # set "Operation Code as "01"
+ # set "Register Address" to bit 15:8
+ val = 0x1 | ((reg & 0xff) << 8)
+ self.__write_mdio(self.IO_CTRL_REG, val)
+ self.__wait_for_done()
+
+
+class Bcm5396SPI:
+ '''The class to access BCM5396 through SPI interface'''
+ SPI_CMD = 'spi-bb'
+
+ READ_CMD = 0x60
+ WRITE_CMD = 0x61
+
+ SPI_STS_DIO = 0xF0
+ SPI_STS_REG = 0xFE
+ SPI_STS_REG_RACK = 0x1 << 5
+ SPI_STS_REG_SPIF = 0x1 << 7
+ PAGE_REG = 0xFF
+
+ def __init__(self, cs, clk, mosi, miso):
+ self.cs = cs
+ self.clk = clk
+ self.mosi = mosi
+ self.miso = miso
+ self.page = -1
+
+ def __bytes2val(self, values):
+ # LSB first, MSB last
+ pos = 0
+ result = 0L
+ for byte in values:
+ if type(byte) is str:
+ byte = int(byte, 16)
+ if byte > 255:
+ raise Exception('%s is not a byte in the list %s'\
+ % (byte, values))
+ result |= byte << pos
+ pos += 8
+ return result
+
+ def __val2bytes(self, value, n):
+ result = []
+ for _ in range(n):
+ result.append(value & 0xFF)
+ value >>= 8
+ if value > 0:
+ raise Exception('Value, %s, is too large for %s bytes'
+ % (value, n))
+ return result
+
+ def __io(self, bytes_to_write, to_read=0):
+ # TODO: check parameters
+ cmd = '%s -s %s -S low -c %s -o %s -i %s '\
+ % (self.SPI_CMD, self.cs, self.clk, self.mosi, self.miso)
+ if len(bytes_to_write):
+ write_cmd = '-w %s %s '\
+ % (len(bytes_to_write) * 8,
+ ' '.join([str(byte) for byte in bytes_to_write]))
+ else:
+ write_cmd = ''
+ if to_read:
+ # spi-bb will first return the exact number of bits used for
+ # writing. So, total number of bits to read should also include
+ # the number of bits written.
+ cmd += '-r %s ' % str((len(bytes_to_write) + to_read) * 8)
+ cmd += write_cmd
+ rc = 0L
+ out = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)\
+ .communicate()[0]
+ if to_read:
+ # need to parse the result
+ for line in out.split('\n'):
+ if not line.startswith('Read'):
+ continue
+ res = line.split(':')[1]
+ rc = self.__bytes2val(res.split()[len(bytes_to_write):])
+ break
+ return rc
+
+ def __set_page(self, page):
+ page &= 0xff
+ if self.page == page:
+ return
+ self.__io([self.WRITE_CMD, self.PAGE_REG, page])
+ self.page = page
+
+ def __read_spi_reg(self, reg):
+ reg &= 0xFF
+ return self.__io([self.READ_CMD, reg], 1)
+
+ def __read_spi_sts(self):
+ return self.__read_spi_reg(self.SPI_STS_REG)
+
+ def __read_spi_dio(self):
+ return self.__read_spi_reg(self.SPI_STS_DIO)
+
+ def read(self, page, reg, n_bytes):
+ '''Read a register value from a page.'''
+ if n_bytes > 8:
+ print('TODO to support reading more than 8 bytes')
+ return 0
+ if page > 0xff or reg > 0xff:
+ print('Page and register must be <= 255')
+ return 0
+ try:
+ self.__set_page(page)
+ self.__io([self.READ_CMD, reg], 1)
+ while True:
+ # check sts
+ sts = self.__read_spi_sts()
+ if sts & self.SPI_STS_REG_RACK:
+ break
+ bytes = []
+ for _ in range(n_bytes):
+ bytes.append(self.__read_spi_dio())
+ except Exception as e:
+ print(e)
+ return self.__bytes2val(bytes)
+
+ def write(self, page, reg, val, n_bytes):
+ '''Write a value as n bytes to a register on a page.'''
+ if page > 0xff or reg > 0xff:
+ print('Page and register must be <= 255')
+ return
+ bytes = self.__val2bytes(val, n_bytes)
+ if len(bytes) > 8:
+ print('TODO to support writing more than 8 bytes')
+ return
+ bytes = [self.WRITE_CMD, reg] + bytes
+ try:
+ self.__set_page(page)
+ self.__io(bytes)
+ except Exception as e:
+ print(e)
+
+class Bcm5396:
+ '''The class for BCM5396 Switch'''
+
+ MDIO_ACCESS = 0
+ SPI_ACCESS = 1
+
+ def __init__(self, access, **kwargs):
+ if access == self.MDIO_ACCESS:
+ self.access = Bcm5396MDIO(**kwargs)
+ else:
+ self.access = Bcm5396SPI(**kwargs)
+
+ def write(self, page, reg, value, n_bytes):
+ return self.access.write(page, reg, value, n_bytes)
+
+ def read(self, page, reg, n_bytes):
+ return self.access.read(page, reg, n_bytes)
+
+ def __add_remove_vlan(self, add, vid, untag, fwd, spt):
+ VLAN_PAGE = 0x5
+ CTRL_ADDR = 0x60
+ CTRL_START_DONE = (0x1 << 7)
+ VID_ADDR = 0x61
+ ENTRY_ADDR = 0x63
+
+ fwd_map = self.__ports2portmap(fwd)
+ untag_map = self.__ports2portmap(untag)
+
+ # mark it as write and stop the previous action
+ ctrl = 0
+ self.write(VLAN_PAGE, CTRL_ADDR, ctrl, 1)
+ # write entry
+ if (add):
+ entry = 0x1L | ((spt & 0x1F) << 1) \
+ | (fwd_map << 6) | (untag_map << 23)
+ else:
+ entry = 0x0L
+ self.write(VLAN_PAGE, ENTRY_ADDR, entry, 8)
+ # write vid as the index
+ self.write(VLAN_PAGE, VID_ADDR, vid & 0xFFF, 2)
+ # start the write
+ ctrl = CTRL_START_DONE
+ self.write(VLAN_PAGE, CTRL_ADDR, ctrl, 1)
+ while True:
+ ctrl = self.read(VLAN_PAGE, CTRL_ADDR, 1)
+ if not (ctrl & CTRL_START_DONE):
+ # done
+ break
+ time.sleep(0.010) # 10ms
+
+ def add_vlan(self, vid, untag, fwd, spt=0):
+ return self.__add_remove_vlan(True, vid, untag, fwd, spt)
+
+ def remove_vlan(self, vid):
+ return self.__add_remove_vlan(False, vid, [], [], 0)
+
+ def get_vlan(self, vid):
+ VLAN_PAGE = 0x5
+ CTRL_ADDR = 0x60
+ CTRL_START_DONE = (0x1 << 7)
+ CTRL_READ = 0x1
+ VID_ADDR = 0x61
+ ENTRY_ADDR = 0x63
+
+ # mark it as read and stop the previous action
+ ctrl = CTRL_READ
+ self.write(VLAN_PAGE, CTRL_ADDR, ctrl, 1)
+ # write the vid as the index
+ self.write(VLAN_PAGE, VID_ADDR, vid & 0xFFF, 2)
+ # start the read
+ ctrl = CTRL_READ|CTRL_START_DONE
+ self.write(VLAN_PAGE, CTRL_ADDR, ctrl, 1)
+ while True:
+ ctrl = self.read(VLAN_PAGE, CTRL_ADDR, 1)
+ if not (ctrl & CTRL_START_DONE):
+ # done
+ break
+ time.sleep(0.010) # 10ms
+ entry = self.read(VLAN_PAGE, ENTRY_ADDR, 8)
+ res = {}
+ res['valid'] = True if entry & 0x1 else False
+ res['spt'] = (entry >> 1) & 0x1f
+ res['fwd'] = self.__portmap2ports((entry >> 6) & 0x1ffff)
+ res['untag'] = self.__portmap2ports((entry >> 23) & 0x1ffff)
+ return res
+
+ def __portmap2ports(self, port_map):
+ return list(set([port if port_map & (0x1 << port) else None
+ for port in range (0, 17)])
+ - set([None]))
+
+ def __ports2portmap(self, ports):
+ port_map = 0
+ for port in ports:
+ port_map |= (0x1 << port)
+ return port_map & 0x1FFFF
+
+ def __parse_arl_result(self, vid, result):
+ is_bitset = lambda bit: True if result & (0x1 << bit) else False
+ if not is_bitset(3):
+ return None
+ res = {}
+ # parse vid first
+ res['vid'] = (vid >> 48) & 0xfff
+ mac_val = vid & 0xffffffffffffL
+ mac_list = []
+ for pos in range(5, -1, -1):
+ mac_list.append('{:02x}'.format((mac_val >> (pos * 8)) & 0xff))
+ res['mac'] = ':'.join(mac_list)
+ if mac_val & (0x1 << 40):
+ res['ports'] = self.__portmap2ports((result >> 6) & 0xffff)
+ else:
+ res['ports'] = [(result >> 6) & 0xf]
+ res['static'] = is_bitset(5)
+ res['age'] = is_bitset(4)
+ res['valid'] = is_bitset(3)
+ res['priority'] = result & 0x7
+ return res
+
+ def get_all_arls(self):
+ ARL_PAGE = 0x5
+ SEARCH_CTRL_ADDR = 0x30
+ SEARCH_CTRL_START_DONE = (0x1 << 7)
+ SEARCH_CTRL_SR_VALID = (0x1)
+
+ VID0_ADDR = 0x33
+ RESULT0_ADDR = 0x3B
+ VID1_ADDR = 0x40
+ RESULT1_ADDR = 0x48
+
+ all = []
+ # write START to search control
+ ctrl = SEARCH_CTRL_START_DONE
+ self.write(ARL_PAGE, SEARCH_CTRL_ADDR, ctrl, 1)
+ while True:
+ ctrl = self.read(ARL_PAGE, SEARCH_CTRL_ADDR, 1)
+ if not (ctrl & SEARCH_CTRL_START_DONE):
+ # Done
+ break
+ if not (ctrl & SEARCH_CTRL_SR_VALID):
+ # result is not ready, sleep and retry
+ time.sleep(0.010) # 10ms
+ continue
+ for vid_addr, result_addr in [[VID1_ADDR, RESULT1_ADDR],
+ [VID0_ADDR, RESULT0_ADDR]]:
+ vid = self.read(ARL_PAGE, vid_addr, 8)
+ result = self.read(ARL_PAGE, result_addr, 4)
+ one = self.__parse_arl_result(vid, result)
+ if one:
+ all.append(one)
+ return all
+
+ def vlan_ctrl(self, enable):
+ VLAN_CTRL_PAGE = 0x34
+ VLAN_CTRL0_REG = 0x0
+ VLAN_CTRL0_B_EN_1QVLAN = 0x1 << 7
+
+ ctrl = self.read(VLAN_CTRL_PAGE, VLAN_CTRL0_REG, 1)
+ need_write = False
+ if enable:
+ if not ctrl & VLAN_CTRL0_B_EN_1QVLAN:
+ need_write = True;
+ ctrl |= VLAN_CTRL0_B_EN_1QVLAN
+ else:
+ if ctrl & VLAN_CTRL0_B_EN_1QVLAN:
+ need_write = True;
+ ctrl &= (~VLAN_CTRL0_B_EN_1QVLAN) & 0xFF
+ if need_write:
+ self.write(VLAN_CTRL_PAGE, VLAN_CTRL0_REG, ctrl, 1)
+
+ def vlan_set_port_default(self, port, vid, pri=0):
+ VLAN_PORT_PAGE = 0x34
+ VLAN_PORT_REG_BASE = 0x10
+
+ if port < 0 or port > 16:
+ raise Exception('Invalid port number %s' % port)
+ if pri < 0 or pri > 7:
+ raise Exception('Invalid priority %s' % pri)
+ if vid < 0 or vid > 0xFFF:
+ raise Exception('Invalid VLAN %s' % vid)
+ reg = VLAN_PORT_REG_BASE + port * 2
+ ctrl = (pri << 13) | vid
+ self.write(VLAN_PORT_PAGE, reg, ctrl, 2)
+
+ def vlan_get_port_default(self, port):
+ VLAN_PORT_PAGE = 0x34
+ VLAN_PORT_REG_BASE = 0x10
+
+ if port < 0 or port > 16:
+ raise Exception('Invalid port number %s' % port)
+ reg = VLAN_PORT_REG_BASE + port * 2
+ val = self.read(VLAN_PORT_PAGE, reg, 2)
+ res = {}
+ res['priority'] = (val >> 13) & 0x7
+ res['vid'] = val & 0xFFF
+ return res
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/bcm5396_util.py b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/bcm5396_util.py
new file mode 100644
index 0000000..1496412
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/bcm5396_util.py
@@ -0,0 +1,260 @@
+#!/usr/bin/python
+# Copyright 2004-present Facebook. All rights reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+from argparse import ArgumentParser
+from bcm5396 import Bcm5396
+
+def auto_long(x):
+ return long(x, 0)
+
+def auto_int(x):
+ return int(x, 0)
+
+def get_bcm(args):
+ if args.spi:
+ return Bcm5396(Bcm5396.SPI_ACCESS, cs=args.cs, clk=args.clk,
+ mosi=args.mosi, miso=args.miso)
+ else:
+ return Bcm5396(Bcm5396.MDIO_ACCESS, mdc=args.mdc, mdio=args.mdio)
+
+
+def read_register(args):
+ bcm = get_bcm(args)
+ val = bcm.read(args.page, args.register, args.size)
+ print('Read from BCM5396 ({}:{}.{}): {}'
+ .format(hex(args.page), hex(args.register), args.size, hex(val)))
+
+def write_register(args):
+ bcm = get_bcm(args)
+ val = bcm.write(args.page, args.register, args.value, args.size)
+ print('Write to BCM5396 ({}.{}): {}'
+ .format(hex(args.page), hex(args.register), hex(args.value)))
+
+def register_parser(subparser):
+ reg_parser = subparser.add_parser('register', help='Register IO')
+ reg_sub = reg_parser.add_subparsers()
+
+ read_parser = reg_sub.add_parser('read', help='read switch register')
+ read_parser.set_defaults(func=read_register)
+ read_parser.add_argument('page', type=auto_int,
+ help='The page of the register')
+ read_parser.add_argument('register', type=auto_int,
+ help='The register to read from')
+ read_parser.add_argument('size', type=auto_int,
+ help='Number of bytes',
+ choices=range(1, 9))
+
+ write_parser = reg_sub.add_parser('write', help='write switch register')
+ write_parser.set_defaults(func=write_register)
+ write_parser.add_argument('page', type=auto_int,
+ help='The page oof the register')
+ write_parser.add_argument('register', type=auto_int,
+ help='The register to write to')
+ write_parser.add_argument('value', type=auto_long,
+ help='The value to write')
+ write_parser.add_argument('size', type=auto_int,
+ help='Number of bytes',
+ choices=range(1, 9))
+
+
+def dump_arl(args):
+ bcm = get_bcm(args)
+ arls = bcm.get_all_arls()
+ print('All ARLs are:')
+ for entry in arls:
+ print(entry)
+
+def arl_parser(subparser):
+ dump_parser = subparser.add_parser('arl', help='dump all ARL entries')
+ dump_parser.set_defaults(func=dump_arl)
+
+
+def __parse_port_list(parm):
+ '''Parse the port numbers to a list'''
+ if len(parm) == 0:
+ return []
+ ports=[]
+ for port in parm.split(','):
+ idx = port.find('-')
+ if idx == -1:
+ p = int(port)
+ if p < 0 or p > 15:
+ raise Exception('Invalid port number %s' % p)
+ # just one port
+ ports.append(p)
+ else:
+ start = int(port[:idx])
+ end = int(port[idx+1:])
+ if start > end or start < 0 or end > 15:
+ raise Exception('Invalid port range %s-%s' % (start, end))
+ ports.extend(range(start, end + 1))
+ return ports
+
+def enable_vlan(args):
+ bcm = get_bcm(args)
+ bcm.vlan_ctrl(True)
+ print('VLAN function is enabled.')
+
+def disable_vlan(args):
+ bcm = get_bcm(args)
+ bcm.vlan_ctrl(False)
+ print('VLAN function is disabled.')
+
+def add_vlan(args):
+ bcm = get_bcm(args)
+ vid = args.vid
+ fwd = sorted(__parse_port_list(args.fwd))
+ untag = sorted(__parse_port_list(args.untag))
+ spt = args.spt
+ # make sure untag is subset of fwd
+ if not set(untag).issubset(set(fwd)):
+ raise Exception('Some untagged ports, %s, are not part of forward ports'
+ % (set(untag) - set(fwd)))
+ bcm.add_vlan(vid, untag, fwd, spt)
+ print('Added VLAN: %s' % vid)
+ print('Ports in VLAN: %s' % sorted(fwd))
+ print('Ports without VLAN tag: %s' % sorted(untag))
+
+def remove_vlan(args):
+ bcm = get_bcm(args)
+ vid = args.vid
+ bcm.remove_vlan(vid)
+ print('Removed VLAN: %s' % vid)
+
+def show_vlan(args):
+ bcm = get_bcm(args)
+ vid = args.vid
+ vlan = bcm.get_vlan(vid)
+ if not vlan['valid']:
+ print('VLAN %s does not exist' % vid)
+ else:
+ print('VLAN: %s' % vid)
+ print('Spanning tree index: %s' % vlan['spt'])
+ print('Ports in VLAN: %s' % sorted(vlan['fwd']))
+ print('Untagged ports in VLAN: %s' % sorted(vlan['untag']))
+
+def set_port_vlan(args):
+ bcm = get_bcm(args)
+ bcm.vlan_set_port_default(args.port, args.vid, args.pri)
+ print('Set VLAN default for port: %s' % args.port)
+ print('Default VLAN: %s' % args.vid)
+ print('Default priority %s' % args.pri)
+
+def get_port_vlan(args):
+ bcm = get_bcm(args)
+ port = bcm.vlan_get_port_default(args.port)
+ print('Get VLAN default for port: %s' % args.port)
+ print('Default VLAN: %s' % port['vid'])
+ print('Default priority: %s' % port['priority'])
+
+def vlan_parser(subparser):
+ UNTAG_DEFAULT = ''
+ SPT_DEFAULT = 0
+ PRI_DEFAULT = 0
+
+ vlan_parser = subparser.add_parser('vlan', help='Manage vlan function')
+ vlan_sub = vlan_parser.add_subparsers()
+
+ add_parser = vlan_sub.add_parser('add', help='Add or modify a VLAN entry')
+ add_parser.add_argument('vid', type=int, help='The VLAN ID')
+ add_parser.add_argument('fwd', type=str,
+ help='Ports belonging to this VLAN. i.e. 1,4,5-8')
+ add_parser.add_argument('untag', type=str, default=UNTAG_DEFAULT, nargs='?',
+ help='Ports that do not add VLAN tag. i.e. 1,4,5-8'
+ ' (default: all ports are tagged)')
+ add_parser.add_argument('spt', type=int, default=SPT_DEFAULT, nargs='?',
+ help='Spanning tree index (default: %s)'
+ % SPT_DEFAULT)
+ add_parser.set_defaults(func=add_vlan)
+
+ remove_parser = vlan_sub.add_parser('remove', help='Remove a VLAN entry')
+ remove_parser.add_argument('vid', type=int, help='The VLAN ID')
+ remove_parser.set_defaults(func=remove_vlan)
+
+ show_parser = vlan_sub.add_parser('show', help='Show a VLAN entry')
+ show_parser.add_argument('vid', type=int, help='The VLAN ID')
+ show_parser.set_defaults(func=show_vlan)
+
+ enable_parser = vlan_sub.add_parser('enable', help='Enable VLAN function')
+ enable_parser.set_defaults(func=enable_vlan)
+
+ disable_parser = vlan_sub.add_parser('disable', help='Enable VLAN function')
+ disable_parser.set_defaults(func=disable_vlan)
+
+ port_parser = vlan_sub.add_parser('port',
+ help='Set/Get VLAN default for a port')
+ port_sub = port_parser.add_subparsers()
+ set_port = port_sub.add_parser('set', help='Set VLAN default for a port')
+ set_port.add_argument('port', type=int, help='The port number (0..16)')
+ set_port.add_argument('vid', type=int,
+ help='The default VLAN for this port')
+ set_port.add_argument('pri', type=int, default=PRI_DEFAULT, nargs='?',
+ help='The default priority for this port '
+ '(default: %s)' % PRI_DEFAULT)
+ set_port.set_defaults(func=set_port_vlan)
+
+ get_port = port_sub.add_parser('get', help='Get VLAN default for a port')
+ get_port.add_argument('port', type=int, help='The port number (0..16)')
+ get_port.set_defaults(func=get_port_vlan)
+
+def access_parser(ap):
+ SPI_CS_DEFAULT = 68
+ SPI_CLK_DEFAULT = 69
+ SPI_MOSI_DEFAULT = 70
+ SPI_MISO_DEFAULT = 71
+
+ MDIO_MDC_DEFAULT = 6
+ MDIO_MDIO_DEFAULT = 7
+
+ spi_group = ap.add_argument_group('SPI Access')
+ spi_group.add_argument('--spi', action='store_true',
+ help='Access through SPI.')
+ spi_group.add_argument('--cs', type=int, default=SPI_CS_DEFAULT,
+ help='The GPIO number for SPI CS pin (default: %s)'
+ % SPI_CS_DEFAULT)
+ spi_group.add_argument('--clk', type=int, default=SPI_CLK_DEFAULT,
+ help='The GPIO number for SPI CLK pin (default: %s)'
+ % SPI_CLK_DEFAULT)
+ spi_group.add_argument('--mosi', type=int, default=SPI_MOSI_DEFAULT,
+ help='The GPIO number for SPI MOSI pin (default: %s)'
+ % SPI_MOSI_DEFAULT)
+ spi_group.add_argument('--miso', type=int, default=SPI_MISO_DEFAULT,
+ help='The GPIO number for SPI MISO pin (default: %s)'
+ % SPI_MISO_DEFAULT)
+ mdio_group = ap.add_argument_group('MDIO Access (default)')
+ mdio_group.add_argument('--mdc', type=int, default=MDIO_MDC_DEFAULT,
+ help='The GPIO number for MDC pin (default: %s)'
+ % MDIO_MDC_DEFAULT)
+ mdio_group.add_argument('--mdio', type=int, default=MDIO_MDIO_DEFAULT,
+ help='The GPIO number for MDIO pin (default: %s)'
+ % MDIO_MDIO_DEFAULT)
+ return ap
+
+
+if __name__ == '__main__':
+ ap = ArgumentParser()
+ access_parser(ap)
+
+ subparsers = ap.add_subparsers()
+ register_parser(subparsers)
+ arl_parser(subparsers)
+ vlan_parser(subparsers)
+ args = ap.parse_args()
+
+ args.func(args)
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/create_vlan_intf b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/create_vlan_intf
new file mode 100644
index 0000000..2cf7a9a
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/create_vlan_intf
@@ -0,0 +1,37 @@
+#!/bin/bash
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+# only care about 'eth0' and 'oob' intf
+[ "$IFACE" != "eth0" ] && [ "$IFACE" != "oob" ] && exit 0
+
+. /usr/local/fbpackages/utils/ast-functions
+
+board=$(wedge_board_type)
+
+[ "$board" = "WEDGE" ] && exit 0
+
+vlan=4088
+intf="${IFACE}.${vlan}"
+slot=$(wedge_slot_id $board)
+
+vconfig add $IFACE $vlan
+ifconfig $intf up "fe80::$(printf "%x" $slot):1/64"
+
+exit 0
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/eth0_mac_fixup.sh b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/eth0_mac_fixup.sh
new file mode 100644
index 0000000..1cdbcb6
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/eth0_mac_fixup.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+### BEGIN INIT INFO
+# Provides: eth0_mac_fixup.sh
+# Required-Start:
+# Required-Stop:
+# Default-Start: S
+# Default-Stop:
+# Short-Description: Fixup the MAC address for eth0 based on wedge EEPROM
+### END INIT INFO
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin
+
+mac=$(weutil 2>/dev/null | grep '^Local MAC' 2>/dev/null | cut -d' ' -f3 2>/dev/null)
+
+if [ -n "$mac" ]; then
+ ifconfig eth0 hw ether $mac
+ # compare the 'ethaddr' from u-boot env
+ ethaddr=$(fw_printenv ethaddr 2>/dev/null | cut -d'=' -f2 2>/dev/null)
+ if [ "$ethaddr" != "$mac" ]; then
+ fw_setenv "ethaddr" "$mac"
+ fi
+fi
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/fcswitcher.sh b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/fcswitcher.sh
new file mode 100755
index 0000000..53e24f3
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/fcswitcher.sh
@@ -0,0 +1,83 @@
+#! /bin/sh
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+### BEGIN INIT INFO
+# Provides: usbcons
+# Required-Start:
+# Required-Stop:
+# Default-Start: S
+# Default-Stop: 0 6
+# Short-Description: Creates a virtual USB serial device and starts a console
+# on it.
+#
+### END INIT INFO
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin
+NAME="FC Switcher"
+DESC="FC Failover Daemon"
+
+# source function library
+. /etc/init.d/functions
+
+. /usr/local/fbpackages/utils/ast-functions
+
+STOPPER=
+ACTION="$1"
+
+case "$ACTION" in
+ start)
+ if [ "$(wedge_board_type)" = "LC" ]; then
+ # Ability to prevent this from starting by editing cmdline in u-boot.
+ # Keeping this here until I get gadget switching working properly. (t4906522)
+ /usr/local/bin/watch-fc.sh > /dev/null 2>&1 &
+ echo "$NAME."
+ else
+ echo 'skipping watch-fc.sh: only necessary on six-pack line cards.'
+ fi
+ ;;
+ stop)
+ echo -n "Stopping $DESC: "
+ killall watch-fc.sh
+ echo "$NAME."
+ ;;
+ restart|force-reload)
+ echo -n "Restarting $DESC: "
+ killall watch-fc.sh
+ if [ "$(wedge_board_type)" = "LC" ]; then
+ sleep 1
+ /usr/local/bin/watch-fc.sh > /dev/null 2>&1 &
+ echo "$NAME."
+ else
+ echo 'skipping watch-fc.sh: only necessary on six-pack line cards.'
+ fi
+ ;;
+ status)
+ status watch-fc.sh
+ exit $?
+ ;;
+ *)
+ N=${0##*/}
+ N=${N#[SK]??}
+ echo "Usage: $N {start|stop|status|restart|force-reload}" >&2
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/mdio.py b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/mdio.py
new file mode 100755
index 0000000..aa7d4bf
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/mdio.py
@@ -0,0 +1,124 @@
+#!/usr/bin/python
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+from argparse import ArgumentParser
+import subprocess
+import time
+
+IO_BASE = [ 0x1e660000, 0x1e680000 ]
+PHYCR_REG_OFFSET = 0x60
+PHYCR_READ_BIT = 0x1 << 26
+PHYCR_WRITE_BIT = 0x1 << 27
+phycr_reg = lambda mac: IO_BASE[mac - 1] + PHYCR_REG_OFFSET
+PHYDATA_REG_OFFSET = 0x64
+phydata_reg = lambda mac: IO_BASE[mac - 1] + PHYDATA_REG_OFFSET
+
+
+devmem_read_cmd = lambda reg: [ 'devmem', hex(reg) ]
+devmem_write_cmd = lambda reg, val: [ 'devmem', hex(reg), '32', hex(val)]
+
+
+def devmem_read(reg):
+ cmd = devmem_read_cmd(reg)
+ #print('Cmd: {}'.format(cmd))
+ out = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0]
+ return int(out, 0)
+
+
+def devmem_write(reg, val):
+ cmd = devmem_write_cmd(reg, val)
+ #print('Cmd: {}'.format(cmd))
+ subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0]
+
+
+def wait_for_mdio_done(args):
+ reg = phycr_reg(args.mac)
+ while devmem_read(reg) & (PHYCR_READ_BIT|PHYCR_WRITE_BIT):
+ time.sleep(0.010) # 10ms
+
+
+def read_mdio(args):
+ reg = phycr_reg(args.mac)
+ ctrl = devmem_read(reg)
+ ctrl &= 0x3000003f
+ ctrl |= (args.phy & 0x1F) << 16
+ ctrl |= (args.register & 0x1F) << 21
+ ctrl |= PHYCR_READ_BIT
+ devmem_write(reg, ctrl)
+ wait_for_mdio_done(args)
+ val = devmem_read(phydata_reg(args.mac)) >> 16
+ print('Read from PHY ({}.{}): {}'
+ .format(hex(args.phy), hex(args.register), hex(val)))
+
+
+def write_mdio(args):
+ ctrl_reg = phycr_reg(args.mac)
+ ctrl = devmem_read(ctrl_reg)
+ ctrl &= 0x3000003f
+ ctrl |= (args.phy & 0x1F) << 16
+ ctrl |= (args.register & 0x1F) << 21
+ ctrl |= PHYCR_WRITE_BIT
+ data_reg = phydata_reg(args.mac)
+ # write data first
+ devmem_write(data_reg, args.value)
+ # then ctrl
+ devmem_write(ctrl_reg, ctrl)
+ wait_for_mdio_done(args)
+ print('Write to PHY ({}.{}): {}'
+ .format(hex(args.phy), hex(args.register), hex(args.value)))
+
+
+def auto_int(x):
+ return int(x, 0)
+
+if __name__ == '__main__':
+ ap = ArgumentParser()
+ ap.add_argument('--mac', '-m', type=int, default=2,
+ help='The MAC')
+ ap.add_argument('--phy', '-p', type=auto_int, default=0x1f,
+ help='The PHY address')
+
+ subparsers = ap.add_subparsers()
+
+ read_parser = subparsers.add_parser('read',
+ help='read MDIO')
+ read_parser.set_defaults(func=read_mdio)
+ read_parser.add_argument('register', type=auto_int,
+ help='The register to read from')
+
+ write_parser = subparsers.add_parser('write',
+ help='write MDIO')
+ write_parser.set_defaults(func=write_mdio)
+ write_parser.add_argument('register', type=auto_int,
+ help='The register to write to')
+ write_parser.add_argument('value', type=auto_int,
+ help='The value to write to')
+
+ args = ap.parse_args()
+
+ if args.mac != 2 and args.mac != 1:
+ print("MAC can only be either 1 or 2.")
+ exit(-1)
+
+ if args.phy > 0x1f:
+ printf("PHY address must be smaller than 0x1f.")
+ exit(-2)
+
+ args.func(args)
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/mount_data0.sh b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/mount_data0.sh
new file mode 100755
index 0000000..6986be5
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/mount_data0.sh
@@ -0,0 +1,61 @@
+#!/bin/sh
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+### BEGIN INIT INFO
+# Provides: mount_data0
+# Required-Start: mountvirtfs
+# Required-Stop:
+# Default-Start: S
+# Default-Stop:
+# Short-Description: Mount data0 partition from flash chip.
+# Description:
+### END INIT INFO
+
+. /etc/default/rcS
+
+# Find out which device maps to 'data0' on mtd
+# Note: /proc/mtd lists partitions using mtdX, where X is a number,
+# but we mount using /dev/mtdblockX. We'll do some magic here
+# to get the mtdX (char device) and mtdblockX (block device)
+# names.
+MOUNT_POINT="/mnt/data"
+DATA_CHAR_DEV=$(cat /proc/mtd | awk '{ if ($4 == "\"data0\"") print $1 }' |
+ cut -d ':' -f 1 | awk '{ print "/dev/" $1 }')
+if [ -z "$DATA_CHAR_DEV" ]
+then
+ echo "No data0 partition found. Not mounting anything to $MOUNT_POINT."
+else
+ DEVICE_ID=$(echo $DATA_CHAR_DEV | tail -c 2)
+ DATA_BLOCK_DEV=${DATA_CHAR_DEV/mtd/mtdblock}
+
+ echo "data0 partition found on $DATA_BLOCK_DEV; mounting to $MOUNT_POINT."
+ mount -t jffs2 $DATA_BLOCK_DEV $MOUNT_POINT
+
+ # if the mount failed, format the partition and remount
+ if [ $? -ne 0 ]
+ then
+ echo "Mount failed; formatting $DATA_BLOCK_DEV and remounting."
+ flash_eraseall $DATA_CHAR_DEV
+ mount -t jffs2 $DATA_BLOCK_DEV $MOUNT_POINT
+ fi
+fi
+
+: exit 0
+
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/post_led.sh b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/post_led.sh
new file mode 100644
index 0000000..c23349f
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/post_led.sh
@@ -0,0 +1,105 @@
+#!/bin/sh
+#
+# Copyright 2004-present Facebook. All rights reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+usage() {
+ echo "Displays values onto the debug header LEDs."
+ echo "Hex and decimal accepted."
+ echo "Usage: $0 <value>"
+}
+
+. /usr/local/fbpackages/utils/ast-functions
+
+# Function to set the less significant hex digit
+display_lower() {
+ local bit0=$(expr $1 % 2)
+ local bit1=$(expr $1 / 2 % 2)
+ local bit2=$(expr $1 / 4 % 2)
+ local bit3=$(expr $1 / 8 % 2)
+
+ # Set the pins to the correct operating mode.
+ # The relevant pins are GPIOG[0...3].
+ # For GPIO bank G, SCU84[0..3] must be 0.
+ devmem_clear_bit $(scu_addr 84) 0
+ devmem_clear_bit $(scu_addr 84) 1
+ devmem_clear_bit $(scu_addr 84) 2
+ devmem_clear_bit $(scu_addr 84) 3
+
+ # Now set the GPIOs to the right binary values
+ gpio_set 48 $bit0
+ gpio_set 49 $bit1
+ gpio_set 50 $bit2
+ gpio_set 51 $bit3
+}
+
+# Function to set the more significant hex digit
+display_upper() {
+ local bit0=$(expr $1 % 2)
+ local bit1=$(expr $1 / 2 % 2)
+ local bit2=$(expr $1 / 4 % 2)
+ local bit3=$(expr $1 / 8 % 2)
+
+ # Set the pins to the correct operating mode.
+ # The relevant pins are GPIOB[4...7].
+ # GPIOB4: SCU80[12] = 0 and Strap[14] = 0
+ # GPIOB5: SCU80[13] = 0
+ # GPIOB6: SCU80[14] = 0
+ # GPIOB7: SCU80[15] = 0
+ devmem_clear_bit $(scu_addr 70) 14
+ devmem_clear_bit $(scu_addr 80) 12
+ devmem_clear_bit $(scu_addr 80) 13
+ devmem_clear_bit $(scu_addr 80) 14
+ devmem_clear_bit $(scu_addr 80) 15
+
+ gpio_set 12 $bit0
+ gpio_set 13 $bit1
+ gpio_set 14 $bit2
+ gpio_set 15 $bit3
+}
+
+# Check number of parameters
+if [ $# -ne 1 ]
+then
+ usage
+ exit 1
+fi
+
+# Make sure input is actually numeric
+DEC_VALUE=$(printf "%d" $1 2>/dev/null)
+if [ $? -eq 1 ]
+then
+ echo "Unable to parse input as numeric value."
+ exit 1
+fi
+
+# Make sure input is within proper range
+if [ $DEC_VALUE -lt 0 ] || [ $DEC_VALUE -gt 255 ]
+then
+ echo "Value $DEC_VALUE is outside of displayable range 0 - 0xff (255)."
+ exit 1
+fi
+
+# Get upper/lower decimal values
+LOWER_DEC_VALUE=$(expr $DEC_VALUE % 16)
+UPPER_DEC_VALUE=$(expr $DEC_VALUE / 16)
+
+# Display the results
+display_lower $LOWER_DEC_VALUE
+display_upper $UPPER_DEC_VALUE
+
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/power-on.sh b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/power-on.sh
new file mode 100644
index 0000000..843b79a
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/power-on.sh
@@ -0,0 +1,72 @@
+#!/bin/sh
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+
+### BEGIN INIT INFO
+# Provides: power-on
+# Required-Start:
+# Required-Stop:
+# Default-Start: S
+# Default-Stop:
+# Short-Description: Power on micro-server
+### END INIT INFO
+. /usr/local/fbpackages/utils/ast-functions
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin
+
+echo -n "Checking microserver power status ... "
+if wedge_is_us_on 10 "."; then
+ echo "on"
+ on=1
+else
+ echo "off"
+ on=0
+fi
+
+if [ $on -eq 0 ]; then
+ # Reset USB hub
+ /usr/local/bin/reset_usb.sh
+ # Set up ROV
+ /usr/local/bin/setup_rov.sh
+ # Configure the management switch on LEFT side of FC.
+ # Must do so after setup_rov.sh, as setup_rov.sh might reset
+ # T2, which also resets the switch.
+ if [ "$(wedge_board_type)" = "FC-LEFT" ]; then
+ echo -n "Configure management switch ... "
+ if [ $(wedge_board_rev) -gt 1 ]; then
+ echo "skip. Need new switch setup script!!!"
+ else
+ # configure MDIO as GPIO output
+ gpio_set A6 1
+ gpio_set A7 1
+ # set the switch to be configured by CPU
+ gpio_set E2 1
+ # set the switch to be configured by CPU, then reset the switch,
+ # which also cause T2 reset in the current board
+ gpio_set E2 1
+ gpio_set C0 0
+ sleep 1
+ gpio_set C0 1
+ # configure the switch
+ setup_switch.py
+ echo "done"
+ fi
+ fi
+ # Power on now
+ wedge_power.sh on -f
+fi
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/power_led.sh b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/power_led.sh
new file mode 100755
index 0000000..20206a1
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/power_led.sh
@@ -0,0 +1,49 @@
+#!/bin/bash
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+usage() {
+ echo "Usage: $1 <on | off>"
+ exit -1
+}
+
+. /usr/local/fbpackages/utils/ast-functions
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin
+
+set -e
+
+if [ $# != 1 ]; then
+ usage $0
+fi
+
+if [ $1 = "on" ]; then
+ val=1
+elif [ $1 = "off" ]; then
+ val=0
+else
+ usage $0
+fi
+
+# To use GPIOE5 (37), SCU80[21], SCU8C[14], and SCU70[22] must be 0
+devmem_clear_bit $(scu_addr 80) 21
+devmem_clear_bit $(scu_addr 8C) 14
+devmem_clear_bit $(scu_addr 70) 22
+
+gpio_set 37 $val
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/rc.early b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/rc.early
new file mode 100644
index 0000000..0f47c72
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/rc.early
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+# This script will be executed at rcS S04 level, which is right after mount /mnt/data
+# and before almost anything else.
+
+if [ -x /mnt/data/etc/rc.early ]; then
+ /mnt/data/etc/rc.early
+fi
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/rc.local b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/rc.local
new file mode 100644
index 0000000..36fa0f1
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/rc.local
@@ -0,0 +1,26 @@
+#!/bin/sh
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+#
+# This script will be executed *after* all the other init scripts.
+
+if [ -x /mnt/data/etc/rc.local ]; then
+ /mnt/data/etc/rc.local
+fi
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/reset_usb.sh b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/reset_usb.sh
new file mode 100644
index 0000000..7d1c2c6
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/reset_usb.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+. /usr/local/fbpackages/utils/ast-functions
+
+echo -n "Reset USB Switch ... "
+
+# To use GPIOD7 (31), SCU90[1], SCU8C[11], and SCU70[21] must be 0
+devmem_clear_bit $(scu_addr 8C) 11
+devmem_clear_bit $(scu_addr 70) 21
+devmem_clear_bit $(scu_addr 90) 1
+
+gpio_set 31 1
+sleep 1
+gpio_set 31 0
+sleep 1
+gpio_set 31 1
+
+echo "Done"
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/setup-gpio.sh b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/setup-gpio.sh
new file mode 100755
index 0000000..9f0b543
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/setup-gpio.sh
@@ -0,0 +1,349 @@
+#!/bin/bash
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+### BEGIN INIT INFO
+# Provides: gpio-setup
+# Required-Start:
+# Required-Stop:
+# Default-Start: S
+# Default-Stop:
+# Short-Description: Set up GPIO pins as appropriate
+### END INIT INFO
+
+# This file contains definitions for the GPIO pins that were not otherwise
+# defined in other files. We should probably move some more of the
+# definitions to this file at some point.
+
+# The commented-out sections are generally already defined elsewhere,
+# and defining them twice generates errors.
+
+# The exception to this is the definition of the GPIO H0, H1, and H2
+# pins, which seem to adversely affect the rebooting of the system.
+# When defined, the system doesn't reboot cleanly. We're still
+# investigating this.
+
+. /usr/local/fbpackages/utils/ast-functions
+
+# Set up to read the board revision pins, Y0, Y1, Y2
+devmem_set_bit $(scu_addr 70) 19
+devmem_clear_bit $(scu_addr a4) 8
+devmem_clear_bit $(scu_addr a4) 9
+devmem_clear_bit $(scu_addr a4) 10
+echo 192 > /sys/class/gpio/export
+echo 193 > /sys/class/gpio/export
+echo 194 > /sys/class/gpio/export
+
+# enabled Y0, Y1, Y2, we can use wedge_board_rev() now
+board_rev=$(wedge_board_rev)
+
+# Set up ISO_SVR_ID[0-3], GPION[2-5]
+# On wedge, these 4 GPIOs are not connected. And the corresponding
+# 4 pins from uS are strapped to low.
+# On fabic, these 4 pins are connected to uS SVR_ID pins,
+# which are used to set the uS FPGA i2c address.
+# Force all pins to low to have the same uS FPGA i2c address on wedge
+# and fabric
+# To use GPION[2-5], SCU90[4:5] must be 0, and SCU88[2-5] must be 0 also
+devmem_clear_bit $(scu_addr 90) 4
+devmem_clear_bit $(scu_addr 90) 5
+devmem_clear_bit $(scu_addr 88) 2
+devmem_clear_bit $(scu_addr 88) 3
+devmem_clear_bit $(scu_addr 88) 4
+devmem_clear_bit $(scu_addr 88) 5
+gpio_set 106 0
+gpio_set 107 0
+gpio_set 108 0
+gpio_set 109 0
+
+## CARD_EN, GPIO C3
+#devmem_clear_bit $(scu_addr 90) 0
+#devmem_clear_bit $(scu_addr 90) 24
+#echo 18 > /sys/class/gpio/export
+
+# T2_RESET_N, RESET_SEQ0, RESET_SEQ1, on GPIO C0, F2, and F3
+devmem_clear_bit $(scu_addr 90) 0
+devmem_clear_bit $(scu_addr 90) 23
+devmem_clear_bit $(scu_addr 80) 26
+devmem_clear_bit $(scu_addr a4) 13
+devmem_clear_bit $(scu_addr 80) 27
+devmem_clear_bit $(scu_addr a4) 14
+devmem_set_bit $(scu_addr 70) 19
+echo 16 > /sys/class/gpio/export
+echo 42 > /sys/class/gpio/export
+echo 43 > /sys/class/gpio/export
+# output
+
+# PANTHER_PRSNT_N, uServer presence, on GPIO E4
+devmem_clear_bit $(scu_addr 80) 20
+devmem_clear_bit $(scu_addr 8c) 14
+devmem_clear_bit $(scu_addr 70) 22
+echo 36 > /sys/class/gpio/export
+
+# MRSRVR_SYS_RST, reset the uServer, on GPIO C1
+devmem_clear_bit $(scu_addr 90) 0
+devmem_clear_bit $(scu_addr 90) 23
+echo 17 > /sys/class/gpio/export
+# output
+
+# BMC_PWR_BTN_IN_N, uServer power button in, on GPIO D0
+# BMC_PWR_BTN_OUT_N, uServer power button out, on GPIO D1
+devmem_clear_bit $(scu_addr 90) 1
+devmem_clear_bit $(scu_addr 8c) 8
+devmem_clear_bit $(scu_addr 70) 21
+echo 24 > /sys/class/gpio/export
+# we have to ensure that BMC_PWR_BTN_OUT_N is high so that
+# when we enable the isolation buffer, uS will not be powered down
+gpio_set 25 1
+
+## BMC_READY_IN, BMC signal that it's up, on GPIO P7
+# To use GPIOP7 (127), SCU88[23] must be 0
+devmem_clear_bit $(scu_addr 88) 23
+# Put GPIOP7 (127) to low so that we can control uS power now
+# This must be after 'gpio_set 25 1'
+gpio_set 127 0
+
+# PANTHER_I2C_ALERT_N, alert for uServer I2C, GPIO B0
+devmem_clear_bit $(scu_addr 80) 8
+echo 8 > /sys/class/gpio/export
+
+# MNSERV_NIC_SMBUS_ALRT, alert for uServer NIC, GPIO B1
+devmem_clear_bit $(scu_addr 80) 9
+echo 9 > /sys/class/gpio/export
+
+# LED_PWR_BLUE, blue power light, GPIO E5
+devmem_clear_bit $(scu_addr 80) 21
+devmem_clear_bit $(scu_addr 8c) 14
+devmem_clear_bit $(scu_addr 70) 22
+echo 37 > /sys/class/gpio/export
+# output
+
+# BMC_HEARTBEAT_N, heartbeat LED, GPIO Q7
+devmem_clear_bit $(scu_addr 90) 28
+echo 135 > /sys/class/gpio/export
+# output
+
+# XXX: setting those causes the system to lock up on reboot
+## T2 ROV1, ROV2, ROV3 -- voltage reading, GPIOs H0, H1, and H2
+#devmem_clear_bit $(scu_addr 90) 6
+#devmem_clear_bit $(scu_addr 90) 7
+#devmem_clear_bit $(scu_addr 70) 4
+## Do I need to set 70:1 and 70:0 to 1?
+echo 56 > /sys/class/gpio/export
+echo 57 > /sys/class/gpio/export
+echo 58 > /sys/class/gpio/export
+
+# HOTSWAP_PG, hotswap issues, GPIO L3
+devmem_clear_bit $(scu_addr 90) 5
+devmem_clear_bit $(scu_addr 90) 4
+devmem_clear_bit $(scu_addr 84) 19
+echo 99 > /sys/class/gpio/export
+
+# XXX: These interfere with i2c bus 11 (on Linux, it's 12 on the hardware)
+# which we need to talk to the power supplies on certain hardware.
+## Hardware presence pins C4 and C5
+#devmem_clear_bit $(scu_addr 90) 0
+#devmem_clear_bit $(scu_addr 90) 24
+#echo 20 > /sys/class/gpio/export
+#echo 21 > /sys/class/gpio/export
+
+# FAB_GE_SEL, uServer GE connection, GPIO A0
+devmem_clear_bit $(scu_addr 80) 0
+echo 0 > /sys/class/gpio/export
+# output
+
+# USB_OCS_N1, resettable fuse tripped, GPIO Q6
+devmem_clear_bit $(scu_addr 90) 28
+echo 136 > /sys/class/gpio/export
+
+# RX loss signal?
+
+# System SPI
+# Strap 12 must be 0 and Strape 13 must be 1
+devmem_clear_bit $(scu_addr 70) 12
+devmem_set_bit $(scu_addr 70) 13
+# GPIOQ4 is ISO_FLASH_WP, must be 1 to avoid write protection
+# GPIOQ5 is ISO_FLASH_HOLD, must be 1 to be out of reset
+# To use GPIOQ4 and GPIOQ5, SCU90[27] must be 0
+devmem_clear_bit $(scu_addr 90) 27
+gpio_set 134 1
+gpio_set 135 1
+# GPIOD6 is ISO_FL_PRG_SEL, set it to 0 so that BMC does not have control
+# on the EEPROM by default.
+# To use GPIOD6, SCU90[1] must be 0, SCU8C[21] must be 0, and Strap[21] must be 0
+devmem_clear_bit $(scu_addr 90) 1
+devmem_clear_bit $(scu_addr 8c) 8
+devmem_clear_bit $(scu_addr 70) 21
+gpio_set 30 0
+
+# DEBUG_RST_BTN_N, Debug Reset button on front panel, GPIO R2
+devmem_clear_bit $(scu_addr 88) 26
+echo 138 > /sys/class/gpio/export
+
+# DEBUG_PORT_UART_SEL_N, Debug Select button, GPIO B2
+devmem_clear_bit $(scu_addr 80) 10
+echo 10 > /sys/class/gpio/export
+
+# DEBUG_UART_SEL_0, select uServer UART to the debug header, GPIO E0
+devmem_clear_bit $(scu_addr 80) 16
+devmem_clear_bit $(scu_addr 8c) 12
+devmem_clear_bit $(scu_addr 70) 22
+echo 32 > /sys/class/gpio/export
+# output
+
+# RS485 transceiver TX/RX toggle (RTS) pin, GPIOF5
+devmem_clear_bit $(scu_addr 80) 29
+echo 45 > /sys/class/gpio/export
+gpio_set 45 0
+
+# Bloodhound GPIOs, P0-6, G4, J1-3, Y3
+# Make sure GPIOP0,1,2,3,6 are enabled.
+for i in {16..19} 22; do
+ devmem_clear_bit $(scu_addr 88) $i
+done
+# Enable GPIOY3
+devmem_clear_bit $(scu_addr a4) 11
+# GPIOG4
+devmem_clear_bit $(scu_addr 2c) 1
+# GPIOJ1
+devmem_clear_bit $(scu_addr 84) 9
+# GPIOJ2
+devmem_clear_bit $(scu_addr 84) 10
+# GPIOJ11
+devmem_clear_bit $(scu_addr 84) 11
+
+# Export all the GPIOs
+for i in {120..126} 52 {73..75} 195; do
+ echo $i > /sys/class/gpio/export
+done
+
+# Enable the isolation buffer
+wedge_iso_buf_enable
+
+# Get the board type by parsing the EEPROM.
+# This must be after enabling isolation buffer as the i2c bus
+# is isolated by the buffer
+board_type=$(wedge_board_type)
+
+case "$board_type" in
+ FC-LEFT|FC-RIGHT)
+ # On FC
+ # FAB_SLOT_ID is GPIOU0
+ # PEER_FAB_PRSNT is GPIOU1
+ devmem_set_bit $(scu_addr a0) 8
+ devmem_set_bit $(scu_addr a0) 9
+ gpio_export U0
+ gpio_export U1
+ # T2_POWER_UP is GPIOT6
+ devmem_set_bit $(scu_addr a0) 6
+ gpio_export T6
+ # HS_FAULT_N is GPIOT7
+ devmem_set_bit $(scu_addr a0) 7
+ gpio_export T7
+ if [ "$board_type" = "FC-LEFT" ]; then
+ # GPIOE2 is CPU_EEPROM_SEL, on FC-LEFT
+ devmem_clear_bit $(scu_addr 80) 18
+ devmem_clear_bit $(scu_addr 8c) 13
+ devmem_clear_bit $(scu_addr 70) 22
+ gpio_export E2
+ # GPIOA6 and GPIOA7 are MAC2 MDIO pins, we use them as
+ # GPIO for bitbang driver
+ devmem_clear_bit $(scu_addr 90) 2
+ devmem_clear_bit $(scu_addr 80) 6
+ devmem_clear_bit $(scu_addr 80) 7
+ gpio_export A6
+ gpio_export A7
+ fi
+ ;;
+ *)
+ # Set up to watch for FC presence, and switch between interfaces.
+ # GPIOC0..C7, interested in C4, C5
+ devmem_clear_bit $(scu_addr 90) 0
+ devmem_clear_bit $(scu_addr 90) 25
+ if [ $board_rev -lt 3 ]; then
+ # Prior to DVTC
+ # BP_SLOT_ID GPIO pins are U0, U1, U2, U3
+ devmem_set_bit $(scu_addr a0) 8
+ devmem_set_bit $(scu_addr a0) 9
+ devmem_set_bit $(scu_addr a0) 10
+ devmem_set_bit $(scu_addr a0) 11
+ gpio_export U0
+ gpio_export U1
+ gpio_export U2
+ gpio_export U3
+ # T2_POWER_UP is GPIOT6
+ devmem_set_bit $(scu_addr a0) 6
+ gpio_export T6
+ # HS_FAULT_N is GPIOT7
+ devmem_set_bit $(scu_addr a0) 7
+ gpio_export T7
+ else
+ # Starting from DVTC
+ # BP_SLOT_ID GPIO pins are U6, U7, V0, V1
+ devmem_set_bit $(scu_addr 70) 6
+ devmem_set_bit $(scu_addr a0) 14
+ devmem_set_bit $(scu_addr a0) 15
+ devmem_set_bit $(scu_addr a0) 16
+ devmem_set_bit $(scu_addr a0) 17
+ gpio_export U6
+ gpio_export U7
+ gpio_export V0
+ gpio_export V1
+ # T2_POWER_UP is GPIOU4
+ devmem_set_bit $(scu_addr a0) 12
+ gpio_export U4
+ # HS_FAULT_N is GPIOU5
+ devmem_set_bit $(scu_addr a0) 13
+ gpio_export U5
+ fi
+ ;;
+esac
+
+# Make it possible to turn off T2 if fand sees overheating via GPIOF1
+# Do not change the GPIO direction here as the default value of this GPIO
+# is low, which causes a Non-maskable interrupt to the uS.
+devmem_clear_bit $(scu_addr 80) 25
+devmem_clear_bit $(scu_addr a4) 12
+echo 41 > /sys/class/gpio/export
+
+# Allow us to set the fan LEDs boards.
+# This is GPIO G5, G6, G7, and J0
+
+devmem_clear_bit $(scu_addr 70) 23
+devmem_clear_bit $(scu_addr 84) 5
+devmem_clear_bit $(scu_addr 84) 6
+devmem_clear_bit $(scu_addr 84) 7
+devmem_clear_bit $(scu_addr 84) 8
+
+echo 53 > /sys/class/gpio/export
+echo 54 > /sys/class/gpio/export
+echo 55 > /sys/class/gpio/export
+echo 72 > /sys/class/gpio/export
+echo "out" > /sys/class/gpio/gpio53/direction
+echo "out" > /sys/class/gpio/gpio54/direction
+echo "out" > /sys/class/gpio/gpio55/direction
+echo "out" > /sys/class/gpio/gpio72/direction
+
+# Once we set "out", output values will be random unless we set them
+# to something
+
+echo "0" > /sys/class/gpio/gpio53/value
+echo "0" > /sys/class/gpio/gpio54/value
+echo "0" > /sys/class/gpio/gpio55/value
+echo "0" > /sys/class/gpio/gpio72/value
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/setup_rov.sh b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/setup_rov.sh
new file mode 100755
index 0000000..749fe65
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/setup_rov.sh
@@ -0,0 +1,99 @@
+#!/bin/bash
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+# The T2 chip can prefer different input voltages, depending, presumably
+# of manufacturing variations. We need to determine whether it wants
+# 0.95V or 1.025V, reset the T2 to reduce total power usage, set the
+# outgoing voltage on the first buck converter, and bring T2 up out of
+# reset.
+
+. /usr/local/fbpackages/utils/ast-functions
+
+# read the T2 ROV after the GPIOs are enabled
+t2_rov() {
+ local val0 val1 val2
+ # Note that the values are *not* read in order.
+ val0=$(cat /sys/class/gpio/gpio58/value 2>/dev/null)
+ val1=$(cat /sys/class/gpio/gpio56/value 2>/dev/null)
+ val2=$(cat /sys/class/gpio/gpio57/value 2>/dev/null)
+ echo $((val0 | (val1 << 1) | (val2 << 2)))
+}
+
+rov=$(t2_rov)
+
+# target_volts come from the data sheet and 18mV of loss and
+# some fudging based on actual measurements to get either 1.025V
+# or 0.95V at T2
+if [ $rov -eq 1 ]; then
+ target_volts=0x5a
+elif [ $rov -eq 2 ]; then
+ target_volts=0x65
+else
+ echo "Unrecognized T2 ROV value $rov, setting failed."
+ exit 1
+fi
+target_volts=$(( $target_volts * 1 )) # normalize to decimal
+
+# We shouldn't have to rmmod pmbus, because it hasn't been loaded yet,
+# but if the script is rerun after the system is up, it may be necessary.
+rmmod pmbus
+reload=$?
+
+# Get current voltage value
+cur_volts=$(i2cget -y 1 0x60 0x8b w)
+cur_volts=$(( $cur_volts * 1 )) # normalize to decimal
+
+# Only bounce the T2 if we actually need to modify the voltage
+if [ $cur_volts -ne $target_volts ]; then
+ # Set values before turning out output; we're using "PCIE, then MCS"
+ echo 1 > /sys/class/gpio/gpio42/value
+ echo 1 > /sys/class/gpio/gpio43/value
+ echo out > /sys/class/gpio/gpio42/direction
+ echo out > /sys/class/gpio/gpio43/direction
+ echo 0 > /sys/class/gpio/gpio16/value
+ echo out > /sys/class/gpio/gpio16/direction
+ # T2 is in reset; note that this may cause NMI messages on the uServer,
+ # which shouldn't be up anyway when this is first run.
+
+ # Set the requested value to the current value to avoid rapid shifts
+ i2cset -y 1 0x60 0x21 $cur_volts w
+ # Enable the requested voltage
+ i2cset -y 1 0x60 0xd2 0x5a
+ i2cset -y 1 0x60 0xd3 0x5a
+ sleep 1
+
+ # Set the target voltage
+ i2cset -y 1 0x60 0x21 $target_volts w
+
+ sleep 1
+
+ # Let T2 come out of reset
+ echo 1 > /sys/class/gpio/gpio16/value
+ echo "T2 ROV value set based on $rov."
+ sleep 2
+ echo 0 > /sys/class/gpio/gpio42/value
+ echo 0 > /sys/class/gpio/gpio43/value
+else
+ echo "T2 ROV already correctly set."
+fi
+# Bring back pmbus if necessary
+if [ $reload -eq 0 ]; then
+ modprobe pmbus
+fi
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/setup_switch.py b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/setup_switch.py
new file mode 100644
index 0000000..995cec8
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/setup_switch.py
@@ -0,0 +1,49 @@
+#!/usr/bin/python
+#
+# Copyright 2004-present Facebook. All rights reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+# This script combines multiple switch configuration into one python script.
+# All such configuration can be done directly through bcm5396_util.py.
+# But, it turns out it took several seconds to just start the python script.
+# Involking the script 16 times (2 to create vlan, 13 to set port vlan default,
+# and 1 to enable vlan function) contributes 1 minute delay.
+
+from bcm5396 import Bcm5396
+
+MDC_GPIO = 6
+MDIO_GPIO = 7
+
+INTERNAL_VLAN = 4088
+DEFAULT_VLAN=4090
+
+INTERNAL_PORTS = [3, 10, 1, 11, 0, 8, 2, 9, 4, 12, 14, 13]
+FRONT_PORT=5
+
+if __name__ == '__main__':
+ bcm = Bcm5396(Bcm5396.MDIO_ACCESS, mdc=MDC_GPIO, mdio=MDIO_GPIO)
+ # create default VLAN including internal ports and front panel
+ # port (un-tagged)
+ bcm.add_vlan(DEFAULT_VLAN, INTERNAL_PORTS + [FRONT_PORT],
+ INTERNAL_PORTS + [FRONT_PORT], 0)
+ # set ingress vlan for internal ports and front panel port to default vlan
+ for port in INTERNAL_PORTS + [FRONT_PORT]:
+ bcm.vlan_set_port_default(port, DEFAULT_VLAN, 0)
+ # create internal vlan including internal ports only (tagged)
+ bcm.add_vlan(INTERNAL_VLAN, [], INTERNAL_PORTS, 0)
+ # enable vlan
+ bcm.vlan_ctrl(True)
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/sol.sh b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/sol.sh
new file mode 100755
index 0000000..ccbdc61
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/sol.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+CONSOLE_SH=/usr/local/fbpackages/utils/us_console.sh
+
+$CONSOLE_SH connect
+
+echo "You are in SOL session."
+echo "Use ctrl-x to quit."
+echo "-----------------------"
+echo
+
+trap '"$CONSOLE_SH" disconnect' INT TERM QUIT EXIT
+
+/usr/bin/microcom -s 57600 /dev/ttyS1
+
+echo
+echo
+echo "-----------------------"
+echo "Exit from SOL session."
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/src/include/i2c-dev.h b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/src/include/i2c-dev.h
new file mode 100644
index 0000000..eefb6e4
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/src/include/i2c-dev.h
@@ -0,0 +1,362 @@
+/*
+ i2c-dev.h - i2c-bus driver, char device interface
+
+ Copyright (C) 1995-97 Simon G. Vogl
+ Copyright (C) 1998-99 Frodo Looijaard <frodol@dds.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+#ifndef _LINUX_I2C_DEV_H
+#define _LINUX_I2C_DEV_H
+
+#include <linux/types.h>
+#include <sys/ioctl.h>
+#include <stddef.h>
+#include <string.h>
+
+
+/* -- i2c.h -- */
+
+#define _I2C_MIN(a, b) (((a) <= (b)) ? (a) : (b))
+
+/*
+ * I2C Message - used for pure i2c transaction, also from /dev interface
+ */
+struct i2c_msg {
+ __u16 addr; /* slave address */
+ unsigned short flags;
+#define I2C_CLIENT_PEC 0x04 /* Use Packet Error Checking */
+#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
+#define I2C_M_RD 0x0001 /* read data, from slave to master */
+#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */
+#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
+#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
+#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
+#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
+ short len; /* msg length */
+ char *buf; /* pointer to msg data */
+};
+
+/* To determine what functionality is present */
+
+#define I2C_FUNC_I2C 0x00000001
+#define I2C_FUNC_10BIT_ADDR 0x00000002
+#define I2C_FUNC_PROTOCOL_MANGLING 0x00000004 /* I2C_M_{REV_DIR_ADDR,NOSTART,..} */
+#define I2C_FUNC_SMBUS_PEC 0x00000008
+#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0x00008000 /* SMBus 2.0 */
+#define I2C_FUNC_SMBUS_QUICK 0x00010000
+#define I2C_FUNC_SMBUS_READ_BYTE 0x00020000
+#define I2C_FUNC_SMBUS_WRITE_BYTE 0x00040000
+#define I2C_FUNC_SMBUS_READ_BYTE_DATA 0x00080000
+#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA 0x00100000
+#define I2C_FUNC_SMBUS_READ_WORD_DATA 0x00200000
+#define I2C_FUNC_SMBUS_WRITE_WORD_DATA 0x00400000
+#define I2C_FUNC_SMBUS_PROC_CALL 0x00800000
+#define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0x01000000
+#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000
+#define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 /* I2C-like block xfer */
+#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* w/ 1-byte reg. addr. */
+
+#define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | \
+ I2C_FUNC_SMBUS_WRITE_BYTE)
+#define I2C_FUNC_SMBUS_BYTE_DATA (I2C_FUNC_SMBUS_READ_BYTE_DATA | \
+ I2C_FUNC_SMBUS_WRITE_BYTE_DATA)
+#define I2C_FUNC_SMBUS_WORD_DATA (I2C_FUNC_SMBUS_READ_WORD_DATA | \
+ I2C_FUNC_SMBUS_WRITE_WORD_DATA)
+#define I2C_FUNC_SMBUS_BLOCK_DATA (I2C_FUNC_SMBUS_READ_BLOCK_DATA | \
+ I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)
+#define I2C_FUNC_SMBUS_I2C_BLOCK (I2C_FUNC_SMBUS_READ_I2C_BLOCK | \
+ I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)
+
+/* Old name, for compatibility */
+#define I2C_FUNC_SMBUS_HWPEC_CALC I2C_FUNC_SMBUS_PEC
+
+/*
+ * Data for SMBus Messages
+ */
+#define I2C_SMBUS_BLOCK_MAX 32 /* As specified in SMBus standard */
+#define I2C_SMBUS_I2C_BLOCK_MAX 32 /* Not specified but we use same structure */
+union i2c_smbus_data {
+ __u8 byte;
+ __u16 word;
+ __u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */
+ /* and one more for PEC */
+};
+
+#define I2C_SMBUS_BLOCK_LARGE_MAX 240
+union i2c_smbus_large_data {
+ union i2c_smbus_data data;
+ __u8 block[I2C_SMBUS_BLOCK_LARGE_MAX + 2]; /* block[0] is used for length */
+ /* and one more for PEC */
+};
+
+/* smbus_access read or write markers */
+#define I2C_SMBUS_READ 1
+#define I2C_SMBUS_WRITE 0
+
+/* SMBus transaction types (size parameter in the above functions)
+ Note: these no longer correspond to the (arbitrary) PIIX4 internal codes! */
+#define I2C_SMBUS_QUICK 0
+#define I2C_SMBUS_BYTE 1
+#define I2C_SMBUS_BYTE_DATA 2
+#define I2C_SMBUS_WORD_DATA 3
+#define I2C_SMBUS_PROC_CALL 4
+#define I2C_SMBUS_BLOCK_DATA 5
+#define I2C_SMBUS_I2C_BLOCK_BROKEN 6
+#define I2C_SMBUS_BLOCK_PROC_CALL 7 /* SMBus 2.0 */
+#define I2C_SMBUS_I2C_BLOCK_DATA 8
+#define I2C_SMBUS_BLOCK_LARGE_DATA 9
+
+
+/* /dev/i2c-X ioctl commands. The ioctl's parameter is always an
+ * unsigned long, except for:
+ * - I2C_FUNCS, takes pointer to an unsigned long
+ * - I2C_RDWR, takes pointer to struct i2c_rdwr_ioctl_data
+ * - I2C_SMBUS, takes pointer to struct i2c_smbus_ioctl_data
+ */
+#define I2C_RETRIES 0x0701 /* number of times a device address should
+ be polled when not acknowledging */
+#define I2C_TIMEOUT 0x0702 /* set timeout in units of 10 ms */
+
+/* NOTE: Slave address is 7 or 10 bits, but 10-bit addresses
+ * are NOT supported! (due to code brokenness)
+ */
+#define I2C_SLAVE 0x0703 /* Use this slave address */
+#define I2C_SLAVE_FORCE 0x0706 /* Use this slave address, even if it
+ is already in use by a driver! */
+#define I2C_TENBIT 0x0704 /* 0 for 7 bit addrs, != 0 for 10 bit */
+
+#define I2C_FUNCS 0x0705 /* Get the adapter functionality mask */
+
+#define I2C_RDWR 0x0707 /* Combined R/W transfer (one STOP only) */
+
+#define I2C_PEC 0x0708 /* != 0 to use PEC with SMBus */
+#define I2C_SMBUS 0x0720 /* SMBus transfer */
+
+
+/* This is the structure as used in the I2C_SMBUS ioctl call */
+struct i2c_smbus_ioctl_data {
+ __u8 read_write;
+ __u8 command;
+ __u32 size;
+ union i2c_smbus_data *data;
+};
+
+/* This is the structure as used in the I2C_RDWR ioctl call */
+struct i2c_rdwr_ioctl_data {
+ struct i2c_msg *msgs; /* pointers to i2c_msgs */
+ __u32 nmsgs; /* number of i2c_msgs */
+};
+
+#define I2C_RDRW_IOCTL_MAX_MSGS 42
+
+
+static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command,
+ int size, union i2c_smbus_data *data)
+{
+ struct i2c_smbus_ioctl_data args;
+
+ args.read_write = read_write;
+ args.command = command;
+ args.size = size;
+ args.data = data;
+ return ioctl(file,I2C_SMBUS,&args);
+}
+
+
+static inline __s32 i2c_smbus_write_quick(int file, __u8 value)
+{
+ return i2c_smbus_access(file,value,0,I2C_SMBUS_QUICK,NULL);
+}
+
+static inline __s32 i2c_smbus_read_byte(int file)
+{
+ union i2c_smbus_data data;
+ if (i2c_smbus_access(file,I2C_SMBUS_READ,0,I2C_SMBUS_BYTE,&data))
+ return -1;
+ else
+ return 0x0FF & data.byte;
+}
+
+static inline __s32 i2c_smbus_write_byte(int file, __u8 value)
+{
+ return i2c_smbus_access(file,I2C_SMBUS_WRITE,value,
+ I2C_SMBUS_BYTE,NULL);
+}
+
+static inline __s32 i2c_smbus_read_byte_data(int file, __u8 command)
+{
+ union i2c_smbus_data data;
+ if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
+ I2C_SMBUS_BYTE_DATA,&data))
+ return -1;
+ else
+ return 0x0FF & data.byte;
+}
+
+static inline __s32 i2c_smbus_write_byte_data(int file, __u8 command,
+ __u8 value)
+{
+ union i2c_smbus_data data;
+ data.byte = value;
+ return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
+ I2C_SMBUS_BYTE_DATA, &data);
+}
+
+static inline __s32 i2c_smbus_read_word_data(int file, __u8 command)
+{
+ union i2c_smbus_data data;
+ if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
+ I2C_SMBUS_WORD_DATA,&data))
+ return -1;
+ else
+ return 0x0FFFF & data.word;
+}
+
+static inline __s32 i2c_smbus_write_word_data(int file, __u8 command,
+ __u16 value)
+{
+ union i2c_smbus_data data;
+ data.word = value;
+ return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
+ I2C_SMBUS_WORD_DATA, &data);
+}
+
+static inline __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value)
+{
+ union i2c_smbus_data data;
+ data.word = value;
+ if (i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
+ I2C_SMBUS_PROC_CALL,&data))
+ return -1;
+ else
+ return 0x0FFFF & data.word;
+}
+
+
+/* Returns the number of read bytes */
+static inline __s32 i2c_smbus_read_block_data(int file, __u8 command,
+ __u8 *values)
+{
+ union i2c_smbus_data data;
+ if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
+ I2C_SMBUS_BLOCK_DATA,&data))
+ return -1;
+ else {
+ memcpy(values, &data.block[1], _I2C_MIN(data.block[0], I2C_SMBUS_BLOCK_MAX));
+ return data.block[0];
+ }
+}
+
+static inline __s32 i2c_smbus_write_block_data(int file, __u8 command,
+ __u8 length, const __u8 *values)
+{
+ union i2c_smbus_data data;
+ if (length > 32)
+ length = 32;
+ memcpy(&data.block[1], values, length);
+ data.block[0] = length;
+ return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
+ I2C_SMBUS_BLOCK_DATA, &data);
+}
+
+static inline __s32 i2c_smbus_read_block_large_data(int file, __u8 command,
+ __u8 *values)
+{
+ union i2c_smbus_large_data data;
+ if (i2c_smbus_access(file, I2C_SMBUS_READ, command,
+ I2C_SMBUS_BLOCK_LARGE_DATA,
+ (union i2c_smbus_data *)&data)) {
+ return -1;
+ } else {
+ /* the first byte is the length which is not copied */
+ memcpy(values, &data.block[1], _I2C_MIN(data.block[0], I2C_SMBUS_BLOCK_LARGE_MAX));
+ return data.block[0];
+ }
+}
+
+static inline __s32 i2c_smbus_write_block_large_data(int file, __u8 command,
+ __u8 length,
+ const __u8 *values)
+{
+ union i2c_smbus_large_data data;
+ if (length > I2C_SMBUS_BLOCK_LARGE_MAX) {
+ length = I2C_SMBUS_BLOCK_LARGE_MAX;
+ }
+ data.block[0] = length;
+ memcpy(&data.block[1], values, length);
+ return i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
+ I2C_SMBUS_BLOCK_LARGE_DATA,
+ (union i2c_smbus_data *)&data);
+}
+
+/* Returns the number of read bytes */
+/* Until kernel 2.6.22, the length is hardcoded to 32 bytes. If you
+ ask for less than 32 bytes, your code will only work with kernels
+ 2.6.23 and later. */
+static inline __s32 i2c_smbus_read_i2c_block_data(int file, __u8 command,
+ __u8 length, __u8 *values)
+{
+ union i2c_smbus_data data;
+
+ if (length > 32)
+ length = 32;
+ data.block[0] = length;
+ if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
+ length == 32 ? I2C_SMBUS_I2C_BLOCK_BROKEN :
+ I2C_SMBUS_I2C_BLOCK_DATA,&data))
+ return -1;
+ else {
+ memcpy(values, &data.block[1], _I2C_MIN(data.block[0], I2C_SMBUS_BLOCK_MAX));
+ return data.block[0];
+ }
+}
+
+static inline __s32 i2c_smbus_write_i2c_block_data(int file, __u8 command,
+ __u8 length,
+ const __u8 *values)
+{
+ union i2c_smbus_data data;
+ if (length > 32)
+ length = 32;
+ memcpy(&data.block[1], values, length);
+ data.block[0] = length;
+ return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
+ I2C_SMBUS_I2C_BLOCK_BROKEN, &data);
+}
+
+/* Returns the number of read bytes */
+static inline __s32 i2c_smbus_block_process_call(int file, __u8 command,
+ __u8 length, __u8 *values)
+{
+ union i2c_smbus_data data;
+ if (length > 32)
+ length = 32;
+ memcpy(&data.block[1], values, length);
+ data.block[0] = length;
+ if (i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
+ I2C_SMBUS_BLOCK_PROC_CALL,&data))
+ return -1;
+ else {
+ memcpy(values, &data.block[1], _I2C_MIN(data.block[0], I2C_SMBUS_BLOCK_MAX));
+ return data.block[0];
+ }
+}
+
+#undef _I2C_MIN
+
+#endif /* _LINUX_I2C_DEV_H */
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/src/include/log.h b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/src/include/log.h
new file mode 100644
index 0000000..a69d69e
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/src/include/log.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef LOG_H
+#define LOG_H
+
+#include <stdio.h>
+#include <string.h>
+
+//#define DEBUG
+//#define VERBOSE
+
+#define _LOG(dst, fmt, ...) do { \
+ fprintf(dst, "%s:%d " fmt "\n", \
+ __FUNCTION__, __LINE__, ##__VA_ARGS__); \
+ fflush(dst); \
+} while(0)
+
+#define LOG_ERR(err, fmt, ...) do { \
+ char buf[128]; \
+ strerror_r(err, buf, sizeof(buf)); \
+ _LOG(stderr, "ERROR " fmt ": %s", ##__VA_ARGS__, buf); \
+} while(0)
+
+#define LOG_INFO(fmt, ...) do { \
+ _LOG(stdout, fmt, ##__VA_ARGS__); \
+} while(0)
+
+#ifdef DEBUG
+#define LOG_DBG(fmt, ...) do { \
+ _LOG(stdout, fmt, ##__VA_ARGS__); \
+} while(0)
+#else
+#define LOG_DBG(fmt, ...)
+#endif
+
+#ifdef VERBOSE
+#define LOG_VER(fmt, ...) do { \
+ _LOG(stdout, fmt, ##__VA_ARGS__); \
+} while(0)
+#else
+#define LOG_VER(fmt, ...)
+#endif
+
+#endif
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/us_console.sh b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/us_console.sh
new file mode 100755
index 0000000..6f445ee
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/us_console.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+usage() {
+ echo "$0 <connect | disconnect>"
+}
+
+. /usr/local/fbpackages/utils/ast-functions
+
+if [ $# -ne 1 ]; then
+ usage
+ exit 1
+fi
+
+if [ "$1" == "connect" ]; then
+ VALUE=1
+elif [ "$1" == "disconnect" ]; then
+ VALUE=0
+else
+ usage
+ exit 1
+fi
+
+# GPIOE0 (32) controls if uS console connects to UART1 or not.
+# To enable GPIOE0, SCU80[16], SCU8C[12], and SCU70[22] must be 0
+devmem_clear_bit $(scu_addr 80) 16
+devmem_clear_bit $(scu_addr 8C) 12
+devmem_clear_bit $(scu_addr 70) 22
+
+gpio_set 32 $VALUE
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/watch-fc.sh b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/watch-fc.sh
new file mode 100755
index 0000000..d04b7cd
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/watch-fc.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+. /usr/local/fbpackages/utils/ast-functions
+
+MAC1LINK=0 # GPIOA0
+FAB0_PRES=20 # GPIOC4
+FAB1_PRES=21 # GPIOC5
+while true; do
+ # fabN_pres: active low.
+ if [ $(gpio_get $FAB0_PRES) = 0 ]; then
+ gpio_set $MAC1LINK 0
+ elif [ $(gpio_get $FAB1_PRES) = 0 ]; then
+ gpio_set $MAC1LINK 1
+ fi
+ sleep 1
+done
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/wedge_power.sh b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/wedge_power.sh
new file mode 100644
index 0000000..5ba5311
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/wedge_power.sh
@@ -0,0 +1,201 @@
+#!/bin/bash
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+. /usr/local/fbpackages/utils/ast-functions
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin
+
+prog="$0"
+
+usage() {
+ echo "Usage: $prog <command> [command options]"
+ echo
+ echo "Commands:"
+ echo " status: Get the current microserver power status"
+ echo
+ echo " on: Power on microserver if not powered on already"
+ echo " options:"
+ echo " -f: Re-do power on sequence no matter if microserver has "
+ echo " been powered on or not."
+ echo
+ echo " off: Power off microserver ungracefully"
+ echo
+ echo " reset: Power reset microserver ungracefully"
+ echo " options:"
+ echo " -s: Power reset whole wedge system ungracefully"
+ echo
+}
+
+# Because the voltage leak from uS COM pins could cause uS to struck when transitting
+# from S5 to S0, we will need to explicitely pull down uS COM pins before powering off/reset
+# and restoring COM pins after
+
+pull_down_us_com() {
+ # set GPIOL6 and GPIOL7 low
+ devmem_clear_bit $(scu_addr 84) 22
+ devmem_clear_bit $(scu_addr 84) 23
+ gpio_set 94 0
+ gpio_set 95 0
+ # now, connect uart from BMC to the uS
+ gpio_set 32 1
+}
+
+restore_us_com() {
+ devmem_set_bit $(scu_addr 84) 22
+ devmem_set_bit $(scu_addr 84) 23
+ # if sol.sh is running, keep uart from uS connected with BMC
+ if ps | grep sol.sh > /dev/null 2>&1; then
+ gpio_set 32 1
+ else
+ gpio_set 32 0
+ fi
+}
+
+do_status() {
+ echo -n "Microserver power is "
+ if wedge_is_us_on; then
+ echo "on"
+ else
+ echo "off"
+ fi
+ return 0
+}
+
+do_on() {
+ local force opt
+ force=0
+ while getopts "f" opt; do
+ case $opt in
+ f)
+ force=1
+ ;;
+ *)
+ usage
+ exit -1
+ ;;
+
+ esac
+ done
+ echo -n "Power on microserver ..."
+ if [ $force -eq 0 ]; then
+ # need to check if uS is on or not
+ if wedge_is_us_on 10 "."; then
+ echo " Already on. Skip!"
+ return 1
+ fi
+ fi
+ # first make sure, GPIOD1 (25) is high
+ gpio_set 25 1
+ # then, put GPIOP7 (127) to low
+ gpio_set 127 0
+ # generate the power on pulse
+ gpio_set 25 0
+ sleep 1
+ gpio_set 25 1
+ sleep 1
+ restore_us_com
+ # Turn on the power LED (GPIOE5)
+ /usr/local/bin/power_led.sh on
+ echo " Done"
+ return 0
+}
+
+do_off() {
+ echo -n "Power off microserver ..."
+ pull_down_us_com
+ # first make sure, GPIOD1 (25) is high
+ gpio_set 25 1
+ # then, put GPIOP7 (127) to low
+ gpio_set 127 0
+ gpio_set 25 0
+ sleep 5
+ gpio_set 25 1
+ # Turn off the power LED (GPIOE5)
+ /usr/local/bin/power_led.sh off
+ echo " Done"
+ return 0
+}
+
+do_reset() {
+ local system opt
+ system=0
+ while getopts "s" opt; do
+ case $opt in
+ s)
+ system=1
+ ;;
+ *)
+ usage
+ exit -1
+ ;;
+ esac
+ done
+ if [ $system -eq 1 ]; then
+ echo -n "Power reset whole system ..."
+ rmmod adm1275
+ i2cset -y 12 0x10 0xd9 c
+ else
+ if ! wedge_is_us_on; then
+ echo "Power resetting microserver that is powered off has no effect."
+ echo "Use '$prog on' to power the microserver on"
+ return -1
+ fi
+ echo -n "Power reset microserver ..."
+ pull_down_us_com
+ # then, put GPIOP7 (127) to low
+ gpio_set 127 0
+ gpio_set 17 0
+ sleep 1
+ gpio_set 17 1
+ sleep 1
+ restore_us_com
+ fi
+ echo " Done"
+ return 0
+}
+
+if [ $# -lt 1 ]; then
+ usage
+ exit -1
+fi
+
+command="$1"
+shift
+
+case "$command" in
+ status)
+ do_status $@
+ ;;
+ on)
+ do_on $@
+ ;;
+ off)
+ do_off $@
+ ;;
+ reset)
+ do_reset $@
+ ;;
+ *)
+ usage
+ exit -1
+ ;;
+esac
+
+exit $?
diff --git a/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/wedge_us_mac.sh b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/wedge_us_mac.sh
new file mode 100644
index 0000000..34b8e59
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/fbutils/files/wedge_us_mac.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin
+
+mac=$(i2cdump -y 0x0 0x49 s 0xd4 | grep '^00: d4'| awk '{ print $3":"$4":"$5":"$6":"$7":"$8 }') 2>/dev/null
+
+if [ -n "$mac" ]; then
+ echo $mac
+else
+ echo "Cannot find out the microserver MAC" 1>&2
+fi
diff --git a/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/Makefile b/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/Makefile
new file mode 100644
index 0000000..8282964
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/Makefile
@@ -0,0 +1,13 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+all: ipmid
+
+ipmid: ipmid.c \
+ platform/timestamp.c platform/sel.c platform/sdr.c \
+ platform/wedge/sensor.c \
+ platform/wedge/fruid.c
+ $(CC) -pthread -lwedge_eeprom -std=c99 -o $@ $^ $(LDFLAGS)
+
+.PHONY: clean
+
+clean:
+ rm -rf *.o platform/*.o platform/wedge/*.o ipmid
diff --git a/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/ipmid.c b/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/ipmid.c
new file mode 100644
index 0000000..4771e15
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/ipmid.c
@@ -0,0 +1,1490 @@
+/*
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This file contains code to support IPMI2.0 Specificaton available @
+ * http://www.intel.com/content/www/us/en/servers/ipmi/ipmi-specifications.html
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "platform/sdr.h"
+#include "platform/sel.h"
+#include "platform/fruid.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <syslog.h>
+#include <string.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#define SOCK_PATH "/tmp/ipmi_socket"
+
+#define MAX_NUM_DIMMS 4
+#define IPMI_SEL_VERSION 0x51
+#define IPMI_SDR_VERSION 0x51
+
+#define SIZE_AUTH_ENABLES 5
+#define SIZE_IP_ADDR 4
+#define SIZE_MAC_ADDR 6
+#define SIZE_NET_MASK 4
+#define SIZE_IP_HDR 3
+#define SIZE_RMCP_PORT 2
+#define SIZE_COMMUNITY_STR 18
+#define SIZE_DEST_TYPE 4
+#define SIZE_DEST_ADDR 18
+#define SIZE_TIME_STAMP 4
+
+#define SIZE_PROC_FREQ 2
+#define SIZE_DIMM_SPEED 2
+#define SIZE_DIMM_SIZE 2
+
+#define SIZE_SYSFW_VER 17
+#define SIZE_SYS_NAME 17
+#define SIZE_OS_NAME 17
+#define SIZE_OS_VER 17
+#define SIZE_BMC_URL 17
+#define SIZE_OS_HV_URL 17
+
+#define SIZE_SEL_REC 16
+#define SIZE_IPMI_RES_HDR 3
+
+#define MAX_IPMI_MSG_SIZE 100
+
+// IPMI request Structure (IPMI/Section 9.2)
+typedef struct
+{
+ unsigned char netfn_lun;
+ unsigned char cmd;
+ unsigned char data[];
+} ipmi_req_t;
+
+// IPMI response Structure (IPMI/Section 9.3)
+typedef struct
+{
+ unsigned char netfn_lun;
+ unsigned char cmd;
+ unsigned char cc;
+ unsigned char data[];
+} ipmi_res_t;
+
+// LAN Configuration Structure (IPMI/Table 23.4)
+typedef struct
+{
+ unsigned char set_in_prog;
+ unsigned char auth_support;
+ unsigned char auth_enables[SIZE_AUTH_ENABLES];
+ unsigned char ip_addr[SIZE_IP_ADDR];
+ unsigned char ip_src;
+ unsigned char mac_addr[SIZE_MAC_ADDR];
+ unsigned char net_mask[SIZE_NET_MASK];
+ unsigned char ip_hdr[SIZE_IP_HDR];
+ unsigned char pri_rmcp_port[SIZE_RMCP_PORT];
+ unsigned char sec_rmcp_port[SIZE_RMCP_PORT];
+ unsigned char arp_ctrl;
+ unsigned char garp_interval;
+ unsigned char df_gw_ip_addr[SIZE_IP_ADDR];
+ unsigned char df_gw_mac_addr[SIZE_MAC_ADDR];
+ unsigned char back_gw_ip_addr[SIZE_IP_ADDR];
+ unsigned char back_gw_mac_addr[SIZE_MAC_ADDR];
+ unsigned char community_str[SIZE_COMMUNITY_STR];
+ unsigned char no_of_dest;
+ unsigned char dest_type[SIZE_DEST_TYPE];
+ unsigned char dest_addr[SIZE_DEST_ADDR];
+} lan_config_t;
+
+// Structure to store Processor Information
+typedef struct
+{
+ unsigned char type;
+ unsigned char freq[SIZE_PROC_FREQ];
+} proc_info_t;
+
+// Structure to store DIMM Information
+typedef struct
+{
+ unsigned char type;
+ unsigned char speed[SIZE_DIMM_SPEED];
+ unsigned char size[SIZE_DIMM_SIZE];
+} dimm_info_t;
+
+// Structure for System Info Params (IPMI/Section 22.14a)
+typedef struct
+{
+ unsigned char set_in_prog;
+ unsigned char sysfw_ver[SIZE_SYSFW_VER];
+ unsigned char sys_name[SIZE_SYS_NAME];
+ unsigned char pri_os_name[SIZE_OS_NAME];
+ unsigned char present_os_name[SIZE_OS_NAME];
+ unsigned char present_os_ver[SIZE_OS_VER];
+ unsigned char bmc_url[SIZE_BMC_URL];
+ unsigned char os_hv_url[SIZE_OS_HV_URL];
+} sys_info_param_t;
+
+// Network Function Codes (IPMI/Section 5.1)
+enum
+{
+ NETFN_CHASSIS_REQ = 0x00,
+ NETFN_CHASSIS_RES,
+ NETFN_BRIDGE_REQ,
+ NETFN_BRIDGE_RES,
+ NETFN_SENSOR_REQ,
+ NETFN_SENSOR_RES,
+ NETFN_APP_REQ,
+ NETFN_APP_RES,
+ NETFN_FIRMWARE_REQ,
+ NETFN_FIRMWARE_RES,
+ NETFN_STORAGE_REQ,
+ NETFN_STORAGE_RES,
+ NETFN_TRANSPORT_REQ,
+ NETFN_TRANSPORT_RES,
+ NETFN_OEM_REQ = 0x30,
+ NETFN_OEM_RES = 0x31,
+};
+
+// Chassis Command Codes (IPMI/Table H-1)
+enum
+{
+ CMD_CHASSIS_GET_STATUS = 0x01,
+ CMD_CHASSIS_GET_BOOT_OPTIONS = 0x09,
+};
+
+// Application Command Codes (IPMI/Table H-1)
+enum
+{
+ CMD_APP_GET_DEVICE_ID = 0x01,
+ CMD_APP_GET_SELFTEST_RESULTS = 0x04,
+ CMD_APP_GET_DEVICE_GUID = 0x08,
+ CMD_APP_RESET_WDT = 0x22,
+ CMD_APP_SET_WDT = 0x24,
+ CMD_APP_GET_WDT = 0x25,
+ CMD_APP_GET_GLOBAL_ENABLES = 0x2F,
+ CMD_APP_GET_SYSTEM_GUID = 0x37,
+ CMD_APP_SET_SYS_INFO_PARAMS = 0x58,
+ CMD_APP_GET_SYS_INFO_PARAMS = 0x59,
+};
+
+// Storage Command Codes (IPMI/Table H-1)
+enum
+{
+ CMD_STORAGE_GET_FRUID_INFO = 0x10,
+ CMD_STORAGE_READ_FRUID_DATA = 0x11,
+ CMD_STORAGE_GET_SDR_INFO = 0x20,
+ CMD_STORAGE_RSV_SDR = 0x22,
+ CMD_STORAGE_GET_SDR = 0x23,
+ CMD_STORAGE_GET_SEL_INFO = 0x40,
+ CMD_STORAGE_RSV_SEL = 0x42,
+ CMD_STORAGE_GET_SEL = 0x43,
+ CMD_STORAGE_ADD_SEL = 0x44,
+ CMD_STORAGE_CLR_SEL = 0x47,
+ CMD_STORAGE_GET_SEL_TIME = 0x48,
+ CMD_STORAGE_GET_SEL_UTC = 0x5C,
+};
+
+// Transport Command Codes (IPMI/Table H-1)
+enum
+{
+ CMD_TRANSPORT_SET_LAN_CONFIG = 0x01,
+ CMD_TRANSPORT_GET_LAN_CONFIG = 0x02,
+};
+
+// OEM Command Codes (Quanta/FB defined commands)
+enum
+{
+ CMD_OEM_SET_PROC_INFO = 0x1A,
+ CMD_OEM_SET_DIMM_INFO = 0x1C,
+ CMD_OEM_SET_POST_START = 0x73,
+ CMD_OEM_SET_POST_END = 0x74,
+};
+
+// IPMI command Completion Codes (IPMI/Section 5.2)
+enum
+{
+ CC_SUCCESS = 0x00,
+ CC_INVALID_PARAM = 0x80,
+ CC_SEL_ERASE_PROG = 0x81,
+ CC_INVALID_CMD = 0xC1,
+ CC_PARAM_OUT_OF_RANGE = 0xC9,
+ CC_UNSPECIFIED_ERROR = 0xFF,
+};
+
+// LAN Configuration parameters (IPMI/Table 23-4)
+enum
+{
+ LAN_PARAM_SET_IN_PROG,
+ LAN_PARAM_AUTH_SUPPORT,
+ LAN_PARAM_AUTH_ENABLES,
+ LAN_PARAM_IP_ADDR,
+ LAN_PARAM_IP_SRC,
+ LAN_PARAM_MAC_ADDR,
+ LAN_PARAM_NET_MASK,
+ LAN_PARAM_IP_HDR,
+ LAN_PARAM_PRI_RMCP_PORT,
+ LAN_PARAM_SEC_RMCP_PORT,
+ LAN_PARAM_ARP_CTRL,
+ LAN_PARAM_GARP_INTERVAL,
+ LAN_PARAM_DF_GW_IP_ADDR,
+ LAN_PARAM_DF_GW_MAC_ADDR,
+ LAN_PARAM_BACK_GW_IP_ADDR,
+ LAN_PARAM_BACK_GW_MAC_ADDR,
+ LAN_PARAM_COMMUNITY_STR,
+ LAN_PARAM_NO_OF_DEST,
+ LAN_PARAM_DEST_TYPE,
+ LAN_PARAM_DEST_ADDR,
+};
+
+// Boot Option Parameters (IPMI/Table 28-14)
+enum
+{
+ PARAM_SET_IN_PROG = 0x00,
+ PARAM_SVC_PART_SELECT,
+ PARAM_SVC_PART_SCAN,
+ PARAM_BOOT_FLAG_CLR,
+ PARAM_BOOT_INFO_ACK,
+ PARAM_BOOT_FLAGS,
+ PARAM_BOOT_INIT_INFO,
+};
+
+//System Info Parameters (IPMI/Table 22-16c)
+enum
+{
+ SYS_INFO_PARAM_SET_IN_PROG,
+ SYS_INFO_PARAM_SYSFW_VER,
+ SYS_INFO_PARAM_SYS_NAME,
+ SYS_INFO_PARAM_PRI_OS_NAME,
+ SYS_INFO_PARAM_PRESENT_OS_NAME,
+ SYS_INFO_PARAM_PRESENT_OS_VER,
+ SYS_INFO_PARAM_BMC_URL,
+ SYS_INFO_PARAM_OS_HV_URL,
+};
+
+// TODO: Once data storage is finalized, the following structure needs
+// to be retrieved/updated from persistant backend storage
+static lan_config_t g_lan_config = { 0 };
+static proc_info_t g_proc_info = { 0 };
+static dimm_info_t g_dimm_info[MAX_NUM_DIMMS] = { 0 };
+
+// TODO: Need to store this info after identifying proper storage
+static sys_info_param_t g_sys_info_params;
+
+// TODO: Based on performance testing results, might need fine grained locks
+// Since the global data is specific to a NetFunction, adding locs at NetFn level
+static pthread_mutex_t m_chassis;
+static pthread_mutex_t m_app;
+static pthread_mutex_t m_storage;
+static pthread_mutex_t m_transport;
+static pthread_mutex_t m_oem;
+
+/*
+ * Function(s) to handle IPMI messages with NetFn: Chassis
+ */
+// Get Chassis Status (IPMI/Section 28.2)
+static void
+chassis_get_status (unsigned char *response, unsigned char *res_len)
+{
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+
+ res->cc = CC_SUCCESS;
+
+ // TODO: Need to obtain current power state and last power event
+ // from platform and return
+ *data++ = 0x01; // Current Power State
+ *data++ = 0x00; // Last Power Event
+ *data++ = 0x40; // Misc. Chassis Status
+ *data++ = 0x00; // Front Panel Button Disable
+
+ res_len = data - &res->data[0];
+}
+
+// Get System Boot Options (IPMI/Section 28.12)
+static void
+chassis_get_boot_options (unsigned char *request, unsigned char *response,
+ unsigned char *res_len)
+{
+ ipmi_req_t *req = (ipmi_req_t *) request;
+ ipmi_res_t *res= (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+ unsigned char param = req->data[0];
+
+ // Fill response with default values
+ res->cc = CC_SUCCESS;
+ *data++ = 0x01; // Parameter Version
+ *data++ = req->data[0]; // Parameter
+
+ // TODO: Need to store user settings and return
+ switch (param)
+ {
+ case PARAM_SET_IN_PROG:
+ *data++ = 0x00; // Set In Progress
+ break;
+ case PARAM_SVC_PART_SELECT:
+ *data++ = 0x00; // Service Partition Selector
+ break;
+ case PARAM_SVC_PART_SCAN:
+ *data++ = 0x00; // Service Partition Scan
+ break;
+ case PARAM_BOOT_FLAG_CLR:
+ *data++ = 0x00; // BMC Boot Flag Valid Bit Clear
+ break;
+ case PARAM_BOOT_INFO_ACK:
+ *data++ = 0x00; // Write Mask
+ *data++ = 0x00; // Boot Initiator Ack Data
+ break;
+ case PARAM_BOOT_FLAGS:
+ *data++ = 0x00; // Boot Flags
+ *data++ = 0x00; // Boot Device Selector
+ *data++ = 0x00; // Firmwaer Verbosity
+ *data++ = 0x00; // BIOS Override
+ *data++ = 0x00; // Device Instance Selector
+ break;
+ case PARAM_BOOT_INIT_INFO:
+ *data++ = 0x00; // Chanel Number
+ *data++ = 0x00; // Session ID (4 bytes)
+ *data++ = 0x00;
+ *data++ = 0x00;
+ *data++ = 0x00;
+ *data++ = 0x00; // Boot Info Timestamp (4 bytes)
+ *data++ = 0x00;
+ *data++ = 0x00;
+ *data++ = 0x00;
+ break;
+ deault:
+ res->cc = CC_PARAM_OUT_OF_RANGE;
+ break;
+ }
+
+ if (res->cc == CC_SUCCESS) {
+ *res_len = data - &res->data[0];
+ }
+}
+
+// Handle Chassis Commands (IPMI/Section 28)
+static void
+ipmi_handle_chassis (unsigned char *request, unsigned char req_len,
+ unsigned char *response, unsigned char *res_len)
+{
+ ipmi_req_t *req = (ipmi_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char cmd = req->cmd;
+
+ pthread_mutex_lock(&m_chassis);
+ switch (cmd)
+ {
+ case CMD_CHASSIS_GET_STATUS:
+ chassis_get_status (response, res_len);
+ break;
+ case CMD_CHASSIS_GET_BOOT_OPTIONS:
+ chassis_get_boot_options (request, response, res_len);
+ break;
+ default:
+ res->cc = CC_INVALID_CMD;
+ break;
+ }
+ pthread_mutex_unlock(&m_chassis);
+}
+
+/*
+ * Function(s) to handle IPMI messages with NetFn: Application
+ */
+// Get Device ID (IPMI/Section 20.1)
+static void
+app_get_device_id (unsigned char *response, unsigned char *res_len)
+{
+
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+
+ res->cc = CC_SUCCESS;
+
+ //TODO: Following data needs to be updated based on platform
+ *data++ = 0x20; // Device ID
+ *data++ = 0x81; // Device Revision
+ *data++ = 0x00; // Firmware Revision Major
+ *data++ = 0x09; // Firmware Revision Minor
+ *data++ = 0x02; // IPMI Version
+ *data++ = 0xBF; // Additional Device Support
+ *data++ = 0x15; // Manufacturer ID1
+ *data++ = 0xA0; // Manufacturer ID2
+ *data++ = 0x00; // Manufacturer ID3
+ *data++ = 0x46; // Product ID1
+ *data++ = 0x31; // Product ID2
+ *data++ = 0x00; // Aux. Firmware Version1
+ *data++ = 0x00; // Aux. Firmware Version2
+ *data++ = 0x00; // Aux. Firmware Version3
+ *data++ = 0x00; // Aux. Firmware Version4
+
+ *res_len = data - &res->data[0];
+}
+
+// Get Self Test Results (IPMI/Section 20.4)
+static void
+app_get_selftest_results (unsigned char *response, unsigned char *res_len)
+{
+
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+
+ res->cc = CC_SUCCESS;
+
+ //TODO: Following data needs to be updated based on self-test results
+ *data++ = 0x55; // Self-Test result
+ *data++ = 0x00; // Extra error info in case of failure
+
+ *res_len = data - &res->data[0];
+}
+
+// Get Device GUID (IPMI/Section 20.8)
+static void
+app_get_device_guid (unsigned char *response, unsigned char *res_len)
+{
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+
+ res->cc = 0x00;
+
+ // TODO: Following data is Globaly Unique ID i.e. MAC Address..
+ *data++ = 0x0;
+ *data++ = 0x1;
+ *data++ = 0x2;
+ *data++ = 0x3;
+ *data++ = 0x4;
+ *data++ = 0x5;
+ *data++ = 0x6;
+ *data++ = 0x7;
+ *data++ = 0x8;
+ *data++ = 0x9;
+ *data++ = 0xa;
+ *data++ = 0xb;
+ *data++ = 0xc;
+ *data++ = 0xd;
+ *data++ = 0xe;
+ *data++ = 0xf;
+
+ *res_len = data - &res->data[0];
+}
+
+// Get BMC Global Enables (IPMI/Section 22.2)
+static void
+app_get_global_enables (unsigned char *response, unsigned char *res_len)
+{
+
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+
+ res->cc = CC_SUCCESS;
+
+ *data++ = 0x09; // Global Enable
+
+ *res_len = data - &res->data[0];
+}
+
+// Set System Info Params (IPMI/Section 22.14a)
+static void
+app_set_sys_info_params (unsigned char *request, unsigned char *response,
+ unsigned char *res_len)
+{
+
+ ipmi_req_t *req = (ipmi_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char param = req->data[0];
+
+ res->cc = CC_SUCCESS;
+
+ switch (param)
+ {
+ case SYS_INFO_PARAM_SET_IN_PROG:
+ g_sys_info_params.set_in_prog = req->data[1];
+ break;
+ case SYS_INFO_PARAM_SYSFW_VER:
+ memcpy(g_sys_info_params.sysfw_ver, &req->data[1], SIZE_SYSFW_VER);
+ break;
+ case SYS_INFO_PARAM_SYS_NAME:
+ memcpy(g_sys_info_params.sys_name, &req->data[1], SIZE_SYS_NAME);
+ break;
+ case SYS_INFO_PARAM_PRI_OS_NAME:
+ memcpy(g_sys_info_params.pri_os_name, &req->data[1], SIZE_OS_NAME);
+ break;
+ case SYS_INFO_PARAM_PRESENT_OS_NAME:
+ memcpy(g_sys_info_params.present_os_name, &req->data[1], SIZE_OS_NAME);
+ break;
+ case SYS_INFO_PARAM_PRESENT_OS_VER:
+ memcpy(g_sys_info_params.present_os_ver, &req->data[1], SIZE_OS_VER);
+ break;
+ case SYS_INFO_PARAM_BMC_URL:
+ memcpy(g_sys_info_params.bmc_url, &req->data[1], SIZE_BMC_URL);
+ break;
+ case SYS_INFO_PARAM_OS_HV_URL:
+ memcpy(g_sys_info_params.os_hv_url, &req->data[1], SIZE_OS_HV_URL);
+ break;
+ default:
+ res->cc = CC_INVALID_PARAM;
+ break;
+ }
+
+ return;
+}
+
+// Get System Info Params (IPMI/Section 22.14b)
+static void
+app_get_sys_info_params (unsigned char *request, unsigned char *response,
+ unsigned char *res_len)
+{
+
+ ipmi_req_t *req = (ipmi_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+ unsigned char param = req->data[1];
+
+ // Fill default return values
+ res->cc = CC_SUCCESS;
+ *data++ = 1; // Parameter revision
+
+ switch (param)
+ {
+ case SYS_INFO_PARAM_SET_IN_PROG:
+ *data++ = g_sys_info_params.set_in_prog;
+ break;
+ case SYS_INFO_PARAM_SYSFW_VER:
+ memcpy(data, g_sys_info_params.sysfw_ver, SIZE_SYSFW_VER);
+ data += SIZE_SYSFW_VER;
+ break;
+ case SYS_INFO_PARAM_SYS_NAME:
+ memcpy(data, g_sys_info_params.sys_name, SIZE_SYS_NAME);
+ data += SIZE_SYS_NAME;
+ break;
+ case SYS_INFO_PARAM_PRI_OS_NAME:
+ memcpy(data, g_sys_info_params.pri_os_name, SIZE_OS_NAME);
+ data += SIZE_OS_NAME;
+ break;
+ case SYS_INFO_PARAM_PRESENT_OS_NAME:
+ memcpy(data, g_sys_info_params.present_os_name, SIZE_OS_NAME);
+ data += SIZE_OS_NAME;
+ break;
+ case SYS_INFO_PARAM_PRESENT_OS_VER:
+ memcpy(data, g_sys_info_params.present_os_ver, SIZE_OS_VER);
+ data += SIZE_OS_VER;
+ break;
+ case SYS_INFO_PARAM_BMC_URL:
+ memcpy(data, g_sys_info_params.bmc_url, SIZE_BMC_URL);
+ data += SIZE_BMC_URL;
+ break;
+ case SYS_INFO_PARAM_OS_HV_URL:
+ memcpy(data, g_sys_info_params.os_hv_url, SIZE_OS_HV_URL);
+ data += SIZE_OS_HV_URL;
+ break;
+ default:
+ res->cc = CC_INVALID_PARAM;
+ break;
+ }
+
+ if (res->cc == CC_SUCCESS) {
+ *res_len = data - &res->data[0];
+ }
+
+ return;
+}
+
+// Handle Appliction Commands (IPMI/Section 20)
+static void
+ipmi_handle_app (unsigned char *request, unsigned char req_len,
+ unsigned char *response, unsigned char *res_len)
+{
+ ipmi_req_t *req = (ipmi_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char cmd = req->cmd;
+
+ pthread_mutex_lock(&m_app);
+ switch (cmd)
+ {
+ case CMD_APP_GET_DEVICE_ID:
+ app_get_device_id (response, res_len);
+ break;
+ case CMD_APP_GET_SELFTEST_RESULTS:
+ app_get_selftest_results (response, res_len);
+ break;
+ case CMD_APP_GET_DEVICE_GUID:
+ case CMD_APP_GET_SYSTEM_GUID:
+ // Get Device GUID and Get System GUID returns same data
+ // from IPMI stack. FYI, Get System GUID will have to be
+ // sent with in an IPMI session that includes session info
+ app_get_device_guid (response, res_len);
+ break;
+ case CMD_APP_GET_GLOBAL_ENABLES:
+ app_get_global_enables (response, res_len);
+ break;
+ case CMD_APP_SET_SYS_INFO_PARAMS:
+ app_set_sys_info_params (request, response, res_len);
+ break;
+ case CMD_APP_GET_SYS_INFO_PARAMS:
+ app_get_sys_info_params (request, response, res_len);
+ break;
+ default:
+ res->cc = CC_INVALID_CMD;
+ break;
+ }
+ pthread_mutex_unlock(&m_app);
+}
+
+/*
+ * Function(s) to handle IPMI messages with NetFn: Storage
+ */
+
+static void
+storage_get_fruid_info(unsigned char *response, unsigned char *res_len)
+{
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+ int size = plat_fruid_size();
+
+ res->cc = CC_SUCCESS;
+
+ *data++ = size & 0xFF; // FRUID size LSB
+ *data++ = (size >> 8) & 0xFF; // FRUID size MSB
+ *data++ = 0x00; // Device accessed by bytes
+
+ *res_len = data - &res->data[0];
+
+ return;
+}
+
+static void
+storage_get_fruid_data(unsigned char *request, unsigned char *response,
+ unsigned char *res_len)
+{
+ ipmi_req_t *req = (ipmi_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+
+ int offset = req->data[1] + (req->data[2] << 8);
+ int count = req->data[3];
+
+ int ret = plat_fruid_data(offset, count, &(res->data[1]));
+ if (ret) {
+ res->cc = CC_UNSPECIFIED_ERROR;
+ } else {
+ res->cc = CC_SUCCESS;
+ *data++ = count;
+ data += count;
+ }
+
+ if (res->cc == CC_SUCCESS) {
+ *res_len = data - &res->data[0];
+ }
+ return;
+}
+
+static void
+storage_get_sdr_info (unsigned char *response, unsigned char *res_len)
+{
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+ int num_entries; // number of sdr records
+ int free_space; // free space in SDR device in bytes
+ time_stamp_t ts_recent_add; // Recent Addition Timestamp
+ time_stamp_t ts_recent_erase; // Recent Erasure Timestamp
+
+ // Use platform APIs to get SDR information
+ num_entries = plat_sdr_num_entries ();
+ free_space = plat_sdr_free_space ();
+ plat_sdr_ts_recent_add (&ts_recent_add);
+ plat_sdr_ts_recent_erase (&ts_recent_erase);
+
+ res->cc = CC_SUCCESS;
+
+ *data++ = IPMI_SDR_VERSION; // SDR version
+ *data++ = num_entries & 0xFF; // number of sdr entries
+ *data++ = (num_entries >> 8) & 0xFF;
+ *data++ = free_space & 0xFF; // Free SDR Space
+ *data++ = (free_space >> 8) & 0xFF;
+
+ memcpy(data, ts_recent_add.ts, SIZE_TIME_STAMP);
+ data += SIZE_TIME_STAMP;
+
+ memcpy(data, ts_recent_erase.ts, SIZE_TIME_STAMP);
+ data += SIZE_TIME_STAMP;
+
+ *data++ = 0x02; // Operations supported
+
+ *res_len = data - &res->data[0];
+
+ return;
+}
+
+static void
+storage_rsv_sdr (unsigned char *response, unsigned char *res_len)
+{
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+ int rsv_id; // SDR reservation ID
+
+ // Use platform APIs to get a SDR reservation ID
+ rsv_id = plat_sdr_rsv_id ();
+ if (rsv_id < 0)
+ {
+ res->cc = CC_UNSPECIFIED_ERROR;
+ return;
+ }
+
+ res->cc = CC_SUCCESS;
+ *data++ = rsv_id & 0xFF; // Reservation ID
+ *data++ = (rsv_id >> 8) & 0XFF;
+
+ *res_len = data - &res->data[0];
+
+ return;
+}
+
+static void
+storage_get_sdr (unsigned char *request, unsigned char *response,
+ unsigned char *res_len)
+{
+ ipmi_req_t *req = (ipmi_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+
+ int read_rec_id; //record ID to be read
+ int next_rec_id; //record ID for the next entry
+ int rsv_id; // Reservation ID for the request
+ int rec_offset; // Read offset into the record
+ int rec_bytes; // Number of bytes to be read
+ sdr_rec_t entry; // SDR record entry
+ int ret;
+
+ rsv_id = (req->data[1] >> 8) | req->data[0];
+ read_rec_id = (req->data[3] >> 8) | req->data[2];
+ rec_offset = req->data[4];
+ rec_bytes = req->data[5];
+
+ // Use platform API to read the record Id and get next ID
+ ret = plat_sdr_get_entry (rsv_id, read_rec_id, &entry, &next_rec_id);
+ if (ret)
+ {
+ res->cc = CC_UNSPECIFIED_ERROR;
+ return;
+ }
+
+ res->cc = CC_SUCCESS;
+ *data++ = next_rec_id & 0xFF; // next record ID
+ *data++ = (next_rec_id >> 8) & 0xFF;
+
+ memcpy (data, &entry.rec[rec_offset], rec_bytes);
+ data += rec_bytes;
+
+ *res_len = data - &res->data[0];
+
+ return;
+}
+
+static void
+storage_get_sel_info (unsigned char *response, unsigned char *res_len)
+{
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+ int num_entries; // number of log entries
+ int free_space; // free space in SEL device in bytes
+ time_stamp_t ts_recent_add; // Recent Addition Timestamp
+ time_stamp_t ts_recent_erase; // Recent Erasure Timestamp
+
+ // Use platform APIs to get SEL information
+ num_entries = plat_sel_num_entries ();
+ free_space = plat_sel_free_space ();
+ plat_sel_ts_recent_add (&ts_recent_add);
+ plat_sel_ts_recent_erase (&ts_recent_erase);
+
+ res->cc = CC_SUCCESS;
+
+ *data++ = IPMI_SEL_VERSION; // SEL version
+ *data++ = num_entries & 0xFF; // number of log entries
+ *data++ = (num_entries >> 8) & 0xFF;
+ *data++ = free_space & 0xFF; // Free SEL Space
+ *data++ = (free_space >> 8) & 0xFF;
+
+ memcpy(data, ts_recent_add.ts, SIZE_TIME_STAMP);
+ data += SIZE_TIME_STAMP;
+
+ memcpy(data, ts_recent_erase.ts, SIZE_TIME_STAMP);
+ data += SIZE_TIME_STAMP;
+
+ *data++ = 0x02; // Operations supported
+
+ *res_len = data - &res->data[0];
+
+ return;
+}
+
+static void
+storage_rsv_sel (unsigned char *response, unsigned char *res_len)
+{
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+ int rsv_id; // SEL reservation ID
+
+ // Use platform APIs to get a SEL reservation ID
+ rsv_id = plat_sel_rsv_id ();
+ if (rsv_id < 0)
+ {
+ res->cc = CC_SEL_ERASE_PROG;
+ return;
+ }
+
+ res->cc = CC_SUCCESS;
+ *data++ = rsv_id & 0xFF; // Reservation ID
+ *data++ = (rsv_id >> 8) & 0XFF;
+
+ *res_len = data - &res->data[0];
+
+ return;
+}
+
+static void
+storage_get_sel (unsigned char *request, unsigned char *response,
+ unsigned char *res_len)
+{
+ ipmi_req_t *req = (ipmi_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+
+ int read_rec_id; //record ID to be read
+ int next_rec_id; //record ID for the next msg
+ sel_msg_t entry; // SEL log entry
+ int ret;
+
+ read_rec_id = (req->data[3] >> 8) | req->data[2];
+
+ // Use platform API to read the record Id and get next ID
+ ret = plat_sel_get_entry (read_rec_id, &entry, &next_rec_id);
+ if (ret)
+ {
+ res->cc = CC_UNSPECIFIED_ERROR;
+ return;
+ }
+
+ res->cc = CC_SUCCESS;
+ *data++ = next_rec_id & 0xFF; // next record ID
+ *data++ = (next_rec_id >> 8) & 0xFF;
+
+ memcpy(data, entry.msg, SIZE_SEL_REC);
+ data += SIZE_SEL_REC;
+
+ *res_len = data - &res->data[0];
+
+ return;
+}
+
+static void
+storage_add_sel (unsigned char *request, unsigned char *response,
+ unsigned char *res_len)
+{
+ ipmi_req_t *req = (ipmi_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+
+
+ int record_id; // Record ID for added entry
+ int ret;
+
+ sel_msg_t entry;
+
+ memcpy(entry.msg, req->data, SIZE_SEL_REC);
+
+ // Use platform APIs to add the new SEL entry
+ ret = plat_sel_add_entry (&entry, &record_id);
+ if (ret)
+ {
+ res->cc = CC_UNSPECIFIED_ERROR;
+ return;
+ }
+
+ res->cc = CC_SUCCESS;
+ *data++ = record_id & 0xFF;
+ *data++ = (record_id >> 8) & 0xFF;
+
+ *res_len = data - &res->data[0];
+
+ return;
+}
+
+static void
+storage_clr_sel (unsigned char *request, unsigned char *response,
+ unsigned char *res_len)
+{
+ ipmi_req_t *req = (ipmi_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+
+ sel_erase_stat_t status;
+ int ret;
+ int rsv_id;
+
+ // Verify the request to contain 'CLR' characters
+ if ((req->data[2] != 'C') || (req->data[3] != 'L') || (req->data[4] != 'R'))
+ {
+ res->cc = CC_INVALID_PARAM;
+ return;
+ }
+
+ // Populate reservation ID given in request
+ rsv_id = (req->data[1] << 8) | req->data[0];
+
+ // Use platform APIs to clear or get status
+ if (req->data[5] == IPMI_SEL_INIT_ERASE)
+ {
+ ret = plat_sel_erase (rsv_id);
+ }
+ else if (req->data[5] == IPMI_SEL_ERASE_STAT)
+ {
+ ret = plat_sel_erase_status (rsv_id, &status);
+ }
+ else
+ {
+ res->cc = CC_INVALID_PARAM;
+ return;
+ }
+
+ // Handle platform error and return
+ if (ret)
+ {
+ res->cc = CC_UNSPECIFIED_ERROR;
+ return;
+ }
+
+ res->cc = CC_SUCCESS;
+ *data++ = status;
+
+ *res_len = data - &res->data[0];
+
+ return;
+}
+
+static void
+ipmi_handle_storage (unsigned char *request, unsigned char req_len,
+ unsigned char *response, unsigned char *res_len)
+{
+ ipmi_req_t *req = (ipmi_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char cmd = req->cmd;
+
+ res->cc = CC_SUCCESS;
+ *res_len = 0;
+
+ pthread_mutex_lock(&m_storage);
+ switch (cmd)
+ {
+ case CMD_STORAGE_GET_FRUID_INFO:
+ storage_get_fruid_info (response, res_len);
+ break;
+ case CMD_STORAGE_READ_FRUID_DATA:
+ storage_get_fruid_data (request, response, res_len);
+ break;
+ case CMD_STORAGE_GET_SEL_INFO:
+ storage_get_sel_info (response, res_len);
+ break;
+ case CMD_STORAGE_RSV_SEL:
+ storage_rsv_sel (response, res_len);
+ break;
+ case CMD_STORAGE_ADD_SEL:
+ storage_add_sel (request, response, res_len);
+ break;
+ case CMD_STORAGE_GET_SEL:
+ storage_get_sel (request, response, res_len);
+ break;
+ case CMD_STORAGE_CLR_SEL:
+ storage_clr_sel (request, response, res_len);
+ break;
+ case CMD_STORAGE_GET_SDR_INFO:
+ storage_get_sdr_info (response, res_len);
+ break;
+ case CMD_STORAGE_RSV_SDR:
+ storage_rsv_sdr (response, res_len);
+ break;
+ case CMD_STORAGE_GET_SDR:
+ storage_get_sdr (request, response, res_len);
+ break;
+ default:
+ res->cc = CC_INVALID_CMD;
+ break;
+ }
+
+ pthread_mutex_unlock(&m_storage);
+ return;
+}
+
+/*
+ * Function(s) to handle IPMI messages with NetFn: Transport
+ */
+
+// Set LAN Configuration (IPMI/Section 23.1)
+static void
+transport_set_lan_config (unsigned char *request, unsigned char *response,
+ unsigned char *res_len)
+{
+ ipmi_req_t *req = (ipmi_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char param = req->data[1];
+
+ // Fill the response with default values
+ res->cc = CC_SUCCESS;
+
+ switch (param)
+ {
+ case LAN_PARAM_SET_IN_PROG:
+ g_lan_config.set_in_prog = req->data[2];
+ break;
+ case LAN_PARAM_AUTH_SUPPORT:
+ g_lan_config.auth_support = req->data[2];
+ break;
+ case LAN_PARAM_AUTH_ENABLES:
+ memcpy(g_lan_config.auth_enables, &req->data[2], SIZE_AUTH_ENABLES);
+ break;
+ case LAN_PARAM_IP_ADDR:
+ memcpy(g_lan_config.ip_addr, &req->data[2], SIZE_IP_ADDR);
+ break;
+ case LAN_PARAM_IP_SRC:
+ g_lan_config.ip_src = req->data[2];
+ break;
+ case LAN_PARAM_MAC_ADDR:
+ memcpy(g_lan_config.mac_addr, &req->data[2], SIZE_MAC_ADDR);
+ break;
+ case LAN_PARAM_NET_MASK:
+ memcpy(g_lan_config.net_mask, &req->data[2], SIZE_NET_MASK);
+ break;
+ case LAN_PARAM_IP_HDR:
+ memcpy(g_lan_config.ip_hdr, &req->data[2], SIZE_IP_HDR);
+ break;
+ case LAN_PARAM_PRI_RMCP_PORT:
+ g_lan_config.pri_rmcp_port[0] = req->data[2];
+ g_lan_config.pri_rmcp_port[1] = req->data[3];
+ break;
+ case LAN_PARAM_SEC_RMCP_PORT:
+ g_lan_config.sec_rmcp_port[0] = req->data[2];
+ g_lan_config.sec_rmcp_port[1] = req->data[3];
+ break;
+ case LAN_PARAM_ARP_CTRL:
+ g_lan_config.arp_ctrl = req->data[2];
+ break;
+ case LAN_PARAM_GARP_INTERVAL:
+ g_lan_config.garp_interval = req->data[2];
+ break;
+ case LAN_PARAM_DF_GW_IP_ADDR:
+ memcpy(g_lan_config.df_gw_ip_addr, &req->data[2], SIZE_IP_ADDR);
+ break;
+ case LAN_PARAM_DF_GW_MAC_ADDR:
+ memcpy(g_lan_config.df_gw_mac_addr, &req->data[2], SIZE_MAC_ADDR);
+ break;
+ case LAN_PARAM_BACK_GW_IP_ADDR:
+ memcpy(g_lan_config.back_gw_ip_addr, &req->data[2], SIZE_IP_ADDR);
+ break;
+ case LAN_PARAM_BACK_GW_MAC_ADDR:
+ memcpy(g_lan_config.back_gw_mac_addr, &req->data[2], SIZE_MAC_ADDR);
+ break;
+ case LAN_PARAM_COMMUNITY_STR:
+ memcpy(g_lan_config.community_str, &req->data[2], SIZE_COMMUNITY_STR);
+ break;
+ case LAN_PARAM_NO_OF_DEST:
+ g_lan_config.no_of_dest = req->data[2];
+ break;
+ case LAN_PARAM_DEST_TYPE:
+ memcpy(g_lan_config.dest_type, &req->data[2], SIZE_DEST_TYPE);
+ break;
+ case LAN_PARAM_DEST_ADDR:
+ memcpy(g_lan_config.dest_addr, &req->data[2], SIZE_DEST_ADDR);
+ break;
+ default:
+ res->cc = CC_INVALID_PARAM;
+ break;
+ }
+}
+
+// Get LAN Configuration (IPMI/Section 23.2)
+static void
+transport_get_lan_config (unsigned char *request, unsigned char *response,
+ unsigned char *res_len)
+{
+
+ ipmi_req_t *req = (ipmi_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char *data = &res->data[0];
+ unsigned char param = req->data[1];
+
+ // Fill the response with default values
+ res->cc = CC_SUCCESS;
+ *data++ = 0x01; // Parameter revision
+
+ switch (param)
+ {
+ case LAN_PARAM_SET_IN_PROG:
+ *data++ = g_lan_config.set_in_prog;
+ break;
+ case LAN_PARAM_AUTH_SUPPORT:
+ *data++ = g_lan_config.auth_support;
+ break;
+ case LAN_PARAM_AUTH_ENABLES:
+ memcpy(data, g_lan_config.auth_enables, SIZE_AUTH_ENABLES);
+ data += SIZE_AUTH_ENABLES;
+ break;
+ case LAN_PARAM_IP_ADDR:
+ memcpy(data, g_lan_config.ip_addr, SIZE_IP_ADDR);
+ data += SIZE_IP_ADDR;
+ break;
+ case LAN_PARAM_IP_SRC:
+ *data++ = g_lan_config.ip_src;
+ break;
+ case LAN_PARAM_MAC_ADDR:
+ memcpy(data, g_lan_config.mac_addr, SIZE_MAC_ADDR);
+ data += SIZE_MAC_ADDR;
+ break;
+ case LAN_PARAM_NET_MASK:
+ memcpy(data, g_lan_config.net_mask, SIZE_NET_MASK);
+ data += SIZE_NET_MASK;
+ break;
+ case LAN_PARAM_IP_HDR:
+ memcpy(data, g_lan_config.ip_hdr, SIZE_IP_HDR);
+ data += SIZE_IP_HDR;
+ break;
+ case LAN_PARAM_PRI_RMCP_PORT:
+ *data++ = g_lan_config.pri_rmcp_port[0];
+ *data++ = g_lan_config.pri_rmcp_port[1];
+ break;
+ case LAN_PARAM_SEC_RMCP_PORT:
+ *data++ = g_lan_config.sec_rmcp_port[0];
+ *data++ = g_lan_config.sec_rmcp_port[1];
+ break;
+ case LAN_PARAM_ARP_CTRL:
+ *data++ = g_lan_config.arp_ctrl;
+ break;
+ case LAN_PARAM_GARP_INTERVAL:
+ *data++ = g_lan_config.garp_interval;
+ break;
+ case LAN_PARAM_DF_GW_IP_ADDR:
+ memcpy(data, g_lan_config.df_gw_ip_addr, SIZE_IP_ADDR);
+ data += SIZE_IP_ADDR;
+ break;
+ case LAN_PARAM_DF_GW_MAC_ADDR:
+ memcpy(data, g_lan_config.df_gw_mac_addr, SIZE_MAC_ADDR);
+ data += SIZE_MAC_ADDR;
+ break;
+ case LAN_PARAM_BACK_GW_IP_ADDR:
+ memcpy(data, g_lan_config.back_gw_ip_addr, SIZE_IP_ADDR);
+ data += SIZE_IP_ADDR;
+ break;
+ case LAN_PARAM_BACK_GW_MAC_ADDR:
+ memcpy(data, g_lan_config.back_gw_mac_addr, SIZE_MAC_ADDR);
+ data += SIZE_MAC_ADDR;
+ break;
+ case LAN_PARAM_COMMUNITY_STR:
+ memcpy(data, g_lan_config.community_str, SIZE_COMMUNITY_STR);
+ data += SIZE_COMMUNITY_STR;
+ break;
+ case LAN_PARAM_NO_OF_DEST:
+ *data++ = g_lan_config.no_of_dest;
+ break;
+ case LAN_PARAM_DEST_TYPE:
+ memcpy(data, g_lan_config.dest_type, SIZE_DEST_TYPE);
+ data += SIZE_DEST_TYPE;
+ break;
+ case LAN_PARAM_DEST_ADDR:
+ memcpy(data, g_lan_config.dest_addr, SIZE_DEST_ADDR);
+ data += SIZE_DEST_ADDR;
+ break;
+ default:
+ res->cc = CC_INVALID_PARAM;
+ break;
+ }
+
+ if (res->cc == CC_SUCCESS) {
+ *res_len = data - &res->data[0];
+ }
+}
+
+// Handle Transport Commands (IPMI/Section 23)
+static void
+ipmi_handle_transport (unsigned char *request, unsigned char req_len,
+ unsigned char *response, unsigned char *res_len)
+{
+ ipmi_req_t *req = (ipmi_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char cmd = req->cmd;
+
+ pthread_mutex_lock(&m_transport);
+ switch (cmd)
+ {
+ case CMD_TRANSPORT_SET_LAN_CONFIG:
+ transport_set_lan_config (request, response, res_len);
+ break;
+ case CMD_TRANSPORT_GET_LAN_CONFIG:
+ transport_get_lan_config (request, response, res_len);
+ break;
+ default:
+ res->cc = CC_INVALID_CMD;
+ break;
+ }
+ pthread_mutex_unlock(&m_transport);
+}
+
+/*
+ * Function(s) to handle IPMI messages with NetFn: OEM
+ */
+
+static void
+oem_set_proc_info (unsigned char *request, unsigned char *response,
+ unsigned char *res_len)
+{
+ ipmi_req_t *req = (ipmi_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+
+ g_proc_info.type = req->data[1];
+ g_proc_info.freq[0] = req->data[2];
+ g_proc_info.freq[1] = req->data[3];
+
+ res->cc = CC_SUCCESS;
+ *res_len = 0;
+}
+
+static void
+oem_set_dimm_info (unsigned char *request, unsigned char *response,
+ unsigned char *res_len)
+{
+ ipmi_req_t *req = (ipmi_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+
+ unsigned char index = req->data[0];
+
+ g_dimm_info[index].type = req->data[1];
+ g_dimm_info[index].speed[0] = req->data[2];
+ g_dimm_info[index].speed[1] = req->data[3];
+ g_dimm_info[index].size[0] = req->data[4];
+ g_dimm_info[index].size[1] = req->data[5];
+
+ res->cc = CC_SUCCESS;
+ *res_len = 0;
+}
+
+static void
+oem_set_post_start (unsigned char *response, unsigned char *res_len)
+{
+
+ ipmi_res_t *res = (ipmi_res_t *) response;
+
+ // TODO: For now logging the event, need to find usage for this info
+ syslog (LOG_INFO, "POST Start Event\n");
+
+ res->cc = CC_SUCCESS;
+ *res_len = 0;
+}
+
+static void
+oem_set_post_end (unsigned char *response, unsigned char *res_len)
+{
+
+ ipmi_res_t *res = (ipmi_res_t *) response;
+
+ // TODO: For now logging the event, need to find usage for this info
+ syslog (LOG_INFO, "POST End Event\n");
+
+ res->cc = CC_SUCCESS;
+ *res_len = 0;
+}
+
+static void
+ipmi_handle_oem (unsigned char *request, unsigned char req_len,
+ unsigned char *response, unsigned char *res_len)
+{
+
+ ipmi_req_t *req = (ipmi_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+
+ unsigned char cmd = req->cmd;
+
+ pthread_mutex_lock(&m_oem);
+ switch (cmd)
+ {
+ case CMD_OEM_SET_PROC_INFO:
+ oem_set_proc_info (request, response, res_len);
+ break;
+ case CMD_OEM_SET_DIMM_INFO:
+ oem_set_dimm_info (request, response, res_len);
+ break;
+ case CMD_OEM_SET_POST_START:
+ oem_set_post_start (response, res_len);
+ break;
+ case CMD_OEM_SET_POST_END:
+ oem_set_post_end (response, res_len);
+ break;
+ default:
+ res->cc = CC_INVALID_CMD;
+ break;
+ }
+ pthread_mutex_unlock(&m_oem);
+}
+
+/*
+ * Function to handle all IPMI messages
+ */
+static void
+ipmi_handle (unsigned char *request, unsigned char req_len,
+ unsigned char *response, unsigned char *res_len)
+{
+
+ ipmi_req_t *req = (ipmi_req_t *) request;
+ ipmi_res_t *res = (ipmi_res_t *) response;
+ unsigned char netfn;
+
+ netfn = req->netfn_lun >> 2;
+
+ // Provide default values in the response message
+ res->cmd = req->cmd;
+ res->cc = 0xFF; // Unspecified completion code
+ *res_len = 0;
+
+ switch (netfn)
+ {
+ case NETFN_CHASSIS_REQ:
+ res->netfn_lun = NETFN_CHASSIS_RES << 2;
+ ipmi_handle_chassis (request, req_len, response, res_len);
+ break;
+ case NETFN_APP_REQ:
+ res->netfn_lun = NETFN_APP_RES << 2;
+ ipmi_handle_app (request, req_len, response, res_len);
+ break;
+ case NETFN_STORAGE_REQ:
+ res->netfn_lun = NETFN_STORAGE_RES << 2;
+ ipmi_handle_storage (request, req_len, response, res_len);
+ break;
+ case NETFN_TRANSPORT_REQ:
+ res->netfn_lun = NETFN_TRANSPORT_RES << 2;
+ ipmi_handle_transport (request, req_len, response, res_len);
+ break;
+ case NETFN_OEM_REQ:
+ res->netfn_lun = NETFN_OEM_RES << 2;
+ ipmi_handle_oem (request, req_len, response, res_len);
+ break;
+ default:
+ res->netfn_lun = (netfn + 1) << 2;
+ break;
+ }
+
+ // This header includes NetFunction, Command, and Completion Code
+ *res_len += SIZE_IPMI_RES_HDR;
+
+ return;
+}
+
+void
+*conn_handler(void *socket_desc) {
+ int sock = *(int*)socket_desc;
+ int n;
+ unsigned char req_buf[MAX_IPMI_MSG_SIZE];
+ unsigned char res_buf[MAX_IPMI_MSG_SIZE];
+ unsigned char res_len = 0;
+
+ n = recv (sock, req_buf, sizeof(req_buf), 0);
+ if (n <= 0) {
+ syslog(LOG_ALERT, "ipmid: recv() failed with %d\n", n);
+ goto conn_cleanup;
+ }
+
+ ipmi_handle(req_buf, n, res_buf, &res_len);
+
+ if (send (sock, res_buf, res_len, 0) < 0) {
+ syslog(LOG_ALERT, "ipmid: send() failed\n");
+ }
+
+conn_cleanup:
+ close(sock);
+
+ pthread_exit(NULL);
+ return 0;
+}
+
+
+int
+main (void)
+{
+ int s, s2, t, len;
+ struct sockaddr_un local, remote;
+ pthread_t tid;
+
+ daemon(1, 0);
+ openlog("ipmid", LOG_CONS, LOG_DAEMON);
+
+ plat_sel_init();
+ plat_sensor_init();
+ plat_sdr_init();
+ plat_fruid_init();
+
+ pthread_mutex_init(&m_chassis, NULL);
+ pthread_mutex_init(&m_app, NULL);
+ pthread_mutex_init(&m_storage, NULL);
+ pthread_mutex_init(&m_transport, NULL);
+ pthread_mutex_init(&m_oem, NULL);
+
+ if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) == -1)
+ {
+ syslog(LOG_ALERT, "ipmid: socket() failed\n");
+ exit (1);
+ }
+
+ local.sun_family = AF_UNIX;
+ strcpy (local.sun_path, SOCK_PATH);
+ unlink (local.sun_path);
+ len = strlen (local.sun_path) + sizeof (local.sun_family);
+ if (bind (s, (struct sockaddr *) &local, len) == -1)
+ {
+ syslog(LOG_ALERT, "ipmid: bind() failed\n");
+ exit (1);
+ }
+
+ if (listen (s, 5) == -1)
+ {
+ syslog(LOG_ALERT, "ipmid: listen() failed\n");
+ exit (1);
+ }
+
+ while(1) {
+ int n;
+ t = sizeof (remote);
+ if ((s2 = accept (s, (struct sockaddr *) &remote, &t)) < 0) {
+ syslog(LOG_ALERT, "ipmid: accept() failed\n");
+ break;
+ }
+
+ // Creating a worker thread to handle the request
+ // TODO: Need to monitor the server performance with higher load and
+ // see if we need to create pre-defined number of workers and schedule
+ // the requests among them.
+ if (pthread_create(&tid, NULL, conn_handler, (void*) &s2) < 0) {
+ syslog(LOG_ALERT, "ipmid: pthread_create failed\n");
+ close(s2);
+ continue;
+ }
+
+ pthread_detach(tid);
+ }
+
+ close(s);
+
+ pthread_mutex_destroy(&m_chassis);
+ pthread_mutex_destroy(&m_app);
+ pthread_mutex_destroy(&m_storage);
+ pthread_mutex_destroy(&m_transport);
+ pthread_mutex_destroy(&m_oem);
+
+ return 0;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/fruid.h b/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/fruid.h
new file mode 100644
index 0000000..3580b08
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/fruid.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2015-present Facebook. All Rights Reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __FRUID_H__
+#define __FRUID_H__
+
+int plat_fruid_size(void);
+int plat_fruid_data(int offset, int count, unsigned char *data);
+int plat_fruid_init(void);
+
+#endif /* __FRUID_H__ */
diff --git a/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/sdr.c b/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/sdr.c
new file mode 100644
index 0000000..e0a2f9a
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/sdr.c
@@ -0,0 +1,412 @@
+/*
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This file represents platform specific implementation for storing
+ * SDR record entries and acts as back-end for IPMI stack
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include "sdr.h"
+#include "sensor.h"
+#include "timestamp.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <syslog.h>
+#include <string.h>
+
+// SDR Header magic number
+#define SDR_HDR_MAGIC 0xFBFBFBFB
+
+// SDR Header version number
+#define SDR_HDR_VERSION 0x01
+
+// SDR reservation IDs can not be 0x00 or 0xFFFF
+#define SDR_RSVID_MIN 0x01
+#define SDR_RSVID_MAX 0xFFFE
+
+#define SDR_RECORDS_MAX 64 // to support around 64 sensors
+
+// SDR index to keep track
+#define SDR_INDEX_MIN 0
+#define SDR_INDEX_MAX (SDR_RECORDS_MAX - 1)
+
+// Record ID can not be 0x0 (IPMI/Section 31)
+#define SDR_RECID_MIN 1
+#define SDR_RECID_MAX SDR_RECORDS_MAX
+
+// Special RecID value for first and last (IPMI/Section 31)
+#define SDR_RECID_FIRST 0x0000
+#define SDR_RECID_LAST 0xFFFF
+
+#define SDR_VERSION 0x51
+#define SDR_LEN_MAX 64
+
+#define SDR_FULL_TYPE 0x01
+#define SDR_MGMT_TYPE 0x12
+#define SDR_OEM_TYPE 0xC0
+
+#define SDR_FULL_LEN 64
+#define SDR_MGMT_LEN 32
+#define SDR_OEM_LEN 64
+
+// SDR header struct to keep track of SEL Log entries
+typedef struct {
+ int magic; // Magic number to check validity
+ int version; // version number of this header
+ int begin; // index to the first SDR entry
+ int end; // index to the last SDR entry
+ time_stamp_t ts_add; // last addition time stamp
+ time_stamp_t ts_erase; // last erase time stamp
+} sdr_hdr_t;
+
+// Keep track of last Reservation ID
+static int g_rsv_id = 0x01;
+
+// SDR Header and data global structures
+static sdr_hdr_t g_sdr_hdr;
+static sdr_rec_t g_sdr_data[SDR_RECORDS_MAX];
+
+// Add a new SDR entry
+static int
+plat_sdr_add_entry(sdr_rec_t *rec, int *rec_id) {
+ // If SDR is full, return error
+ if (plat_sdr_num_entries() == SDR_RECORDS_MAX) {
+ syslog(LOG_ALERT, "plat_sdr_add_entry: SDR full\n");
+ return -1;
+ }
+
+ // Add Record ID which is array index + 1
+ rec->rec[0] = g_sdr_hdr.end+1;
+
+ // Add the enry at end
+ memcpy(g_sdr_data[g_sdr_hdr.end].rec, rec->rec, sizeof(sdr_rec_t));
+
+ // Return the newly added record ID
+ *rec_id = g_sdr_hdr.end+1;
+
+ // Increment the end pointer
+ ++g_sdr_hdr.end;
+
+ // Update timestamp for add in header
+ time_stamp_fill(g_sdr_hdr.ts_add.ts);
+
+ return 0;
+}
+
+static int
+sdr_add_mgmt_rec(sensor_mgmt_t *p_rec) {
+ int rec_id = 0;
+ sdr_rec_t sdr = { 0 };
+ sdr_mgmt_t rec = { 0 };
+
+ // Populate SDR MGMT record
+ rec.ver = SDR_VERSION;
+ rec.type = SDR_MGMT_TYPE;
+ rec.len = SDR_MGMT_LEN;
+
+ rec.slave_addr = p_rec->slave_addr;
+ rec.chan_no = p_rec->chan_no;
+
+ rec.pwr_state_init = p_rec->pwr_state_init;
+ rec.dev_caps = p_rec->dev_caps;
+ rec.ent_id = p_rec->ent_id;
+ rec.ent_inst = p_rec->ent_inst;
+ rec.oem = p_rec->oem;
+ rec.str_type_len = p_rec->str_type_len;
+ memcpy(rec.str, p_rec->str, SENSOR_STR_SIZE);
+
+ // Copy this record to generic SDR record
+ memcpy(sdr.rec, &rec, SDR_LEN_MAX);
+
+ // Add this record to SDR repo
+ if (plat_sdr_add_entry(&sdr, &rec_id)) {
+ syslog(LOG_ALERT, "sdr_add_mgmt_rec: plat_sdr_add_entry failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+sdr_add_disc_rec(sensor_disc_t *p_rec) {
+ int rec_id = 0;
+ sdr_rec_t sdr = { 0 };
+ sdr_full_t rec = { 0 };
+
+ // Populate SDR FULL record
+ rec.ver = SDR_VERSION;
+ rec.type = SDR_FULL_TYPE;
+ rec.len = SDR_FULL_LEN;
+
+ rec.owner = p_rec->owner;
+ rec.lun = p_rec->lun;
+
+ rec.ent_id = p_rec->ent_id;
+ rec.ent_inst = p_rec->ent_inst;
+ rec.sensor_init = p_rec->sensor_init;
+ rec.sensor_caps = p_rec->sensor_caps;
+ rec.sensor_type = p_rec->sensor_type;
+ rec.evt_read_type = p_rec->evt_read_type;
+ memcpy(rec.assert_evt_mask, p_rec->assert_evt_mask, 2);
+ memcpy(rec.deassert_evt_mask, p_rec->deassert_evt_mask, 2);
+ memcpy(rec.read_evt_mask, p_rec->read_evt_mask, 2);
+ rec.oem = p_rec->oem;
+ rec.str_type_len = p_rec->str_type_len;
+ memcpy(rec.str, p_rec->str, SENSOR_STR_SIZE);
+
+ // Copy this record to generic SDR record
+ memcpy(sdr.rec, &rec, SDR_LEN_MAX);
+
+ // Add this record to SDR repo
+ if (plat_sdr_add_entry(&sdr, &rec_id)) {
+ syslog(LOG_ALERT, "sdr_add_disc_rec: plat_sdr_add_entry failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+sdr_add_thresh_rec(sensor_thresh_t *p_rec) {
+ int rec_id = 0;
+ sdr_rec_t sdr = { 0 };
+ sdr_full_t rec = { 0 };
+
+ // Populate SDR FULL record
+ rec.ver = SDR_VERSION;
+ rec.type = SDR_FULL_TYPE;
+ rec.len = SDR_FULL_LEN;
+
+ rec.owner = p_rec->owner;
+ rec.lun = p_rec->lun;
+
+ rec.ent_id = p_rec->ent_id;
+ rec.ent_inst = p_rec->ent_inst;
+ rec.sensor_init = p_rec->sensor_init;
+ rec.sensor_caps = p_rec->sensor_caps;
+ rec.sensor_type = p_rec->sensor_type;
+ rec.evt_read_type = p_rec->evt_read_type;
+ memcpy(rec.lt_read_mask, p_rec->lt_read_mask, 2);
+ memcpy(rec.ut_read_mask, p_rec->ut_read_mask, 2);
+ memcpy(rec.set_thresh_mask, p_rec->set_thresh_mask, 2);
+ rec.sensor_units1 = p_rec->sensor_units1;
+ rec.sensor_units2 = p_rec->sensor_units2;
+ rec.sensor_units3 = p_rec->sensor_units3;
+ rec.linear = p_rec->linear;
+ rec.m_val = p_rec->m_val;
+ rec.m_tolerance = p_rec->m_tolerance;
+ rec.b_val = p_rec->b_val;
+ rec.b_accuracy = p_rec->b_accuracy;
+ rec.analog_flags = p_rec->analog_flags;
+ rec.nominal = p_rec->nominal;
+ rec.normal_max = p_rec->normal_max;
+ rec.normal_min = p_rec->normal_min;
+ rec.max_reading = p_rec->max_reading;
+ rec.min_reading = p_rec->min_reading;
+ rec.unr_thresh = p_rec->unr_thresh;
+ rec.uc_thresh = p_rec->uc_thresh;
+ rec.unc_thresh = p_rec->unc_thresh;
+ rec.lnr_thresh = p_rec->lnr_thresh;
+ rec.lc_thresh = p_rec->lc_thresh;
+ rec.lnc_thresh = p_rec->lnc_thresh;
+ rec.pos_hyst = p_rec->pos_hyst;
+ rec.neg_hyst = p_rec->neg_hyst;
+ rec.oem = p_rec->oem;
+ rec.str_type_len = p_rec->str_type_len;
+ memcpy(rec.str, p_rec->str, SENSOR_STR_SIZE);
+
+ // Copy this record to generic SDR record
+ memcpy(sdr.rec, &rec, SDR_LEN_MAX);
+
+ // Add this record to SDR repo
+ if (plat_sdr_add_entry(&sdr, &rec_id)) {
+ syslog(LOG_ALERT, "sdr_add_thresh_rec: plat_sdr_add_entry failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+sdr_add_oem_rec(sensor_oem_t *p_rec) {
+ int rec_id = 0;
+ sdr_rec_t sdr = { 0 };
+ sdr_oem_t rec = { 0 };
+
+ // Populate SDR OEM record
+ rec.ver = SDR_VERSION;
+ rec.type = SDR_OEM_TYPE;
+ rec.len = SDR_OEM_LEN;
+
+ memcpy(rec.mfr_id, p_rec->mfr_id, 3);
+ memcpy(rec.oem_data, p_rec->oem_data, SENSOR_OEM_DATA_SIZE);
+
+ // Copy this record to generic SDR record
+ memcpy(sdr.rec, &rec, SDR_LEN_MAX);
+
+ // Add this record to SDR repo
+ if (plat_sdr_add_entry(&sdr, &rec_id)) {
+ syslog(LOG_ALERT, "sdr_add_oem_rec: plat_sdr_add_entry failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+// Platform specific SEL API entry points
+// Retrieve time stamp for recent add operation
+void
+plat_sdr_ts_recent_add(time_stamp_t *ts) {
+ memcpy(ts->ts, g_sdr_hdr.ts_add.ts, 0x04);
+}
+
+// Retrieve time stamp for recent erase operation
+void
+plat_sdr_ts_recent_erase(time_stamp_t *ts) {
+ memcpy(ts->ts, g_sdr_hdr.ts_erase.ts, 0x04);
+}
+
+// Retrieve total number of entries in SDR repo
+int
+plat_sdr_num_entries(void) {
+ return (g_sdr_hdr.end - g_sdr_hdr.begin);
+}
+
+// Retrieve total free space available in SDR repo
+int
+plat_sdr_free_space(void) {
+ int total_space;
+ int used_space;
+
+ total_space = SDR_RECORDS_MAX * sizeof(sdr_rec_t);
+ used_space = plat_sdr_num_entries() * sizeof(sdr_rec_t);
+
+ return (total_space - used_space);
+}
+
+// Reserve an ID that will be used in later operations
+// IPMI/Section 33.11
+int
+plat_sdr_rsv_id() {
+ // Increment the current reservation ID and return
+ if (g_rsv_id++ == SDR_RSVID_MAX) {
+ g_rsv_id = SDR_RSVID_MIN;
+ }
+
+ return g_rsv_id;
+}
+
+// Get the SDR entry for a given record ID
+// IPMI/Section 33.12
+int
+plat_sdr_get_entry(int rsv_id, int read_rec_id, sdr_rec_t *rec,
+ int *next_rec_id) {
+
+ int index;
+
+ // Make sure the rsv_id matches
+ if (rsv_id != g_rsv_id) {
+ syslog(LOG_ALERT, "plat_sdr_get_entry: Reservation ID mismatch\n");
+ return -1;
+ }
+
+ // Find the index in to array based on given index
+ if (read_rec_id == SDR_RECID_FIRST) {
+ index = g_sdr_hdr.begin;
+ } else if (read_rec_id == SDR_RECID_LAST) {
+ index = g_sdr_hdr.end - 1;
+ } else {
+ index = read_rec_id - 1;
+ }
+
+ // If the SDR repo is empty return error
+ if (plat_sdr_num_entries() == 0) {
+ syslog(LOG_ALERT, "plat_sdr_get_entry: No entries\n");
+ return -1;
+ }
+
+ // Check for boundary conditions
+ if ((index < SDR_INDEX_MIN) || (index > SDR_INDEX_MAX)) {
+ syslog(LOG_ALERT, "plat_sdr_get_entry: Invalid Record ID %d\n", read_rec_id);
+ return -1;
+ }
+
+ // Check to make sure the given id is valid
+ if (index < g_sdr_hdr.begin || index >= g_sdr_hdr.end) {
+ syslog(LOG_ALERT, "plat_sdr_get_entry: Wrong Record ID %d\n", read_rec_id);
+ return -1;
+ }
+
+ memcpy(rec->rec, g_sdr_data[index].rec, sizeof(sdr_rec_t));
+
+ // Return the next record ID in the log
+ *next_rec_id = ++read_rec_id;
+
+ // If this is the last entry in the log, return 0xFFFF
+ if (*next_rec_id == g_sdr_hdr.end) {
+ *next_rec_id = SDR_RECID_LAST;
+ }
+
+ return 0;
+}
+
+
+// Initialize SDR Repo structure
+int
+plat_sdr_init(void) {
+ int num;
+ sensor_mgmt_t *p_mgmt;
+ sensor_thresh_t *p_thresh;
+ sensor_disc_t *p_disc;
+ sensor_oem_t *p_oem;
+
+ // Populate SDR Header
+ g_sdr_hdr.magic = SDR_HDR_MAGIC;
+ g_sdr_hdr.version = SDR_HDR_VERSION;
+ g_sdr_hdr.begin = SDR_INDEX_MIN;
+ g_sdr_hdr.end = SDR_INDEX_MIN;
+ memset(g_sdr_hdr.ts_add.ts, 0x0, 4);
+ memset(g_sdr_hdr.ts_erase.ts, 0x0, 4);
+
+ // Populate all mgmt control sensors
+ plat_sensor_mgmt_info(&num, &p_mgmt);
+ for (int i = 0; i < num; i++) {
+ sdr_add_mgmt_rec(&p_mgmt[i]);
+ }
+
+ // Populate all discrete sensors
+ plat_sensor_disc_info(&num, &p_disc);
+ for (int i = 0; i < num; i++) {
+ sdr_add_disc_rec(&p_disc[i]);
+ }
+
+ // Populate all threshold sensors
+ plat_sensor_thresh_info(&num, &p_thresh);
+ for (int i = 0; i < num; i++) {
+ sdr_add_thresh_rec(&p_thresh[i]);
+ }
+
+ // Populate all OEM sensors
+ plat_sensor_oem_info(&num, &p_oem);
+ for (int i = 0; i < num; i++) {
+ sdr_add_oem_rec(&p_oem[i]);
+ }
+
+ return 0;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/sdr.h b/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/sdr.h
new file mode 100644
index 0000000..5e2a591
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/sdr.h
@@ -0,0 +1,132 @@
+/*
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __SDR_H__
+#define __SDR_H__
+
+#include "timestamp.h"
+
+typedef struct {
+ unsigned char rec[64];
+} sdr_rec_t;
+
+// Full Sensor SDR record; IPMI/Section 43.1
+typedef struct {
+ // Sensor Record Header
+ unsigned char rec_id[2];
+ unsigned char ver;
+ unsigned char type;
+ unsigned char len;
+ // Record Key Bytes
+ unsigned char owner;
+ unsigned char lun;
+ unsigned char sensor_num;
+ // Record Body Bytes
+ unsigned char ent_id;
+ unsigned char ent_inst;
+ unsigned char sensor_init;
+ unsigned char sensor_caps;
+ unsigned char sensor_type;
+ unsigned char evt_read_type;
+ union {
+ unsigned char assert_evt_mask[2];
+ unsigned char lt_read_mask[2];
+ };
+ union {
+ unsigned char deassert_evt_mask[2];
+ unsigned char ut_read_mask[2];
+ };
+ union {
+ unsigned char read_evt_mask[2];
+ unsigned char set_thresh_mask[2];
+ };
+ unsigned char sensor_units1;
+ unsigned char sensor_units2;
+ unsigned char sensor_units3;
+ unsigned char linear;
+ unsigned char m_val;
+ unsigned char m_tolerance;
+ unsigned char b_val;
+ unsigned char b_accuracy;
+ unsigned char accuracy_dir;
+ unsigned char rb_exp;
+ unsigned char analog_flags;
+ unsigned char nominal;
+ unsigned char normal_max;
+ unsigned char normal_min;
+ unsigned char max_reading;
+ unsigned char min_reading;
+ unsigned char unr_thresh;
+ unsigned char uc_thresh;
+ unsigned char unc_thresh;
+ unsigned char lnr_thresh;
+ unsigned char lc_thresh;
+ unsigned char lnc_thresh;
+ unsigned char pos_hyst;
+ unsigned char neg_hyst;
+ unsigned char rsvd[2];
+ unsigned char oem;
+ unsigned char str_type_len;
+ char str[16];
+} sdr_full_t;
+
+// Mgmt. Controller SDR record; IPMI/ Section 43.9
+typedef struct {
+ // Sensor Record Header
+ unsigned char rec_id[2];
+ unsigned char ver;
+ unsigned char type;
+ unsigned char len;
+ // Record Key Bytes
+ unsigned char slave_addr;
+ unsigned char chan_no;
+ // Record Body Bytes
+ unsigned char pwr_state_init;
+ unsigned char dev_caps;
+ unsigned char rsvd[3];
+ unsigned char ent_id;
+ unsigned char ent_inst;
+ unsigned char oem;
+ unsigned char str_type_len;
+ char str[16];
+} sdr_mgmt_t;
+
+// OEM type SDR record; IPMI/Section 43.12
+typedef struct {
+ // Sensor Record Header
+ unsigned char rec_id[2];
+ unsigned char ver;
+ unsigned char type;
+ unsigned char len;
+ // Record Body Bytes
+ unsigned char mfr_id[3];
+ unsigned char oem_data[56];
+} sdr_oem_t;
+
+void plat_sdr_ts_recent_add(time_stamp_t *ts);
+void plat_sdr_ts_recent_erase(time_stamp_t *ts);
+int plat_sdr_num_entries(void);
+int plat_sdr_free_space(void);
+int plat_sdr_rsv_id();
+int plat_sdr_get_entry(int rsv_id, int read_rec_id, sdr_rec_t *rec,
+ int *next_rec_id);
+int plat_sdr_init(void);
+
+#endif /* __SDR_H__ */
diff --git a/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/sel.c b/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/sel.c
new file mode 100644
index 0000000..a7aa78f
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/sel.c
@@ -0,0 +1,438 @@
+/*
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This file represents platform specific implementation for storing
+ * SEL logs and acts as back-end for IPMI stack
+ *
+ * TODO: Optimize the file handling to keep file open always instead of
+ * current open/seek/close
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include "sel.h"
+#include "timestamp.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <syslog.h>
+#include <string.h>
+
+// SEL File.
+#define SEL_LOG_FILE "/mnt/data/sel.bin"
+
+// SEL Header magic number
+#define SEL_HDR_MAGIC 0xFBFBFBFB
+
+// SEL Header version number
+#define SEL_HDR_VERSION 0x01
+
+// SEL Data offset from file beginning
+#define SEL_DATA_OFFSET 0x100
+
+// SEL reservation IDs can not be 0x00 or 0xFFFF
+#define SEL_RSVID_MIN 0x01
+#define SEL_RSVID_MAX 0xFFFE
+
+// Number of SEL records before wrap
+#define SEL_RECORDS_MAX 128 // TODO: Based on need we can make it bigger
+#define SEL_ELEMS_MAX (SEL_RECORDS_MAX+1)
+
+// Index for circular array
+#define SEL_INDEX_MIN 0x00
+#define SEL_INDEX_MAX SEL_RECORDS_MAX
+
+// Record ID can not be 0x0 (IPMI/Section 31)
+#define SEL_RECID_MIN (SEL_INDEX_MIN+1)
+#define SEL_RECID_MAX (SEL_INDEX_MAX+1)
+
+// Special RecID value for first and last (IPMI/Section 31)
+#define SEL_RECID_FIRST 0x0000
+#define SEL_RECID_LAST 0xFFFF
+
+// SEL header struct to keep track of SEL Log entries
+typedef struct {
+ int magic; // Magic number to check validity
+ int version; // version number of this header
+ int begin; // index to the begining of the log
+ int end; // index to end of the log
+ time_stamp_t ts_add; // last addition time stamp
+ time_stamp_t ts_erase; // last erase time stamp
+} sel_hdr_t;
+
+// Keep track of last Reservation ID
+static int g_rsv_id = 0x01;
+
+// Cached version of SEL Header and data
+static sel_hdr_t g_sel_hdr;
+static sel_msg_t g_sel_data[SEL_ELEMS_MAX];
+
+// Local helper functions to interact with file system
+static int
+file_get_sel_hdr(void) {
+ FILE *fp;
+
+ fp = fopen(SEL_LOG_FILE, "r");
+ if (fp == NULL) {
+ return -1;
+ }
+
+ if (fread(&g_sel_hdr, sizeof(sel_hdr_t), 1, fp) <= 0) {
+ syslog(LOG_ALERT, "file_get_sel_hdr: fread\n");
+ fclose (fp);
+ return -1;
+ }
+
+ fclose(fp);
+ return 0;
+}
+
+static int
+file_get_sel_data(void) {
+ FILE *fp;
+
+ fp = fopen(SEL_LOG_FILE, "r");
+ if (fp == NULL) {
+ syslog(LOG_ALERT, "file_get_sel_data: fopen\n");
+ return -1;
+ }
+
+ if (fseek(fp, SEL_DATA_OFFSET, SEEK_SET)) {
+ syslog(LOG_ALERT, "file_get_sel_data: fseek\n");
+ fclose(fp);
+ return -1;
+ }
+
+ unsigned char buf[SEL_ELEMS_MAX * 16];
+ if (fread(buf, 1, SEL_ELEMS_MAX * sizeof(sel_msg_t), fp) <= 0) {
+ syslog(LOG_ALERT, "file_get_sel_data: fread\n");
+ fclose(fp);
+ return -1;
+ }
+
+ fclose(fp);
+
+ for (int i = 0; i < SEL_ELEMS_MAX; i++) {
+ for (int j = 0; j < sizeof(sel_msg_t);j++) {
+ g_sel_data[i].msg[j] = buf[i*16 + j];
+ }
+ }
+
+ return 0;
+}
+
+static int
+file_store_sel_hdr(void) {
+ FILE *fp;
+
+ fp = fopen(SEL_LOG_FILE, "r+");
+ if (fp == NULL) {
+ syslog(LOG_ALERT, "file_store_sel_hdr: fopen\n");
+ return -1;
+ }
+
+ if (fwrite(&g_sel_hdr, sizeof(sel_hdr_t), 1, fp) <= 0) {
+ syslog(LOG_ALERT, "file_store_sel_hdr: fwrite\n");
+ fclose(fp);
+ return -1;
+ }
+
+ fclose(fp);
+
+ return 0;
+}
+
+static int
+file_store_sel_data(int recId, sel_msg_t *data) {
+ FILE *fp;
+ int index;
+
+ fp = fopen(SEL_LOG_FILE, "r+");
+ if (fp == NULL) {
+ syslog(LOG_ALERT, "file_store_sel_data: fopen\n");
+ return -1;
+ }
+
+ // Records are stored using zero-based index
+ index = (recId-1) * sizeof(sel_msg_t);
+
+ if (fseek(fp, SEL_DATA_OFFSET+index, SEEK_SET)) {
+ syslog(LOG_ALERT, "file_store_sel_data: fseek\n");
+ fclose(fp);
+ return -1;
+ }
+
+ if (fwrite(data->msg, sizeof(sel_msg_t), 1, fp) <= 0) {
+ syslog(LOG_ALERT, "file_store_sel_data: fwrite\n");
+ fclose(fp);
+ return -1;
+ }
+
+ fclose(fp);
+
+ return 0;
+}
+
+// Platform specific SEL API entry points
+// Retrieve time stamp for recent add operation
+void
+plat_sel_ts_recent_add(time_stamp_t *ts) {
+ memcpy(ts->ts, g_sel_hdr.ts_add.ts, 0x04);
+}
+
+// Retrieve time stamp for recent erase operation
+void
+plat_sel_ts_recent_erase(time_stamp_t *ts) {
+ memcpy(ts->ts, g_sel_hdr.ts_erase.ts, 0x04);
+}
+
+// Retrieve total number of entries in SEL log
+int
+plat_sel_num_entries(void) {
+ if (g_sel_hdr.begin <= g_sel_hdr.end) {
+ return (g_sel_hdr.end - g_sel_hdr.begin);
+ } else {
+ return (g_sel_hdr.end + (SEL_INDEX_MAX - g_sel_hdr.begin + 1));
+ }
+}
+
+// Retrieve total free space available in SEL log
+int
+plat_sel_free_space(void) {
+ int total_space;
+ int used_space;
+
+ total_space = SEL_RECORDS_MAX * sizeof(sel_msg_t);
+ used_space = plat_sel_num_entries() * sizeof(sel_msg_t);
+
+ return (total_space - used_space);
+}
+
+// Reserve an ID that will be used in later operations
+// IPMI/Section 31.4
+int
+plat_sel_rsv_id() {
+ // Increment the current reservation ID and return
+ if (g_rsv_id++ == SEL_RSVID_MAX) {
+ g_rsv_id = SEL_RSVID_MIN;
+ }
+
+ return g_rsv_id;
+}
+
+// Get the SEL entry for a given record ID
+// IPMI/Section 31.5
+int
+plat_sel_get_entry(int read_rec_id, sel_msg_t *msg, int *next_rec_id) {
+
+ int index;
+
+ // Find the index in to array based on given index
+ if (read_rec_id == SEL_RECID_FIRST) {
+ index = g_sel_hdr.begin;
+ } else if (read_rec_id == SEL_RECID_LAST) {
+ if (g_sel_hdr.end) {
+ index = g_sel_hdr.end - 1;
+ } else {
+ index = SEL_INDEX_MAX;
+ }
+ } else {
+ index = read_rec_id - 1;
+ }
+
+ // If the log is empty return error
+ if (plat_sel_num_entries() == 0) {
+ syslog(LOG_ALERT, "plat_sel_get_entry: No entries\n");
+ return -1;
+ }
+
+ // Check for boundary conditions
+ if ((index < SEL_INDEX_MIN) || (index > SEL_INDEX_MAX)) {
+ syslog(LOG_ALERT, "plat_sel_get_entry: Invalid Record ID %d\n", read_rec_id);
+ return -1;
+ }
+
+ // If begin < end, check to make sure the given id falls between
+ if (g_sel_hdr.begin < g_sel_hdr.end) {
+ if (index < g_sel_hdr.begin || index >= g_sel_hdr.end) {
+ syslog(LOG_ALERT, "plat_sel_get_entry: Wrong Record ID %d\n", read_rec_id);
+ return -1;
+ }
+ }
+
+ // If end < begin, check to make sure the given id is valid
+ if (g_sel_hdr.begin > g_sel_hdr.end) {
+ if (index >= g_sel_hdr.end && index < g_sel_hdr.begin) {
+ syslog(LOG_ALERT, "plat_sel_get_entry: Wrong Record ID2 %d\n", read_rec_id);
+ return -1;
+ }
+ }
+
+ memcpy(msg->msg, g_sel_data[index].msg, sizeof(sel_msg_t));
+
+ // Return the next record ID in the log
+ *next_rec_id = read_rec_id++;
+ if (*next_rec_id > SEL_INDEX_MAX) {
+ *next_rec_id = SEL_INDEX_MIN;
+ }
+
+ // If this is the last entry in the log, return 0xFFFF
+ if (*next_rec_id == g_sel_hdr.end) {
+ *next_rec_id = SEL_RECID_LAST;
+ }
+
+ return 0;
+}
+
+// Add a new entry in to SEL log
+// IPMI/Section 31.6
+int
+plat_sel_add_entry(sel_msg_t *msg, int *rec_id) {
+ // If the SEL if full, roll over. To keep track of empty condition, use
+ // one empty location less than the max records.
+ if (plat_sel_num_entries() == SEL_RECORDS_MAX) {
+ syslog(LOG_ALERT, "plat_sel_add_entry: SEL rollover\n");
+ if (++g_sel_hdr.begin > SEL_INDEX_MAX) {
+ g_sel_hdr.begin = SEL_INDEX_MIN;
+ }
+ }
+
+ // Update message's time stamp starting at byte 4
+ time_stamp_fill(&msg->msg[3]);
+
+ // Add the enry at end
+ memcpy(g_sel_data[g_sel_hdr.end].msg, msg->msg, sizeof(sel_msg_t));
+
+ // Return the newly added record ID
+ *rec_id = g_sel_hdr.end+1;
+
+ if (file_store_sel_data(*rec_id, msg)) {
+ syslog(LOG_ALERT, "plat_sel_add_entry: file_store_sel_data\n");
+ return -1;
+ }
+
+ // Increment the end pointer
+ if (++g_sel_hdr.end > SEL_INDEX_MAX) {
+ g_sel_hdr.end = SEL_INDEX_MIN;
+ }
+
+ // Update timestamp for add in header
+ time_stamp_fill(g_sel_hdr.ts_add.ts);
+
+ // Store the structure persistently
+ if (file_store_sel_hdr()) {
+ syslog(LOG_ALERT, "plat_sel_add_entry: file_store_sel_hdr\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+// Erase the SEL completely
+// IPMI/Section 31.9
+// Note: To reduce wear/tear, instead of erasing, manipulating the metadata
+int
+plat_sel_erase(int rsv_id) {
+ if (rsv_id != g_rsv_id) {
+ return -1;
+ }
+
+ // Erase SEL Logs
+ g_sel_hdr.begin = SEL_INDEX_MIN;
+ g_sel_hdr.end = SEL_INDEX_MIN;
+
+ // Update timestamp for erase in header
+ time_stamp_fill(g_sel_hdr.ts_erase.ts);
+
+ // Store the structure persistently
+ if (file_store_sel_hdr()) {
+ syslog(LOG_ALERT, "plat_sel_erase: file_store_sel_hdr\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+// To get the erase status while erase happens
+// IPMI/Section 31.2
+// Note: Since we are not doing offline erasing, need not return in-progress state
+int
+plat_sel_erase_status(int rsv_id, sel_erase_stat_t *status) {
+ if (rsv_id != g_rsv_id) {
+ return -1;
+ }
+
+ // Since we do not do any offline erasing, always return erase done
+ *status = SEL_ERASE_DONE;
+
+ return 0;
+}
+
+// Initialize SEL log file
+int
+plat_sel_init(void) {
+ FILE *fp;
+
+ // Check if the file exists or not
+ if (access(SEL_LOG_FILE, F_OK) == 0) {
+ // Since file is present, fetch all the contents to cache
+ if (file_get_sel_hdr()) {
+ syslog(LOG_ALERT, "plat_init_sel: file_get_sel_hdr\n");
+ return -1;
+ }
+
+ if (file_get_sel_data()) {
+ syslog(LOG_ALERT, "plat_init_sel: file_get_sel_data\n");
+ return -1;
+ }
+
+ return 0;
+ }
+
+ // File not present, so create the file
+ fp = fopen(SEL_LOG_FILE, "w+");
+ if (fp == NULL) {
+ syslog(LOG_ALERT, "plat_init_sel: fopen\n");
+ return -1;
+ }
+
+ fclose (fp);
+
+ // Populate SEL Header in to the file
+ g_sel_hdr.magic = SEL_HDR_MAGIC;
+ g_sel_hdr.version = SEL_HDR_VERSION;
+ g_sel_hdr.begin = SEL_INDEX_MIN;
+ g_sel_hdr.end = SEL_INDEX_MIN;
+ memset(g_sel_hdr.ts_add.ts, 0x0, 4);
+ memset(g_sel_hdr.ts_erase.ts, 0x0, 4);
+
+ if (file_store_sel_hdr()) {
+ syslog(LOG_ALERT, "plat_init_sel: file_store_sel_hdr\n");
+ return -1;
+ }
+
+ // Populate SEL Data in to the file
+ for (int i = 1; i <= SEL_RECORDS_MAX; i++) {
+ sel_msg_t msg = {0};
+ if (file_store_sel_data(i, &msg)) {
+ syslog(LOG_ALERT, "plat_init_sel: file_store_sel_data\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/sel.h b/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/sel.h
new file mode 100644
index 0000000..81dbcdf
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/sel.h
@@ -0,0 +1,51 @@
+/*
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __SEL_H__
+#define __SEL_H__
+
+#include "timestamp.h"
+
+enum {
+ IPMI_SEL_INIT_ERASE = 0xAA,
+ IPMI_SEL_ERASE_STAT = 0x00,
+};
+
+typedef enum {
+ SEL_ERASE_IN_PROG = 0x00,
+ SEL_ERASE_DONE = 0x01,
+} sel_erase_stat_t;
+
+typedef struct {
+ unsigned char msg[16];
+} sel_msg_t;
+
+void plat_sel_ts_recent_add(time_stamp_t *ts);
+void plat_sel_ts_recent_erase(time_stamp_t *ts);
+int plat_sel_num_entries(void);
+int plat_sel_free_space(void);
+int plat_sel_rsv_id();
+int plat_sel_get_entry(int read_rec_id, sel_msg_t *msg, int *next_rec_id);
+int plat_sel_add_entry(sel_msg_t *msg, int *rec_id);
+int plat_sel_erase(int rsv_id);
+int plat_sel_erase_status(int rsv_id, sel_erase_stat_t *status);
+int plat_sel_init(void);
+
+#endif /* __SEL_H__ */
diff --git a/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/sensor.h b/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/sensor.h
new file mode 100644
index 0000000..5d8c11a
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/sensor.h
@@ -0,0 +1,117 @@
+/*
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __SENSOR_H__
+#define __SENSOR_H__
+
+#include "timestamp.h"
+
+#define IANA_ID_SIZE 3
+#define SENSOR_STR_SIZE 16
+#define SENSOR_OEM_DATA_SIZE 56
+
+// Threshold Sensor Descriptor
+typedef struct {
+ unsigned char owner;
+ unsigned char lun;
+ unsigned char sensor_num;
+ unsigned char ent_id;
+ unsigned char ent_inst;
+ unsigned char sensor_init;
+ unsigned char sensor_caps;
+ unsigned char sensor_type;
+ unsigned char evt_read_type;
+ unsigned char lt_read_mask[2];
+ unsigned char ut_read_mask[2];
+ unsigned char set_thresh_mask[2];
+ unsigned char sensor_units1;
+ unsigned char sensor_units2;
+ unsigned char sensor_units3;
+ unsigned char linear;
+ unsigned char m_val;
+ unsigned char m_tolerance;
+ unsigned char b_val;
+ unsigned char b_accuracy;
+ unsigned char accuracy_dir;
+ unsigned char rb_exp;
+ unsigned char analog_flags;
+ unsigned char nominal;
+ unsigned char normal_max;
+ unsigned char normal_min;
+ unsigned char max_reading;
+ unsigned char min_reading;
+ unsigned char unr_thresh;
+ unsigned char uc_thresh;
+ unsigned char unc_thresh;
+ unsigned char lnr_thresh;
+ unsigned char lc_thresh;
+ unsigned char lnc_thresh;
+ unsigned char pos_hyst;
+ unsigned char neg_hyst;
+ unsigned char oem;
+ unsigned char str_type_len;
+ char str[SENSOR_STR_SIZE];
+} sensor_thresh_t;
+
+// Discrete Sensor Descriptor
+typedef struct {
+ unsigned char owner;
+ unsigned char lun;
+ unsigned char sensor_num;
+ unsigned char ent_id;
+ unsigned char ent_inst;
+ unsigned char sensor_init;
+ unsigned char sensor_caps;
+ unsigned char sensor_type;
+ unsigned char evt_read_type;
+ unsigned char assert_evt_mask[2];
+ unsigned char deassert_evt_mask[2];
+ unsigned char read_evt_mask[2];
+ unsigned char oem;
+ unsigned char str_type_len;
+ char str[SENSOR_STR_SIZE];
+} sensor_disc_t;
+
+// Mgmt. Controller Sensor Descriptor
+typedef struct {
+ unsigned char slave_addr;
+ unsigned char chan_no;
+ unsigned char pwr_state_init;
+ unsigned char dev_caps;
+ unsigned char ent_id;
+ unsigned char ent_inst;
+ unsigned char oem;
+ unsigned char str_type_len;
+ char str[SENSOR_STR_SIZE];
+} sensor_mgmt_t;
+
+// OEM type Sensor Descriptor
+typedef struct {
+ unsigned char mfr_id[IANA_ID_SIZE];
+ unsigned char oem_data[SENSOR_OEM_DATA_SIZE];
+} sensor_oem_t;
+
+void plat_sensor_mgmt_info(int *num, sensor_mgmt_t **p_sensor);
+void plat_sensor_disc_info(int *num, sensor_disc_t **p_sensor);
+void plat_sensor_thresh_info(int *num, sensor_thresh_t **p_sensor);
+void plat_sensor_oem_info(int *num, sensor_oem_t **p_sensor);
+int plat_sensor_init(void);
+
+#endif /* __SENSOR_H__ */
diff --git a/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/timestamp.c b/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/timestamp.c
new file mode 100644
index 0000000..11ac03e
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/timestamp.c
@@ -0,0 +1,46 @@
+/*
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This file is a helper file to fill timestamps from platform
+ * used by SEL Logs, SDR records etc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <syslog.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+
+// Local helper function to fill time stamp
+void
+time_stamp_fill(unsigned char *ts) {
+ unsigned int time;
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+
+ time = tv.tv_sec;
+ ts[0] = time & 0xFF;
+ ts[1] = (time >> 8) & 0xFF;
+ ts[2] = (time >> 16) & 0xFF;
+ ts[3] = (time >> 24) & 0xFF;
+
+ return;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/timestamp.h b/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/timestamp.h
new file mode 100644
index 0000000..430dd23
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/timestamp.h
@@ -0,0 +1,30 @@
+/*
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __TIMESTAMP_H__
+#define __TIMESTAMP_H__
+
+typedef struct {
+ unsigned char ts[4];
+} time_stamp_t;
+
+void time_stamp_fill(unsigned char *ts);
+
+#endif /* __TIMESTAMP_H__ */
diff --git a/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/wedge/fruid.c b/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/wedge/fruid.c
new file mode 100644
index 0000000..1d9ddda
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/wedge/fruid.c
@@ -0,0 +1,183 @@
+/*
+ *
+ * Copyright 2015-present Facebook. All Rights Reserved.
+ *
+ * This file provides platform specific implementation of FRUID information
+ *
+ * FRUID specification can be found at
+ * www.intel.com/content/dam/www/public/us/en/documents/product-briefs/platform-management-fru-document-rev-1-2-feb-2013.pdf
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "../fruid.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <syslog.h>
+#include <string.h>
+#include <facebook/wedge_eeprom.h>
+
+#define WEDGE_FRUID_SIZE 0x100
+
+#define COMMON_HDR_VER 1
+#define PROD_INFO_VER 1
+
+#define PROD_INFO_AREA_OFFSET 0x10
+#define PROD_INFO_CKSUM_OFFSET (PROD_INFO_AREA_OFFSET + 1)
+#define LANG_CODE_ENGLISH 25
+#define TYPE_STR 0xC0
+#define TYPE_LAST 0xC1
+#define ZERO_CKSUM_CONST 0x100
+#define LEN_BYTE_SIZE 8
+
+typedef struct _fruid_common_hdr_t {
+ unsigned char ver;
+ unsigned char internal_use_area_offset;
+ unsigned char chassis_info_area_offset;
+ unsigned char board_info_area_offset;
+ unsigned char prod_info_area_offset;
+ unsigned char multi_record_area_offset;
+ unsigned char padding;
+ unsigned char cksum;
+} fruid_common_hdr_t;
+
+// Global structures
+static unsigned char g_fruid[WEDGE_FRUID_SIZE] = {0};
+
+static void
+populate_fruid(void) {
+
+ memset(&g_fruid, sizeof(g_fruid), 0);
+
+ fruid_common_hdr_t *chdr = g_fruid;
+
+ // Set Common Header version
+ chdr->ver = COMMON_HDR_VER;
+
+ // Product Info Area offset in multiples of 8 bytes
+ chdr->prod_info_area_offset = PROD_INFO_AREA_OFFSET/LEN_BYTE_SIZE;
+
+ // Calculate zero checksum
+ chdr->cksum = chdr->ver + chdr->prod_info_area_offset;
+ chdr->cksum = ZERO_CKSUM_CONST - chdr->cksum;
+
+ // Retrieve Wedge EEPROM content
+ struct wedge_eeprom_st eeprom;
+ int rc = 0;
+ rc = wedge_eeprom_parse(NULL, &eeprom);
+ if (rc)
+ {
+ syslog(LOG_ALERT, "populate_fruid: wedge_eeprom_parse returns %d\n", rc);
+ return;
+ }
+
+ // Start index at beginning of product info area
+ int i = PROD_INFO_AREA_OFFSET;
+ g_fruid[i++] = PROD_INFO_VER;
+ g_fruid[i++] = 0x00; // prod area length; filled at end
+ g_fruid[i++] = LANG_CODE_ENGLISH;
+
+#define _APPEND_STR_VALUE(name) do { \
+ if (sizeof(name) < 1 || i + 1 + sizeof(name) >= sizeof(g_fruid)) { \
+ break; \
+ } \
+ g_fruid[i++] = TYPE_STR + sizeof(name); \
+ memcpy(&g_fruid[i], name, sizeof(name)); \
+ i += sizeof(name); \
+} while(0)
+
+ // Fill system manufacturer field
+
+ _APPEND_STR_VALUE(eeprom.fbw_system_manufacturer);
+
+ // Fill product name field
+ _APPEND_STR_VALUE(eeprom.fbw_product_name);
+
+ // Fill product number field
+ _APPEND_STR_VALUE(eeprom.fbw_product_number);
+
+ // convert product version to string and fill
+ char vbuf[5] = {0};
+ snprintf(vbuf, sizeof(vbuf), "%0X", eeprom.fbw_product_version);
+ _APPEND_STR_VALUE(vbuf);
+
+ // Fill product serial number field
+ _APPEND_STR_VALUE(eeprom.fbw_product_serial);
+
+ // Fill product asset tag field
+ _APPEND_STR_VALUE(eeprom.fbw_product_asset);
+
+ // Fill fruid file with dummy file name
+ char fruid_file_name[] = "fruid_1.0";
+ _APPEND_STR_VALUE(fruid_file_name);
+
+ // Field to indicate the last entry
+ g_fruid[i++] = TYPE_LAST;
+
+ // length of the area in multiples of 8 bytes
+ int len = i-PROD_INFO_AREA_OFFSET+1;
+ if (len % LEN_BYTE_SIZE){
+ // For non-multiple of 8 bytes, add one for partial data
+ g_fruid[PROD_INFO_CKSUM_OFFSET] = len/LEN_BYTE_SIZE + 1;
+ // And also increment index to keep checksum byte
+ i += (len % LEN_BYTE_SIZE);
+ } else {
+ g_fruid[PROD_INFO_CKSUM_OFFSET] = len/LEN_BYTE_SIZE;
+ }
+
+ // Calculate zero checksum by adding all the values in product info area
+ for (int j = PROD_INFO_AREA_OFFSET; j < i; j++) {
+ g_fruid[i] += g_fruid[j];
+ }
+
+ // Calculate final cksum by subtraction
+ g_fruid[i] = ZERO_CKSUM_CONST - g_fruid[i];
+
+#undef _APPEND_STR_VALUE
+
+ return;
+}
+
+// Access functions for FRUID
+int
+plat_fruid_size(void) {
+ return WEDGE_FRUID_SIZE;
+}
+
+int
+plat_fruid_data(int offset, int count, unsigned char *data) {
+ // Check for the boundary condition
+ if ((offset + count) > WEDGE_FRUID_SIZE) {
+ return -1;
+ }
+
+ // Copy the FRUID content from the global structure
+ memcpy(data, &(g_fruid[offset]), count);
+
+ return 0;
+}
+
+// Initialize FRUID
+int
+plat_fruid_init(void) {
+
+ // Populate FRUID global structure
+ populate_fruid();
+
+ return 0;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/wedge/sensor.c b/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/wedge/sensor.c
new file mode 100644
index 0000000..ce3d4fb
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/platform/wedge/sensor.c
@@ -0,0 +1,371 @@
+/*
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This file provides platform specific implementation of sensor information
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include "../sensor.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <syslog.h>
+#include <string.h>
+
+#define SENSOR_MGMT_MAX 1
+#define SENSOR_DISC_MAX 8
+#define SENSOR_THRESH_MAX 1
+#define SENSOR_OEM_MAX 1
+
+#define BMC_SLAVE_ADDR 0x20
+
+typedef struct {
+ unsigned char num;
+ sensor_mgmt_t sensor[SENSOR_MGMT_MAX];
+} sensor_mgmt_info_t;
+
+typedef struct {
+ unsigned char num;
+ sensor_disc_t sensor[SENSOR_DISC_MAX];
+} sensor_disc_info_t;
+
+typedef struct {
+ unsigned char num;
+ sensor_thresh_t sensor[SENSOR_THRESH_MAX];
+} sensor_thresh_info_t;
+
+typedef struct {
+ unsigned char num;
+ sensor_oem_t sensor[SENSOR_OEM_MAX];
+} sensor_oem_info_t;
+
+// Global structures
+static sensor_mgmt_info_t g_sensor_mgmt = {0};
+static sensor_disc_info_t g_sensor_disc = {0};
+static sensor_thresh_info_t g_sensor_thresh = {0};
+static sensor_oem_info_t g_sensor_oem = {0};
+
+static void
+populate_mgmt_sensors(void) {
+ sensor_mgmt_t sensor = {0};
+
+ // Add record for the AST2100 BMC
+ sensor.slave_addr = BMC_SLAVE_ADDR;
+ sensor.chan_no = 0x0; // Primary BMC controller
+
+ // Init Agent = false
+ sensor.pwr_state_init = 0x00;
+
+ // FRUID = true, SEL = true, SDR = true, Sensor = true
+ sensor.dev_caps = 0x0F;
+
+ // Device ID string
+ // Type - 0xC0: ASCII, Length - 0x09
+ sensor.str_type_len = 0xC0 + 0x09;
+ strncpy(sensor.str, "Wedge-BMC", 0x09);
+
+ // Add this sensor to the global table
+ if (g_sensor_mgmt.num >= SENSOR_MGMT_MAX) {
+ syslog(LOG_ALERT, "populate_mgmt_sensors: num exceeded\n");
+ return;
+ }
+
+ memcpy(&g_sensor_mgmt.sensor[g_sensor_mgmt.num], &sensor, sizeof(sensor_mgmt_t));
+
+ g_sensor_mgmt.num++;
+
+ return;
+}
+
+static void
+populate_disc_sensors(void) {
+
+ sensor_disc_t sensor = {0};
+
+ // Sensor uS Status
+ // Sensor# 0x10
+ // EntitiyId# 0xD0, EntityInst# 0x00
+ // Sensor Type# Chassis 0x18
+ // Event Read/Type# OEM 0x70
+ sensor.owner= BMC_SLAVE_ADDR;
+ sensor.lun = 0x00;
+ sensor.sensor_num = 0x10;
+
+ sensor.ent_id = 0xD0;
+ sensor.ent_inst = 0x00;
+ // Enable Scanning, Enable Events
+ sensor.sensor_init = 0x63;
+ // Supports Auto Re-Arm
+ sensor.sensor_caps = 0x40;
+ sensor.sensor_type = 0x18;
+ sensor.evt_read_type = 0x70;
+ // 1-bit for CPU0 Thermal Trip
+ sensor.assert_evt_mask[0] = 0x04;
+ sensor.deassert_evt_mask[0] = 0x00;
+ sensor.read_evt_mask[0] = 0x04;
+
+ // Device ID string
+ // Type - 0xC0: ASCII, Length - 12
+ sensor.str_type_len = 0xC0 + 9;
+ strncpy(sensor.str, "uS-Status", 9);
+
+ // Add this sensor to the global table
+ if (g_sensor_disc.num >= SENSOR_DISC_MAX) {
+ syslog(LOG_ALERT, "populate_disc_sensors: num exceeded\n");
+ return;
+ }
+
+ memcpy(&g_sensor_disc.sensor[g_sensor_disc.num], &sensor, sizeof(sensor_disc_t));
+
+ g_sensor_disc.num++;
+
+ // Sensor SEL Status
+ // Sensor# 0x5F
+ // EntitiyId# 0xD0, EntityInst# 0x02
+ // Sensor Type# OEM: 0xC0
+ // Event Read/Type# Sensor Specific: 0x6F
+ sensor.owner= BMC_SLAVE_ADDR;
+ sensor.lun = 0x00;
+ sensor.sensor_num = 0x5F;
+
+ sensor.ent_id = 0xD0;
+ sensor.ent_inst = 0x02;
+ // Enable Scanning, Enable Events
+ sensor.sensor_init = 0x63;
+ // Supports Auto Re-Arm
+ sensor.sensor_caps = 0x40;
+ sensor.sensor_type = 0xC0;
+ sensor.evt_read_type = 0x6F;
+ // SEL Clear(bit1), SEL Rollover(bit8)
+ sensor.assert_evt_mask[0] = 0x02;
+ sensor.assert_evt_mask[1] = 0x01;
+ sensor.deassert_evt_mask[0] = 0x00;
+ sensor.deassert_evt_mask[1] = 0x00;
+ sensor.read_evt_mask[0] = 0x02;
+ sensor.read_evt_mask[1] = 0x01;
+
+ // Device ID string
+ // Type - 0xC0: ASCII, Length - 12
+ sensor.str_type_len = 0xC0 + 10;
+ strncpy(sensor.str, "SEL-Status", 10);
+
+ // Add this sensor to the global table
+ if (g_sensor_disc.num >= SENSOR_DISC_MAX) {
+ syslog(LOG_ALERT, "populate_disc_sensors: num exceeded\n");
+ return;
+ }
+
+ memcpy(&g_sensor_disc.sensor[g_sensor_disc.num], &sensor, sizeof(sensor_disc_t));
+
+ g_sensor_disc.num++;
+
+
+ // Sensor WDT
+ // Sensor# 0x60
+ // EntitiyId# 0xD0, EntityInst# 0x03
+ // Sensor Type# WDT2: 0x23
+ // Event Read/Type# Sensor Specific: 0x6F
+ sensor.owner= BMC_SLAVE_ADDR;
+ sensor.lun = 0x00;
+ sensor.sensor_num = 0x60;
+
+ sensor.ent_id = 0xD0;
+ sensor.ent_inst = 0x03;
+ // Enable Scanning, Enable Events
+ sensor.sensor_init = 0x63;
+ // Supports Auto Re-Arm
+ sensor.sensor_caps = 0x40;
+ sensor.sensor_type = 0x23;
+ sensor.evt_read_type = 0x6F;
+ // 5 bits for expiry, reset, pwrdown, pwrcycle, timer
+ sensor.assert_evt_mask[0] = 0x0F;
+ sensor.assert_evt_mask[1] = 0x01;
+ sensor.deassert_evt_mask[0] = 0x00;
+ sensor.deassert_evt_mask[1] = 0x00;
+ sensor.read_evt_mask[0] = 0x0F;
+ sensor.read_evt_mask[1] = 0x01;
+
+ // Device ID string
+ // Type - 0xC0: ASCII, Length - 12
+ sensor.str_type_len = 0xC0 + 3;
+ strncpy(sensor.str, "WDT", 3);
+
+ // Add this sensor to the global table
+ if (g_sensor_disc.num >= SENSOR_DISC_MAX) {
+ syslog(LOG_ALERT, "populate_disc_sensors: num exceeded\n");
+ return;
+ }
+
+ memcpy(&g_sensor_disc.sensor[g_sensor_disc.num], &sensor, sizeof(sensor_disc_t));
+
+ g_sensor_disc.num++;
+
+
+ // Sensor Chassis Pwr Sts
+ // Sensor# 0x70
+ // EntitiyId# 0x15, EntityInst# 0x00
+ // Sensor Type# OEM: 0xC8
+ // Event Read/Type# Sensor Specific: 0x6F
+ sensor.owner= BMC_SLAVE_ADDR;
+ sensor.lun = 0x00;
+ sensor.sensor_num = 0x70;
+
+ sensor.ent_id = 0x15;
+ sensor.ent_inst = 0x00;
+ // Enable Scanning, Enable Events
+ sensor.sensor_init = 0x63;
+ // Supports Auto Re-Arm
+ sensor.sensor_caps = 0x40;
+ sensor.sensor_type = 0xC8;
+ sensor.evt_read_type = 0x6F;
+ // 6 bits for pwroff, pwrcycle, pwron, softdown, ac-lost, hard-reset
+ sensor.assert_evt_mask[0] = 0x3F;
+ sensor.deassert_evt_mask[0] = 0x00;
+ sensor.read_evt_mask[0] = 0x3F;
+
+ // Device ID string
+ // Type - 0xC0: ASCII, Length - 12
+ sensor.str_type_len = 0xC0 + 13;
+ strncpy(sensor.str, "CH-Pwr-Status", 13);
+
+ // Add this sensor to the global table
+ if (g_sensor_disc.num >= SENSOR_DISC_MAX) {
+ syslog(LOG_ALERT, "populate_disc_sensors: num exceeded\n");
+ return;
+ }
+
+ memcpy(&g_sensor_disc.sensor[g_sensor_disc.num], &sensor, sizeof(sensor_disc_t));
+
+ g_sensor_disc.num++;
+
+ // Sensor CPU DIMM Hot
+ // Sensor# 0xB3
+ // EntitiyId# 0xD0, EntityInst# 0x05
+ // Sensor Type# OEM 0xC6
+ // Event Read/Type# Sensor Specific 6Fh
+ sensor.owner= BMC_SLAVE_ADDR;
+ sensor.lun = 0x00;
+ sensor.sensor_num = 0xB3;
+
+ sensor.ent_id = 0xD0;
+ sensor.ent_inst = 0x05;
+ // Enable Scanning, Enable Events
+ sensor.sensor_init = 0x63;
+ // Supports Auto Re-Arm
+ sensor.sensor_caps = 0x40;
+ sensor.sensor_type = 0xC6;
+ sensor.evt_read_type = 0x6F;
+ // Two bits for CPU Hot, DIMM Hot
+ sensor.assert_evt_mask[0] = 0x05;
+ sensor.deassert_evt_mask[0] = 0x05;
+ sensor.read_evt_mask[0] = 0x05;
+
+ // Device ID string
+ // Type - 0xC0: ASCII, Length - 12
+ sensor.str_type_len = 0xC0 + 12;
+ strncpy(sensor.str, "CPU_DIMM_HOT", 12);
+
+ // Add this sensor to the global table
+ if (g_sensor_disc.num >= SENSOR_DISC_MAX) {
+ syslog(LOG_ALERT, "populate_disc_sensors: num exceeded\n");
+ return;
+ }
+
+ memcpy(&g_sensor_disc.sensor[g_sensor_disc.num], &sensor, sizeof(sensor_disc_t));
+
+ g_sensor_disc.num++;
+
+ // Sensor PMBus Status Word Low
+ // Sensor PMBus Status Word High
+ // Sensor PMBus Status MFR
+ // Sensor PMBus Status Input
+ // Sensor NTP Status
+ // Sensor# 0xED
+ // EntitiyId# 0x35, EntityInst# 0x00
+ // Sensor Type# OEM 0xC7
+ // Event Read/Type# Sensor Specific 6Fh
+ sensor.owner= BMC_SLAVE_ADDR;
+ sensor.lun = 0x00;
+ sensor.sensor_num = 0xED;
+
+ sensor.ent_id = 0x35;
+ sensor.ent_inst = 0x00;
+ // Enable Scanning, Enable Events
+ sensor.sensor_init = 0x63;
+ // Supports Auto Re-Arm
+ sensor.sensor_caps = 0x40;
+ sensor.sensor_type = 0xC7;
+ sensor.evt_read_type = 0x6F;
+ // 1-bit for date/time sync failed
+ sensor.assert_evt_mask[0] = 0x01;
+ sensor.deassert_evt_mask[0] = 0x00;
+ sensor.read_evt_mask[0] = 0x01;
+
+ // Device ID string
+ // Type - 0xC0: ASCII, Length - 10
+ sensor.str_type_len = 0xC0 + 12;
+ strncpy(sensor.str, "NTP-Status", 10);
+
+ // Add this sensor to the global table
+ if (g_sensor_disc.num >= SENSOR_DISC_MAX) {
+ syslog(LOG_ALERT, "populate_disc_sensors: num exceeded\n");
+ return;
+ }
+
+ memcpy(&g_sensor_disc.sensor[g_sensor_disc.num], &sensor, sizeof(sensor_disc_t));
+
+ g_sensor_disc.num++;
+
+ return;
+}
+
+// Access functions for Sensor Table
+void
+plat_sensor_mgmt_info(int *p_num, sensor_mgmt_t **p_sensor) {
+ *p_num = g_sensor_mgmt.num;
+ *p_sensor = g_sensor_mgmt.sensor;
+}
+
+void
+plat_sensor_disc_info(int *p_num, sensor_disc_t **p_sensor) {
+ *p_num = g_sensor_disc.num;
+ *p_sensor = g_sensor_disc.sensor;
+}
+
+void
+plat_sensor_thresh_info(int *p_num, sensor_thresh_t **p_sensor) {
+ *p_num = g_sensor_thresh.num;
+ *p_sensor = g_sensor_thresh.sensor;
+}
+
+void
+plat_sensor_oem_info(int *p_num, sensor_oem_t **p_sensor) {
+ *p_num = g_sensor_oem.num;
+ *p_sensor = g_sensor_oem.sensor;
+}
+
+// Initialize Sensor Table
+int
+plat_sensor_init(void) {
+
+ // Populate all Sensors
+ populate_mgmt_sensors();
+ populate_disc_sensors();
+
+ return 0;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/setup-ipmid.sh b/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/setup-ipmid.sh
new file mode 100644
index 0000000..b1fbbb1
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/ipmid/files/setup-ipmid.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+### BEGIN INIT INFO
+# Provides: setup-ipmid
+# Required-Start:
+# Required-Stop:
+# Default-Start: S
+# Default-Stop:
+# Short-Description: Set IPMI Message handler
+### END INIT INFO
+
+echo -n "Setup IPMI message handler... "
+/usr/local/bin/ipmid
+echo "done."
diff --git a/meta-facebook/meta-wedge/recipes-wedge/ipmid/ipmid_0.1.bb b/meta-facebook/meta-wedge/recipes-wedge/ipmid/ipmid_0.1.bb
new file mode 100644
index 0000000..3f9c4a7
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/ipmid/ipmid_0.1.bb
@@ -0,0 +1,54 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+
+SUMMARY = "IPMI Daemon"
+DESCRIPTION = "Daemon to handle IPMI Messages."
+SECTION = "base"
+PR = "r1"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://ipmid.c;beginline=8;endline=20;md5=da35978751a9d71b73679307c4d296ec"
+
+
+DEPENDS_append = "libwedge-eeprom update-rc.d-native"
+
+SRC_URI = "file://Makefile \
+ file://setup-ipmid.sh \
+ file://ipmid.c \
+ file://platform/timestamp.c \
+ file://platform/timestamp.h \
+ file://platform/sel.c \
+ file://platform/sel.h \
+ file://platform/sdr.c \
+ file://platform/sdr.h \
+ file://platform/sensor.h \
+ file://platform/fruid.h \
+ file://platform/wedge/sensor.c \
+ file://platform/wedge/fruid.c \
+ "
+
+S = "${WORKDIR}"
+
+binfiles = "ipmid"
+
+pkgdir = "ipmid"
+
+do_install() {
+ dst="${D}/usr/local/fbpackages/${pkgdir}"
+ bin="${D}/usr/local/bin"
+ install -d $dst
+ install -d $bin
+ install -m 755 ipmid ${dst}/ipmid
+ ln -snf ../fbpackages/${pkgdir}/ipmid ${bin}/ipmid
+ install -d ${D}${sysconfdir}/init.d
+ install -d ${D}${sysconfdir}/rcS.d
+ install -m 755 setup-ipmid.sh ${D}${sysconfdir}/init.d/setup-ipmid.sh
+ update-rc.d -r ${D} setup-ipmid.sh start 64 S .
+}
+
+FBPACKAGEDIR = "${prefix}/local/fbpackages"
+
+FILES_${PN} = "${FBPACKAGEDIR}/ipmid ${prefix}/local/bin ${sysconfdir} "
+
+# Inhibit complaints about .debug directories for the fand binary:
+
+INHIBIT_PACKAGE_DEBUG_SPLIT = "1"
+INHIBIT_PACKAGE_STRIP = "1"
diff --git a/meta-facebook/meta-wedge/recipes-wedge/lm_sensors/files/wedge.conf b/meta-facebook/meta-wedge/recipes-wedge/lm_sensors/files/wedge.conf
new file mode 100644
index 0000000..44b6595
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/lm_sensors/files/wedge.conf
@@ -0,0 +1,71 @@
+
+bus "i2c-3" "ast_i2c.3"
+
+bus "i2c-4" "ast_i2c.4"
+
+bus "i2c-6" "ast_i2c.6"
+
+chip "tmp75-i2c-3-48"
+ label temp1 "Inlet Temp"
+
+chip "tmp75-i2c-3-49"
+ label temp1 "Switch Temp"
+
+chip "tmp75-i2c-3-4a"
+ label temp1 "Outlet Temp"
+
+chip "tmp75-i2c-4-4c"
+ label temp1 "Microserver Ambient Temp"
+
+chip "max127-i2c-6-28"
+ label in0 "+1 Voltage"
+ label in1 "+2.5 Voltage"
+ ignore in2
+ label in3 "+1 Voltage"
+ ignore in4
+ label in5 "+3.3 Voltage"
+ label in6 "+5 Voltage"
+ ignore in7
+
+chip "ast_pwm-*"
+ label fan1 "Fan 2 front"
+ label fan2 "Fan 3 front"
+ label fan3 "Fan 1 front"
+ label fan4 "Fan 0 front"
+ label fan5 "Fan 2 rear"
+ label fan6 "Fan 3 rear"
+ label fan7 "Fan 1 rear"
+ label fan8 "Fan 0 rear"
+ ignore fan9
+ ignore fan10
+ ignore fan11
+ ignore fan12
+ ignore fan13
+ ignore fan14
+ ignore fan15
+ ignore fan16
+
+chip "fb_panther_plus-*"
+ label temp1 "CPU Temp"
+ label temp2 "DIMM0 Temp"
+ ignore temp3
+ ignore temp4
+ ignore temp5
+
+chip "ast_adc-isa-0000"
+ ignore in0
+ ignore in1
+ ignore in2
+ ignore in3
+ ignore in4
+ label in5 "+1 Core Voltage"
+ label in6 "+1 Analog Voltage"
+ label in7 "+5 Voltage"
+ label in8 "+3.3 Voltage"
+ label in9 "+2.5 Voltage"
+ ignore in10
+ ignore in11
+ ignore in12
+ ignore in13
+ ignore in14
+ ignore in15
diff --git a/meta-facebook/meta-wedge/recipes-wedge/lm_sensors/lmsensors_%.bbappend b/meta-facebook/meta-wedge/recipes-wedge/lm_sensors/lmsensors_%.bbappend
new file mode 100644
index 0000000..35433ae
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/lm_sensors/lmsensors_%.bbappend
@@ -0,0 +1,10 @@
+
+FILESEXTRAPATHS_prepend := "${THISDIR}/files:"
+
+SRC_URI += "file://wedge.conf \
+ "
+
+do_install_append() {
+ install -d ${D}${sysconfdir}/sensors.d
+ install -m 644 ../wedge.conf ${D}${sysconfdir}/sensors.d/wedge.conf
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/Makefile b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/Makefile
new file mode 100644
index 0000000..0c9f49f
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/Makefile
@@ -0,0 +1,13 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+all: oob-nic i2craw
+
+oob-nic: main.o nic.o intf.o ll_map.o libnetlink.o
+ $(CC) -o $@ $^ $(LDFLAGS) -lwedge_eeprom
+
+i2craw: i2craw.o
+ $(CC) -o $@ $^ $(LDFLAGS)
+
+.PHONY: clean
+
+clean:
+ rm -rf *.o oob-nic i2craw
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/README b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/README
new file mode 100644
index 0000000..f46971f
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/README
@@ -0,0 +1,10 @@
+
+TODO:
+
+1. Currently, we poll the SMbus instead of rely on the ALERT. The kernel does not handle the ALERT either other than just a print out.
+
+2. Maximum fragment is 32
+
+3. We use libnetlink for bring interface up and setting MAC. That increases the binary by about 50k and also we copied about 5 files from iproute2 here for that. We might be able to get away this by using some non-iproute2 API
+
+4. The dependency in the Makefile does not consider .h
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/etc/oob-nic.sh b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/etc/oob-nic.sh
new file mode 100644
index 0000000..35e1a2a
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/etc/oob-nic.sh
@@ -0,0 +1,103 @@
+#! /bin/sh
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+### BEGIN INIT INFO
+# Provides: oob-nic
+# Required-Start:
+# Required-Stop:
+# Default-Start: S
+# Default-Stop: 0 6
+# Short-Description: One of the first scripts to be executed. Starts or stops
+# the OOB NIC.
+#
+### END INIT INFO
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin
+DAEMON=/usr/sbin/oob-nic
+NAME=oob-nic
+DESC="OOB NIC Driver"
+
+# source function library
+. /etc/init.d/functions
+
+test -f $DAEMON || exit 0
+
+# enable the isolation buffer
+. /usr/local/fbpackages/utils/ast-functions
+wedge_iso_buf_enable
+
+fix_etc_interfaces() {
+ local intf_conf rev
+ intf_conf="/etc/network/interfaces"
+ rev=$(wedge_board_rev)
+ if [ $rev -lt 3 ]; then
+ if ! grep oob $intf_conf > /dev/null 2>&1; then
+ echo >> $intf_conf
+ echo "auto oob" >> $intf_conf
+ echo "iface oob inet dhcp" >> $intf_conf
+ fi
+ fi
+}
+
+STOPPER=
+ACTION="$1"
+
+case "$ACTION" in
+ start)
+ echo -n "Starting $DESC: "
+ if [ ! -d /dev/net ]
+ then
+ mkdir /dev/net
+ fi
+ if [ ! -f /dev/net/tun ]
+ then
+ mknod /dev/net/tun c 10 200
+ chmod 666 /dev/net/tun
+ fi
+ fix_etc_interfaces
+ $DAEMON > /dev/null 2>&1 &
+ echo "$NAME."
+ ;;
+ stop)
+ echo -n "Stopping $DESC: "
+ start-stop-daemon --stop --quiet --exec $DAEMON
+ echo "$NAME."
+ ;;
+ restart|force-reload)
+ echo -n "Restarting $DESC: "
+ start-stop-daemon --stop --quiet --exec $DAEMON
+ sleep 1
+ fix_etc_interfaces
+ $DAEMON > /dev/null 2>&1 &
+ echo "$NAME."
+ ;;
+ status)
+ status $DAEMON
+ exit $?
+ ;;
+ *)
+ N=${0##*/}
+ N=${N#[SK]??}
+ echo "Usage: $N {start|stop|status|restart|force-reload}" >&2
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/hlist.h b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/hlist.h
new file mode 100644
index 0000000..5e89765
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/hlist.h
@@ -0,0 +1,73 @@
+/*
+ * Note: Original file from iproute2 package
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef __HLIST_H__
+#define __HLIST_H__ 1
+/* Hash list stuff from kernel */
+
+#include <stddef.h>
+
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+struct hlist_head {
+ struct hlist_node *first;
+};
+
+struct hlist_node {
+ struct hlist_node *next, **pprev;
+};
+
+static inline void hlist_del(struct hlist_node *n)
+{
+ struct hlist_node *next = n->next;
+ struct hlist_node **pprev = n->pprev;
+ *pprev = next;
+ if (next)
+ next->pprev = pprev;
+}
+
+static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
+{
+ struct hlist_node *first = h->first;
+ n->next = first;
+ if (first)
+ first->pprev = &n->next;
+ h->first = n;
+ n->pprev = &h->first;
+}
+
+#define hlist_for_each(pos, head) \
+ for (pos = (head)->first; pos ; pos = pos->next)
+
+
+#define hlist_for_each_safe(pos, n, head) \
+ for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
+ pos = n)
+
+#define hlist_entry_safe(ptr, type, member) \
+ ({ typeof(ptr) ____ptr = (ptr); \
+ ____ptr ? hlist_entry(____ptr, type, member) : NULL; \
+ })
+
+#define hlist_for_each_entry(pos, head, member) \
+ for (pos = hlist_entry_safe((head)->first, typeof(*(pos)), member);\
+ pos; \
+ pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
+
+#endif /* __HLIST_H__ */
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/i2craw.c b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/i2craw.c
new file mode 100644
index 0000000..f9d499b
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/i2craw.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2004-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdint.h>
+
+#include "facebook/i2c-dev.h"
+#include "facebook/log.h"
+
+void usage(const char *prog) {
+ printf("Usage: %s [options] <bus number> <slave address>\n", prog);
+ printf("\n Options:\n"
+ "\n\t-w 'bytes to write':\n"
+ "\t\t i2c write\n"
+ "\n\t-r <number of bytes to read>:\n"
+ "\t\t if 0 is provided, the first byte of read is used to determine\n"
+ "\t\t how many bytes more to read\n"
+ "\n\t-p:\n"
+ "\t\t Use PEC\n"
+ "\n\t-h:\n"
+ "\t\t Print this help\n"
+ "\n Note: if both '-w' and '-r' are specified, write will be"
+ "\n performed first, followed by read\n");
+}
+
+#define MAX_BYTES 255
+
+int g_use_pec = 0;
+int g_has_write = 0;
+int g_n_write = 0;
+uint8_t g_write_bytes[MAX_BYTES];
+int g_has_read = 0;
+int g_n_read = -1;
+uint8_t g_read_bytes[MAX_BYTES];
+uint8_t g_bus = -1;
+uint8_t g_slave_addr = 0xff;
+
+static int parse_byte_string(const char *str) {
+ const char *startptr = str;
+ char *endptr;
+ int total = 0;
+ unsigned long val;
+
+ do {
+ val = strtoul(startptr, &endptr, 0);
+ if (startptr == endptr) {
+ printf("'%s' is invalid\n", str);
+ return -1;
+ }
+ if (val > MAX_BYTES) {
+ printf("'%s' is invalid\n", str);
+ return -1;
+ }
+ g_write_bytes[total++] = val;
+ if (*endptr == '\0') {
+ break;
+ }
+ if (total >= MAX_BYTES) {
+ printf("'%s' is invalid\n", str);
+ return -1;
+ }
+ startptr = endptr;
+ } while(1);
+
+ return total;
+}
+
+static int i2c_open() {
+ int fd;
+ char fn[32];
+ int rc;
+
+ snprintf(fn, sizeof(fn), "/dev/i2c-%d", g_bus);
+ fd = open(fn, O_RDWR);
+ if (fd == -1) {
+ LOG_ERR(errno, "Failed to open i2c device %s", fn);
+ return -1;
+ }
+
+ rc = ioctl(fd, I2C_SLAVE, g_slave_addr);
+ if (rc < 0) {
+ LOG_ERR(errno, "Failed to open slave @ address 0x%x", g_slave_addr);
+ close(fd);
+ }
+
+ return fd;
+}
+
+static int i2c_io(int fd) {
+ struct i2c_rdwr_ioctl_data data;
+ struct i2c_msg msg[2];
+ int n_msg = 0;
+ int rc;
+
+ memset(&msg, 0, sizeof(msg));
+
+ if (g_has_write) {
+ msg[n_msg].addr = g_slave_addr;
+ msg[n_msg].flags = (g_use_pec) ? I2C_CLIENT_PEC : 0;
+ msg[n_msg].len = g_n_write;
+ msg[n_msg].buf = g_write_bytes;
+ n_msg++;
+ }
+
+ if (g_has_read) {
+ msg[n_msg].addr = g_slave_addr;
+ msg[n_msg].flags = I2C_M_RD
+ | ((g_use_pec) ? I2C_CLIENT_PEC : 0)
+ | ((g_n_read == 0) ? I2C_M_RECV_LEN : 0);
+ /*
+ * In case of g_n_read is 0, block length will be added by
+ * the underlying bus driver.
+ */
+ msg[n_msg].len = (g_n_read) ? g_n_read : 256;
+ msg[n_msg].buf = g_read_bytes;
+ if (g_n_read == 0) {
+ /* If we're using variable length block reads, we have to set the
+ * first byte of the buffer to at least one or the kernel complains.
+ */
+ g_read_bytes[0] = 1;
+ }
+ n_msg++;
+ }
+
+ data.msgs = msg;
+ data.nmsgs = n_msg;
+
+ rc = ioctl(fd, I2C_RDWR, &data);
+ if (rc < 0) {
+ LOG_ERR(errno, "Failed to do raw io");
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char * const argv[]) {
+ int i;
+ int fd;
+ int opt;
+ while ((opt = getopt(argc, argv, "hpw:r:")) != -1) {
+ switch (opt) {
+ case 'h':
+ usage(argv[0]);
+ return 0;
+ case 'p':
+ g_use_pec = 1;
+ break;
+ case 'w':
+ g_has_write = 1;
+ if ((g_n_write = parse_byte_string(optarg)) <= 0) {
+ usage(argv[0]);
+ return -1;
+ }
+ break;
+ case 'r':
+ g_has_read = 1;
+ g_n_read = atoi(optarg);
+ break;
+ default:
+ usage(argv[0]);
+ return -1;
+ }
+ }
+
+ /* make sure we still have arguments for bus and slave address */
+ if (optind + 2 != argc) {
+ printf("Bus or slave address is missing\n");
+ usage(argv[0]);
+ return -1;
+ }
+
+ g_bus = atoi(argv[optind]);
+ g_slave_addr = strtoul(argv[optind + 1], NULL, 0);
+ if ((g_slave_addr & 0x80)) {
+ printf("Slave address must be 7-bit\n");
+ return -1;
+ }
+
+ if (!g_has_write && !g_has_read) {
+ /* by default, read, first byte read is the length */
+ g_has_read = 1;
+ g_n_read = 0;
+ }
+
+ printf("Bus: %d\nDevice address: 0x%x\n", g_bus, g_slave_addr);
+ if (g_has_write) {
+ printf("To write %d bytes:", g_n_write);
+ for (i = 0; i < g_n_write; i++) {
+ printf(" 0x%x", g_write_bytes[i]);
+ }
+ printf("\n");
+ }
+ if (g_has_read) {
+ if (g_n_read) {
+ printf("To read %d bytes.\n", g_n_read);
+ } else {
+ printf("To read data.\n");
+ }
+ }
+
+ fd = i2c_open();
+ if (fd < 0) {
+ return -1;
+ }
+
+ if (i2c_io(fd) < 0) {
+ return -1;
+ }
+
+ if (g_has_read) {
+ printf("Received:\n ");
+ if (g_n_read == 0) {
+ g_n_read = g_read_bytes[0] + 1;
+ }
+ for (i = 0; i < g_n_read; i++) {
+ printf(" 0x%x", g_read_bytes[i]);
+ }
+ printf("\n");
+ }
+
+ return 0;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/intf.c b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/intf.c
new file mode 100644
index 0000000..5bf8480
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/intf.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "intf.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <linux/rtnetlink.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <linux/fib_rules.h>
+
+#include "facebook/log.h"
+#include "libnetlink.h"
+#include "ll_map.h"
+
+struct oob_intf_t {
+ char oi_name[32];
+ int oi_fd;
+ int oi_ifidx;
+ uint8_t oi_mac[6];
+ struct rtnl_handle oi_rth;
+};
+
+#define TUN_DEVICE "/dev/net/tun"
+
+static int oob_intf_set_mac(oob_intf *intf, const uint8_t mac[6]) {
+ int rc;
+ struct {
+ struct nlmsghdr n;
+ struct ifinfomsg ifi;
+ char buf[256];
+ } req;
+
+ memset(&req, 0, sizeof(req));
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+ req.n.nlmsg_type = RTM_NEWLINK;
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.ifi.ifi_family = AF_UNSPEC;
+ req.ifi.ifi_index = intf->oi_ifidx;
+ memcpy(intf->oi_mac, mac, sizeof(intf->oi_mac));
+ addattr_l(&req.n, sizeof(req), IFLA_ADDRESS,
+ intf->oi_mac, sizeof(intf->oi_mac));
+ rc = rtnl_talk(&intf->oi_rth, &req.n, 0, 0, NULL);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to set mac to interface %s @ index %d",
+ intf->oi_name, intf->oi_ifidx);
+ return -rc;
+ }
+
+ LOG_INFO("Set interface %s @ index %d mac to %x:%x:%x:%x:%x:%x",
+ intf->oi_name, intf->oi_ifidx,
+ intf->oi_mac[0], intf->oi_mac[1], intf->oi_mac[2],
+ intf->oi_mac[3], intf->oi_mac[4], intf->oi_mac[5]);
+
+ return 0;
+}
+
+static int oob_intf_bring_up(oob_intf *intf) {
+ int rc;
+ struct {
+ struct nlmsghdr n;
+ struct ifinfomsg ifi;
+ char buf[256];
+ } req;
+
+ memset(&req, 0, sizeof(req));
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+ req.n.nlmsg_type = RTM_NEWLINK;
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.ifi.ifi_family = AF_UNSPEC;
+ req.ifi.ifi_change |= IFF_UP;
+ req.ifi.ifi_flags |= IFF_UP;
+ req.ifi.ifi_index = intf->oi_ifidx;
+ rc = rtnl_talk(&intf->oi_rth, &req.n, 0, 0, NULL);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to bring up interface %s @ index %d",
+ intf->oi_name, intf->oi_ifidx);
+ return -rc;
+ }
+
+ LOG_INFO("Brought up interface %s @ index %d", intf->oi_name, intf->oi_ifidx);
+
+ return 0;
+}
+
+oob_intf* oob_intf_create(const char *name, const uint8_t mac[6]) {
+
+ int rc;
+ int flags;
+ struct ifreq ifr;
+ oob_intf *intf = NULL;
+
+#define _CHECK_RC(fmt, ...) do { \
+ if (rc < 0) { \
+ rc = errno; \
+ LOG_ERR(rc, fmt, ##__VA_ARGS__); \
+ goto err_out; \
+ } \
+} while(0)
+
+ intf = malloc(sizeof(*intf));
+ if (!intf) {
+ rc = ENOMEM;
+ LOG_ERR(rc, "Failed to allocate memory for interface");
+ goto err_out;
+ }
+ memset(intf, 0, sizeof(*intf));
+ strncpy(intf->oi_name, name, sizeof(intf->oi_name));
+ intf->oi_name[sizeof(intf->oi_name) - 1] = '\0';
+ intf->oi_fd = -1;
+
+ rc = rtnl_open(&intf->oi_rth, 0);
+ _CHECK_RC("Failed to open rth_handler");
+
+ rc = open(TUN_DEVICE, O_RDWR);
+ _CHECK_RC("Failed to open %s", TUN_DEVICE);
+ intf->oi_fd = rc;
+
+ memset(&ifr, 0, sizeof(ifr));
+ /*
+ * IFF_TAP: TAP interface
+ * IFF_NO_PI: Do not provide pracket information
+ */
+ ifr.ifr_flags = IFF_TAP|IFF_NO_PI;
+ strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+ ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
+
+ rc = ioctl(intf->oi_fd, TUNSETIFF, (void *) &ifr);
+ _CHECK_RC("Failed to create tap interface %s", ifr.ifr_name);
+
+ /* make fd non-blocking */
+ rc = fcntl(intf->oi_fd, F_GETFL);
+ _CHECK_RC("Failed to get flags from fd ", intf->oi_fd);
+ flags = rc | O_NONBLOCK;
+ rc = fcntl(intf->oi_fd, F_SETFL, rc);
+ _CHECK_RC("Failed to set non-blocking flags ", flags,
+ " to fd ", intf->oi_fd);
+
+ /* set CLOEXEC */
+ rc = fcntl(intf->oi_fd, F_GETFD);
+ _CHECK_RC("Failed to get flags from fd ", intf->oi_fd);
+ flags = rc | FD_CLOEXEC;
+ rc = fcntl(intf->oi_fd, F_SETFD, flags);
+ _CHECK_RC("Failed to set close-on-exec flags ", flags,
+ " to fd ", intf->oi_fd);
+
+ // TODO: if needed, we can adjust send buffer size, TUNSETSNDBUF
+ intf->oi_ifidx = ll_name_to_index(intf->oi_name);
+
+ /* now set the mac address */
+ oob_intf_set_mac(intf, mac);
+
+#if 0
+ /* make it persistent */
+ rc = ioctl(intf->oi_fd, TUNSETPERSIST, 0);
+ _CHECK_RC("Failed to make the tap interface %s persistent", intf->oi_name);
+#endif
+
+ LOG_INFO("Create/attach to tap interface %s @ fd %d, index %d",
+ intf->oi_name, intf->oi_fd, intf->oi_ifidx);
+
+ //oob_intf_bring_up(intf);
+
+ return intf;
+
+ err_out:
+ if (intf) {
+ rtnl_close(&intf->oi_rth);
+ if (intf->oi_fd != -1) {
+ close(intf->oi_fd);
+ }
+ free(intf);
+ }
+
+ return NULL;
+}
+
+int oob_intf_get_fd(const oob_intf *intf) {
+ return intf->oi_fd;
+}
+
+int oob_intf_receive(const oob_intf *intf, char *buf, int len) {
+ int rc;
+ do {
+ rc = read(intf->oi_fd, buf, len);
+ } while (rc == -1 && errno == EINTR);
+ if (rc < 0) {
+ rc = errno;
+ if (rc != EAGAIN) {
+ LOG_ERR(rc, "Failed to read on interface fd %d", intf->oi_fd);
+ return -rc;
+ } else {
+ /* nothing is available */
+ return 0;
+ }
+ } else if (rc == 0) {
+ // Nothing to read. It shall not happen as the fd is non-blocking.
+ // Just add this case to be safe.
+ return 0;
+ } else if (rc > len) {
+ // The pkt is larger than the buffer. We don't have complete packet.
+ // It shall not happen unless the MTU is mis-match. Drop the packet.
+ LOG_ERR(ENOSPC, "Received a too large packet (%d bytes > %d) from the "
+ "tap interface. Drop it...", rc, len);
+ return -ENOSPC;
+ } else {
+ LOG_VER("Recv a packet of %d bytes from %s", rc, intf->oi_name);
+ return rc;
+ }
+}
+
+int oob_intf_send(const oob_intf *intf, const char *buf, int len) {
+ int rc;
+ do {
+ rc = write(intf->oi_fd, buf, len);
+ } while (rc == -1 && errno == EINTR);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to send on interface fd %d", intf->oi_fd);
+ return -rc;
+ } else if (rc < len) {
+ LOG_ERR(EIO, "Failed to send the full packet (%d bytes > %d) for fd %d",
+ len, rc, intf->oi_fd);
+ return -EIO;
+ } else {
+ LOG_VER("Sent a packet of %d bytes to %s", rc, intf->oi_name);
+ return rc;
+ }
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/intf.h b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/intf.h
new file mode 100644
index 0000000..6ea7af1
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/intf.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef INTF_H
+#define INTF_H
+
+#include <stdint.h>
+
+typedef struct oob_intf_t oob_intf;
+
+oob_intf* oob_intf_create(const char *name, const uint8_t mac[6]);
+int oob_intf_get_fd(const oob_intf *intf);
+
+int oob_intf_receive(const oob_intf *intf, char *buf, int len);
+int oob_intf_send(const oob_intf *intf, const char *buf, int len);
+
+#endif
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/libnetlink.c b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/libnetlink.c
new file mode 100644
index 0000000..019e2c8
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/libnetlink.c
@@ -0,0 +1,717 @@
+/*
+ * Note: Original file from iproute2 package
+ *
+ * libnetlink.c RTnetlink service routines.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <net/if_arp.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/uio.h>
+
+#include "libnetlink.h"
+
+int rcvbuf = 1024 * 1024;
+
+void rtnl_close(struct rtnl_handle *rth)
+{
+ if (rth->fd >= 0) {
+ close(rth->fd);
+ rth->fd = -1;
+ }
+}
+
+int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions,
+ int protocol)
+{
+ socklen_t addr_len;
+ int sndbuf = 32768;
+
+ memset(rth, 0, sizeof(*rth));
+
+ rth->fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol);
+ if (rth->fd < 0) {
+ perror("Cannot open netlink socket");
+ return -1;
+ }
+
+ if (setsockopt(rth->fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf)) < 0) {
+ perror("SO_SNDBUF");
+ return -1;
+ }
+
+ if (setsockopt(rth->fd,SOL_SOCKET,SO_RCVBUF,&rcvbuf,sizeof(rcvbuf)) < 0) {
+ perror("SO_RCVBUF");
+ return -1;
+ }
+
+ memset(&rth->local, 0, sizeof(rth->local));
+ rth->local.nl_family = AF_NETLINK;
+ rth->local.nl_groups = subscriptions;
+
+ if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
+ perror("Cannot bind netlink socket");
+ return -1;
+ }
+ addr_len = sizeof(rth->local);
+ if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) {
+ perror("Cannot getsockname");
+ return -1;
+ }
+ if (addr_len != sizeof(rth->local)) {
+ fprintf(stderr, "Wrong address length %d\n", addr_len);
+ return -1;
+ }
+ if (rth->local.nl_family != AF_NETLINK) {
+ fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family);
+ return -1;
+ }
+ rth->seq = time(NULL);
+ return 0;
+}
+
+int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
+{
+ return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE);
+}
+
+int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
+{
+ return rtnl_wilddump_req_filter(rth, family, type, RTEXT_FILTER_VF);
+}
+
+int rtnl_wilddump_req_filter(struct rtnl_handle *rth, int family, int type,
+ __u32 filt_mask)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct ifinfomsg ifm;
+ /* attribute has to be NLMSG aligned */
+ struct rtattr ext_req __attribute__ ((aligned(NLMSG_ALIGNTO)));
+ __u32 ext_filter_mask;
+ } req;
+
+ memset(&req, 0, sizeof(req));
+ req.nlh.nlmsg_len = sizeof(req);
+ req.nlh.nlmsg_type = type;
+ req.nlh.nlmsg_flags = NLM_F_DUMP|NLM_F_REQUEST;
+ req.nlh.nlmsg_pid = 0;
+ req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
+ req.ifm.ifi_family = family;
+
+ req.ext_req.rta_type = IFLA_EXT_MASK;
+ req.ext_req.rta_len = RTA_LENGTH(sizeof(__u32));
+ req.ext_filter_mask = filt_mask;
+
+ return send(rth->fd, (void*)&req, sizeof(req), 0);
+}
+
+int rtnl_send(struct rtnl_handle *rth, const void *buf, int len)
+{
+ return send(rth->fd, buf, len, 0);
+}
+
+int rtnl_send_check(struct rtnl_handle *rth, const void *buf, int len)
+{
+ struct nlmsghdr *h;
+ int status;
+ char resp[1024];
+
+ status = send(rth->fd, buf, len, 0);
+ if (status < 0)
+ return status;
+
+ /* Check for immediate errors */
+ status = recv(rth->fd, resp, sizeof(resp), MSG_DONTWAIT|MSG_PEEK);
+ if (status < 0) {
+ if (errno == EAGAIN)
+ return 0;
+ return -1;
+ }
+
+ for (h = (struct nlmsghdr *)resp; NLMSG_OK(h, status);
+ h = NLMSG_NEXT(h, status)) {
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+ if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
+ fprintf(stderr, "ERROR truncated\n");
+ else
+ errno = -err->error;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
+{
+ struct nlmsghdr nlh;
+ struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK };
+ struct iovec iov[2] = {
+ { .iov_base = &nlh, .iov_len = sizeof(nlh) },
+ { .iov_base = req, .iov_len = len }
+ };
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = iov,
+ .msg_iovlen = 2,
+ };
+
+ nlh.nlmsg_len = NLMSG_LENGTH(len);
+ nlh.nlmsg_type = type;
+ nlh.nlmsg_flags = NLM_F_DUMP|NLM_F_REQUEST;
+ nlh.nlmsg_pid = 0;
+ nlh.nlmsg_seq = rth->dump = ++rth->seq;
+
+ return sendmsg(rth->fd, &msg, 0);
+}
+
+int rtnl_dump_filter_l(struct rtnl_handle *rth,
+ const struct rtnl_dump_filter_arg *arg)
+{
+ struct sockaddr_nl nladdr;
+ struct iovec iov;
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ char buf[16384];
+ int dump_intr = 0;
+
+ iov.iov_base = buf;
+ while (1) {
+ int status;
+ const struct rtnl_dump_filter_arg *a;
+ int found_done = 0;
+ int msglen = 0;
+
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rth->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ fprintf(stderr, "netlink receive error %s (%d)\n",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ if (status == 0) {
+ fprintf(stderr, "EOF on netlink\n");
+ return -1;
+ }
+
+ for (a = arg; a->filter; a++) {
+ struct nlmsghdr *h = (struct nlmsghdr*)buf;
+ msglen = status;
+
+ while (NLMSG_OK(h, msglen)) {
+ int err;
+
+ if (nladdr.nl_pid != 0 ||
+ h->nlmsg_pid != rth->local.nl_pid ||
+ h->nlmsg_seq != rth->dump)
+ goto skip_it;
+
+ if (h->nlmsg_flags & NLM_F_DUMP_INTR)
+ dump_intr = 1;
+
+ if (h->nlmsg_type == NLMSG_DONE) {
+ found_done = 1;
+ break; /* process next filter */
+ }
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+ if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
+ fprintf(stderr,
+ "ERROR truncated\n");
+ } else {
+ errno = -err->error;
+ perror("RTNETLINK answers");
+ }
+ return -1;
+ }
+ err = a->filter(&nladdr, h, a->arg1);
+ if (err < 0)
+ return err;
+
+skip_it:
+ h = NLMSG_NEXT(h, msglen);
+ }
+ }
+
+ if (found_done) {
+ if (dump_intr)
+ fprintf(stderr,
+ "Dump was interrupted and may be inconsistent.\n");
+ return 0;
+ }
+
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Message truncated\n");
+ continue;
+ }
+ if (msglen) {
+ fprintf(stderr, "!!!Remnant of size %d\n", msglen);
+ exit(1);
+ }
+ }
+}
+
+int rtnl_dump_filter(struct rtnl_handle *rth,
+ rtnl_filter_t filter,
+ void *arg1)
+{
+ const struct rtnl_dump_filter_arg a[2] = {
+ { .filter = filter, .arg1 = arg1, },
+ { .filter = NULL, .arg1 = NULL, },
+ };
+
+ return rtnl_dump_filter_l(rth, a);
+}
+
+int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+ unsigned groups, struct nlmsghdr *answer)
+{
+ int status;
+ unsigned seq;
+ struct nlmsghdr *h;
+ struct sockaddr_nl nladdr;
+ struct iovec iov = {
+ .iov_base = (void*) n,
+ .iov_len = n->nlmsg_len
+ };
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ char buf[16384];
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = peer;
+ nladdr.nl_groups = groups;
+
+ n->nlmsg_seq = seq = ++rtnl->seq;
+
+ if (answer == NULL)
+ n->nlmsg_flags |= NLM_F_ACK;
+
+ status = sendmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ perror("Cannot talk to rtnetlink");
+ return -1;
+ }
+
+ memset(buf,0,sizeof(buf));
+
+ iov.iov_base = buf;
+
+ while (1) {
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ fprintf(stderr, "netlink receive error %s (%d)\n",
+ strerror(errno), errno);
+ return -1;
+ }
+ if (status == 0) {
+ fprintf(stderr, "EOF on netlink\n");
+ return -1;
+ }
+ if (msg.msg_namelen != sizeof(nladdr)) {
+ fprintf(stderr, "sender address length == %d\n", msg.msg_namelen);
+ exit(1);
+ }
+ for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
+ int len = h->nlmsg_len;
+ int l = len - sizeof(*h);
+
+ if (l < 0 || len>status) {
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Truncated message\n");
+ return -1;
+ }
+ fprintf(stderr, "!!!malformed message: len=%d\n", len);
+ exit(1);
+ }
+
+ if (nladdr.nl_pid != peer ||
+ h->nlmsg_pid != rtnl->local.nl_pid ||
+ h->nlmsg_seq != seq) {
+ /* Don't forget to skip that message. */
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+ continue;
+ }
+
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+ if (l < sizeof(struct nlmsgerr)) {
+ fprintf(stderr, "ERROR truncated\n");
+ } else {
+ if (!err->error) {
+ if (answer)
+ memcpy(answer, h, h->nlmsg_len);
+ return 0;
+ }
+
+ fprintf(stderr, "RTNETLINK answers: %s\n", strerror(-err->error));
+ errno = -err->error;
+ }
+ return -1;
+ }
+ if (answer) {
+ memcpy(answer, h, h->nlmsg_len);
+ return 0;
+ }
+
+ fprintf(stderr, "Unexpected reply!!!\n");
+
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Message truncated\n");
+ continue;
+ }
+ if (status) {
+ fprintf(stderr, "!!!Remnant of size %d\n", status);
+ exit(1);
+ }
+ }
+}
+
+int rtnl_listen(struct rtnl_handle *rtnl,
+ rtnl_filter_t handler,
+ void *jarg)
+{
+ int status;
+ struct nlmsghdr *h;
+ struct sockaddr_nl nladdr;
+ struct iovec iov;
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ char buf[8192];
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = 0;
+ nladdr.nl_groups = 0;
+
+ iov.iov_base = buf;
+ while (1) {
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ fprintf(stderr, "netlink receive error %s (%d)\n",
+ strerror(errno), errno);
+ if (errno == ENOBUFS)
+ continue;
+ return -1;
+ }
+ if (status == 0) {
+ fprintf(stderr, "EOF on netlink\n");
+ return -1;
+ }
+ if (msg.msg_namelen != sizeof(nladdr)) {
+ fprintf(stderr, "Sender address length == %d\n", msg.msg_namelen);
+ exit(1);
+ }
+ for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
+ int err;
+ int len = h->nlmsg_len;
+ int l = len - sizeof(*h);
+
+ if (l<0 || len>status) {
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Truncated message\n");
+ return -1;
+ }
+ fprintf(stderr, "!!!malformed message: len=%d\n", len);
+ exit(1);
+ }
+
+ err = handler(&nladdr, h, jarg);
+ if (err < 0)
+ return err;
+
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Message truncated\n");
+ continue;
+ }
+ if (status) {
+ fprintf(stderr, "!!!Remnant of size %d\n", status);
+ exit(1);
+ }
+ }
+}
+
+int rtnl_from_file(FILE *rtnl, rtnl_filter_t handler,
+ void *jarg)
+{
+ int status;
+ struct sockaddr_nl nladdr;
+ char buf[8192];
+ struct nlmsghdr *h = (void*)buf;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = 0;
+ nladdr.nl_groups = 0;
+
+ while (1) {
+ int err, len;
+ int l;
+
+ status = fread(&buf, 1, sizeof(*h), rtnl);
+
+ if (status < 0) {
+ if (errno == EINTR)
+ continue;
+ perror("rtnl_from_file: fread");
+ return -1;
+ }
+ if (status == 0)
+ return 0;
+
+ len = h->nlmsg_len;
+ l = len - sizeof(*h);
+
+ if (l<0 || len>sizeof(buf)) {
+ fprintf(stderr, "!!!malformed message: len=%d @%lu\n",
+ len, ftell(rtnl));
+ return -1;
+ }
+
+ status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl);
+
+ if (status < 0) {
+ perror("rtnl_from_file: fread");
+ return -1;
+ }
+ if (status < l) {
+ fprintf(stderr, "rtnl-from_file: truncated message\n");
+ return -1;
+ }
+
+ err = handler(&nladdr, h, jarg);
+ if (err < 0)
+ return err;
+ }
+}
+
+int addattr(struct nlmsghdr *n, int maxlen, int type)
+{
+ return addattr_l(n, maxlen, type, NULL, 0);
+}
+
+int addattr8(struct nlmsghdr *n, int maxlen, int type, __u8 data)
+{
+ return addattr_l(n, maxlen, type, &data, sizeof(__u8));
+}
+
+int addattr16(struct nlmsghdr *n, int maxlen, int type, __u16 data)
+{
+ return addattr_l(n, maxlen, type, &data, sizeof(__u16));
+}
+
+int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data)
+{
+ return addattr_l(n, maxlen, type, &data, sizeof(__u32));
+}
+
+int addattr64(struct nlmsghdr *n, int maxlen, int type, __u64 data)
+{
+ return addattr_l(n, maxlen, type, &data, sizeof(__u64));
+}
+
+int addattrstrz(struct nlmsghdr *n, int maxlen, int type, const char *str)
+{
+ return addattr_l(n, maxlen, type, str, strlen(str)+1);
+}
+
+int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data,
+ int alen)
+{
+ int len = RTA_LENGTH(alen);
+ struct rtattr *rta;
+
+ if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) {
+ fprintf(stderr, "addattr_l ERROR: message exceeded bound of %d\n",maxlen);
+ return -1;
+ }
+ rta = NLMSG_TAIL(n);
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy(RTA_DATA(rta), data, alen);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
+ return 0;
+}
+
+int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len)
+{
+ if (NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len) > maxlen) {
+ fprintf(stderr, "addraw_l ERROR: message exceeded bound of %d\n",maxlen);
+ return -1;
+ }
+
+ memcpy(NLMSG_TAIL(n), data, len);
+ memset((void *) NLMSG_TAIL(n) + len, 0, NLMSG_ALIGN(len) - len);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len);
+ return 0;
+}
+
+struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type)
+{
+ struct rtattr *nest = NLMSG_TAIL(n);
+
+ addattr_l(n, maxlen, type, NULL, 0);
+ return nest;
+}
+
+int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest)
+{
+ nest->rta_len = (void *)NLMSG_TAIL(n) - (void *)nest;
+ return n->nlmsg_len;
+}
+
+struct rtattr *addattr_nest_compat(struct nlmsghdr *n, int maxlen, int type,
+ const void *data, int len)
+{
+ struct rtattr *start = NLMSG_TAIL(n);
+
+ addattr_l(n, maxlen, type, data, len);
+ addattr_nest(n, maxlen, type);
+ return start;
+}
+
+int addattr_nest_compat_end(struct nlmsghdr *n, struct rtattr *start)
+{
+ struct rtattr *nest = (void *)start + NLMSG_ALIGN(start->rta_len);
+
+ start->rta_len = (void *)NLMSG_TAIL(n) - (void *)start;
+ addattr_nest_end(n, nest);
+ return n->nlmsg_len;
+}
+
+int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data)
+{
+ int len = RTA_LENGTH(4);
+ struct rtattr *subrta;
+
+ if (RTA_ALIGN(rta->rta_len) + len > maxlen) {
+ fprintf(stderr,"rta_addattr32: Error! max allowed bound %d exceeded\n",maxlen);
+ return -1;
+ }
+ subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+ subrta->rta_type = type;
+ subrta->rta_len = len;
+ memcpy(RTA_DATA(subrta), &data, 4);
+ rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
+ return 0;
+}
+
+int rta_addattr_l(struct rtattr *rta, int maxlen, int type,
+ const void *data, int alen)
+{
+ struct rtattr *subrta;
+ int len = RTA_LENGTH(alen);
+
+ if (RTA_ALIGN(rta->rta_len) + RTA_ALIGN(len) > maxlen) {
+ fprintf(stderr,"rta_addattr_l: Error! max allowed bound %d exceeded\n",maxlen);
+ return -1;
+ }
+ subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+ subrta->rta_type = type;
+ subrta->rta_len = len;
+ memcpy(RTA_DATA(subrta), data, alen);
+ rta->rta_len = NLMSG_ALIGN(rta->rta_len) + RTA_ALIGN(len);
+ return 0;
+}
+
+int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+ return parse_rtattr_flags(tb, max, rta, len, 0);
+}
+
+int parse_rtattr_flags(struct rtattr *tb[], int max, struct rtattr *rta,
+ int len, unsigned short flags)
+{
+ unsigned short type;
+
+ memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
+ while (RTA_OK(rta, len)) {
+ type = rta->rta_type & ~flags;
+ if ((type <= max) && (!tb[type]))
+ tb[type] = rta;
+ rta = RTA_NEXT(rta,len);
+ }
+ if (len)
+ fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
+ return 0;
+}
+
+int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+ int i = 0;
+
+ memset(tb, 0, sizeof(struct rtattr *) * max);
+ while (RTA_OK(rta, len)) {
+ if (rta->rta_type <= max && i < max)
+ tb[i++] = rta;
+ rta = RTA_NEXT(rta,len);
+ }
+ if (len)
+ fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
+ return i;
+}
+
+int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta,
+ int len)
+{
+ if (RTA_PAYLOAD(rta) < len)
+ return -1;
+ if (RTA_PAYLOAD(rta) >= RTA_ALIGN(len) + sizeof(struct rtattr)) {
+ rta = RTA_DATA(rta) + RTA_ALIGN(len);
+ return parse_rtattr_nested(tb, max, rta);
+ }
+ memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
+ return 0;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/libnetlink.h b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/libnetlink.h
new file mode 100644
index 0000000..9e72692
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/libnetlink.h
@@ -0,0 +1,161 @@
+/*
+ * Note: Original file from iproute2 package
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef __LIBNETLINK_H__
+#define __LIBNETLINK_H__ 1
+
+#include <stdio.h>
+#include <string.h>
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_link.h>
+#include <linux/if_addr.h>
+#include <linux/neighbour.h>
+#include <linux/netconf.h>
+
+struct rtnl_handle
+{
+ int fd;
+ struct sockaddr_nl local;
+ struct sockaddr_nl peer;
+ __u32 seq;
+ __u32 dump;
+};
+
+extern int rcvbuf;
+
+extern int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions);
+extern int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, int protocol);
+extern void rtnl_close(struct rtnl_handle *rth);
+extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type);
+extern int rtnl_wilddump_req_filter(struct rtnl_handle *rth, int fam, int type,
+ __u32 filt_mask);
+extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len);
+
+typedef int (*rtnl_filter_t)(const struct sockaddr_nl *,
+ struct nlmsghdr *n, void *);
+
+struct rtnl_dump_filter_arg
+{
+ rtnl_filter_t filter;
+ void *arg1;
+};
+
+extern int rtnl_dump_filter_l(struct rtnl_handle *rth,
+ const struct rtnl_dump_filter_arg *arg);
+extern int rtnl_dump_filter(struct rtnl_handle *rth, rtnl_filter_t filter,
+ void *arg);
+extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+ unsigned groups, struct nlmsghdr *answer);
+extern int rtnl_send(struct rtnl_handle *rth, const void *buf, int);
+extern int rtnl_send_check(struct rtnl_handle *rth, const void *buf, int);
+
+extern int addattr(struct nlmsghdr *n, int maxlen, int type);
+extern int addattr8(struct nlmsghdr *n, int maxlen, int type, __u8 data);
+extern int addattr16(struct nlmsghdr *n, int maxlen, int type, __u16 data);
+extern int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data);
+extern int addattr64(struct nlmsghdr *n, int maxlen, int type, __u64 data);
+extern int addattrstrz(struct nlmsghdr *n, int maxlen, int type, const char *data);
+
+extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen);
+extern int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len);
+extern struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type);
+extern int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest);
+extern struct rtattr *addattr_nest_compat(struct nlmsghdr *n, int maxlen, int type, const void *data, int len);
+extern int addattr_nest_compat_end(struct nlmsghdr *n, struct rtattr *nest);
+extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data);
+extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, const void *data, int alen);
+
+extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+extern int parse_rtattr_flags(struct rtattr *tb[], int max, struct rtattr *rta,
+ int len, unsigned short flags);
+extern int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+extern int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+
+#define parse_rtattr_nested(tb, max, rta) \
+ (parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta)))
+
+#define parse_rtattr_nested_compat(tb, max, rta, data, len) \
+ ({ data = RTA_PAYLOAD(rta) >= len ? RTA_DATA(rta) : NULL; \
+ __parse_rtattr_nested_compat(tb, max, rta, len); })
+
+static inline __u8 rta_getattr_u8(const struct rtattr *rta)
+{
+ return *(__u8 *)RTA_DATA(rta);
+}
+static inline __u16 rta_getattr_u16(const struct rtattr *rta)
+{
+ return *(__u16 *)RTA_DATA(rta);
+}
+static inline __u32 rta_getattr_u32(const struct rtattr *rta)
+{
+ return *(__u32 *)RTA_DATA(rta);
+}
+static inline __u64 rta_getattr_u64(const struct rtattr *rta)
+{
+ __u64 tmp;
+ memcpy(&tmp, RTA_DATA(rta), sizeof(__u64));
+ return tmp;
+}
+static inline const char *rta_getattr_str(const struct rtattr *rta)
+{
+ return (const char *)RTA_DATA(rta);
+}
+
+extern int rtnl_listen(struct rtnl_handle *, rtnl_filter_t handler,
+ void *jarg);
+extern int rtnl_from_file(FILE *, rtnl_filter_t handler,
+ void *jarg);
+
+#define NLMSG_TAIL(nmsg) \
+ ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
+
+#ifndef IFA_RTA
+#define IFA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
+#endif
+#ifndef IFA_PAYLOAD
+#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg))
+#endif
+
+#ifndef IFLA_RTA
+#define IFLA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
+#endif
+#ifndef IFLA_PAYLOAD
+#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg))
+#endif
+
+#ifndef NDA_RTA
+#define NDA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
+#endif
+#ifndef NDA_PAYLOAD
+#define NDA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndmsg))
+#endif
+
+#ifndef NDTA_RTA
+#define NDTA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndtmsg))))
+#endif
+#ifndef NDTA_PAYLOAD
+#define NDTA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndtmsg))
+#endif
+
+#endif /* __LIBNETLINK_H__ */
+
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/ll_map.c b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/ll_map.c
new file mode 100644
index 0000000..64e5069
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/ll_map.c
@@ -0,0 +1,225 @@
+/*
+ * Note: Original file from iproute2 package
+ *
+ * ll_map.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <net/if.h>
+
+#include "libnetlink.h"
+#include "ll_map.h"
+#include "hlist.h"
+
+struct ll_cache {
+ struct hlist_node idx_hash;
+ struct hlist_node name_hash;
+ unsigned flags;
+ int index;
+ unsigned short type;
+ char name[IFNAMSIZ];
+};
+
+#define IDXMAP_SIZE 1024
+static struct hlist_head idx_head[IDXMAP_SIZE];
+static struct hlist_head name_head[IDXMAP_SIZE];
+
+static struct ll_cache *ll_get_by_index(unsigned index)
+{
+ struct hlist_node *n;
+ unsigned h = index & (IDXMAP_SIZE - 1);
+
+ hlist_for_each(n, &idx_head[h]) {
+ struct ll_cache *im
+ = container_of(n, struct ll_cache, idx_hash);
+ if (im->index == index)
+ return im;
+ }
+
+ return NULL;
+}
+
+static unsigned namehash(const char *str)
+{
+ unsigned hash = 5381;
+
+ while (*str)
+ hash = ((hash << 5) + hash) + *str++; /* hash * 33 + c */
+
+ return hash;
+}
+
+static struct ll_cache *ll_get_by_name(const char *name)
+{
+ struct hlist_node *n;
+ unsigned h = namehash(name) & (IDXMAP_SIZE - 1);
+
+ hlist_for_each(n, &name_head[h]) {
+ struct ll_cache *im
+ = container_of(n, struct ll_cache, name_hash);
+
+ if (strncmp(im->name, name, IFNAMSIZ) == 0)
+ return im;
+ }
+
+ return NULL;
+}
+
+int ll_remember_index(const struct sockaddr_nl *who,
+ struct nlmsghdr *n, void *arg)
+{
+ unsigned int h;
+ const char *ifname;
+ struct ifinfomsg *ifi = NLMSG_DATA(n);
+ struct ll_cache *im;
+ struct rtattr *tb[IFLA_MAX+1];
+
+ if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK)
+ return 0;
+
+ if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi)))
+ return -1;
+
+ im = ll_get_by_index(ifi->ifi_index);
+ if (n->nlmsg_type == RTM_DELLINK) {
+ if (im) {
+ hlist_del(&im->name_hash);
+ hlist_del(&im->idx_hash);
+ free(im);
+ }
+ return 0;
+ }
+
+ memset(tb, 0, sizeof(tb));
+ parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n));
+ ifname = rta_getattr_str(tb[IFLA_IFNAME]);
+ if (ifname == NULL)
+ return 0;
+
+ if (im) {
+ /* change to existing entry */
+ if (strcmp(im->name, ifname) != 0) {
+ hlist_del(&im->name_hash);
+ h = namehash(ifname) & (IDXMAP_SIZE - 1);
+ hlist_add_head(&im->name_hash, &name_head[h]);
+ }
+
+ im->flags = ifi->ifi_flags;
+ return 0;
+ }
+
+ im = malloc(sizeof(*im));
+ if (im == NULL)
+ return 0;
+ im->index = ifi->ifi_index;
+ strcpy(im->name, ifname);
+ im->type = ifi->ifi_type;
+ im->flags = ifi->ifi_flags;
+
+ h = ifi->ifi_index & (IDXMAP_SIZE - 1);
+ hlist_add_head(&im->idx_hash, &idx_head[h]);
+
+ h = namehash(ifname) & (IDXMAP_SIZE - 1);
+ hlist_add_head(&im->name_hash, &name_head[h]);
+
+ return 0;
+}
+
+const char *ll_idx_n2a(unsigned idx, char *buf)
+{
+ const struct ll_cache *im;
+
+ if (idx == 0)
+ return "*";
+
+ im = ll_get_by_index(idx);
+ if (im)
+ return im->name;
+
+ if (if_indextoname(idx, buf) == NULL)
+ snprintf(buf, IFNAMSIZ, "if%d", idx);
+
+ return buf;
+}
+
+const char *ll_index_to_name(unsigned idx)
+{
+ static char nbuf[IFNAMSIZ];
+
+ return ll_idx_n2a(idx, nbuf);
+}
+
+int ll_index_to_type(unsigned idx)
+{
+ const struct ll_cache *im;
+
+ if (idx == 0)
+ return -1;
+
+ im = ll_get_by_index(idx);
+ return im ? im->type : -1;
+}
+
+unsigned ll_index_to_flags(unsigned idx)
+{
+ const struct ll_cache *im;
+
+ if (idx == 0)
+ return 0;
+
+ im = ll_get_by_index(idx);
+ return im ? im->flags : -1;
+}
+
+unsigned ll_name_to_index(const char *name)
+{
+ const struct ll_cache *im;
+ unsigned idx;
+
+ if (name == NULL)
+ return 0;
+
+ im = ll_get_by_name(name);
+ if (im)
+ return im->index;
+
+ idx = if_nametoindex(name);
+ if (idx == 0)
+ sscanf(name, "if%u", &idx);
+ return idx;
+}
+
+void ll_init_map(struct rtnl_handle *rth)
+{
+ static int initialized;
+
+ if (initialized)
+ return;
+
+ if (rtnl_wilddump_request(rth, AF_UNSPEC, RTM_GETLINK) < 0) {
+ perror("Cannot send dump request");
+ exit(1);
+ }
+
+ if (rtnl_dump_filter(rth, ll_remember_index, NULL) < 0) {
+ fprintf(stderr, "Dump terminated\n");
+ exit(1);
+ }
+
+ initialized = 1;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/ll_map.h b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/ll_map.h
new file mode 100644
index 0000000..d74a46f
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/ll_map.h
@@ -0,0 +1,32 @@
+/*
+ * Note: Original file from iproute2 package
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __LL_MAP_H__
+#define __LL_MAP_H__ 1
+
+extern int ll_remember_index(const struct sockaddr_nl *who,
+ struct nlmsghdr *n, void *arg);
+
+extern void ll_init_map(struct rtnl_handle *rth);
+extern unsigned ll_name_to_index(const char *name);
+extern const char *ll_index_to_name(unsigned idx);
+extern const char *ll_idx_n2a(unsigned idx, char *buf);
+extern int ll_index_to_type(unsigned idx);
+extern unsigned ll_index_to_flags(unsigned idx);
+
+#endif /* __LL_MAP_H__ */
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/main.c b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/main.c
new file mode 100644
index 0000000..4312402
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/main.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/errno.h>
+
+#include "nic.h"
+#include "intf.h"
+
+#include "facebook/log.h"
+#include "facebook/wedge_eeprom.h"
+
+#define WAIT4PACKET_TIMEOUT 10000 /* 10ms */
+#define NO_RCV_CHECK_THRESHOLD 100 /* if not receiving pkt for 100 times (1s),
+ * check the NIC status
+ */
+
+static void io_loop(oob_nic *nic, oob_intf *intf, const uint8_t mac[6]) {
+
+ fd_set rfds;
+ int fd = oob_intf_get_fd(intf);
+ struct timeval timeout;
+ int rc;
+ int n_fds;
+ int n_io;
+ char buf[NIC_PKT_SIZE_MAX];
+ int no_rcv = 0;
+ struct oob_nic_status_t sts;
+
+ while (1) {
+ memset(&timeout, 0, sizeof(timeout));
+ timeout.tv_sec = 0;
+ timeout.tv_usec = WAIT4PACKET_TIMEOUT;
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+
+ n_fds = select(fd + 1, &rfds, NULL, NULL, &timeout);
+ if (n_fds < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to select");
+ continue;
+ }
+
+ /*
+ * no matter what, receive packet from nic first, as the nic
+ * has small amount of memory. Without read, the sending could
+ * fail due to OOM.
+ *
+ * TODO: We might want to do something smart here to prevent attack or
+ * just rx flooding. Disable the Receive Enable first, drain the buffer
+ * with oob_nic_receive(), then Tx, and enable Receive Enable after.
+ */
+ for (n_io = 0; n_io < 16; n_io++) {
+ rc = oob_nic_receive(nic, buf, sizeof(buf));
+ if (rc <= 0) {
+ no_rcv++;
+ break;
+ }
+ oob_intf_send(intf, buf, rc);
+ no_rcv = 0;
+ }
+
+ /*
+ * if we didn't receive any packet for NO_RCV_CHECK_THRESHOLD times,
+ * check the nic status
+ */
+ if (no_rcv >= NO_RCV_CHECK_THRESHOLD) {
+ while(oob_nic_get_status(nic, &sts)) {
+ usleep(1000);
+ }
+ LOG_INFO("Failed to receive packets for %d times. NIC status is "
+ "%x.%x", NO_RCV_CHECK_THRESHOLD, sts.ons_byte1, sts.ons_byte2);
+ /*
+ * if the NIC went through initialization, or not set force up, need to
+ * re-program the filters by calling oob_nic_start().
+ */
+ if ((sts.ons_byte1 & NIC_STATUS_D1_INIT)
+ || !(sts.ons_byte1 & NIC_STATUS_D1_FORCE_UP)) {
+ while(oob_nic_start(nic, mac)) {
+ usleep(1000);
+ }
+ }
+ no_rcv = 0;
+ }
+
+ if (n_fds > 0 && FD_ISSET(fd, &rfds)) {
+ for (n_io = 0; n_io < 1; n_io++) {
+ rc = oob_intf_receive(intf, buf, sizeof(buf));
+ if (rc <= 0) {
+ break;
+ }
+ oob_nic_send(nic, buf, rc);
+ }
+ }
+ }
+}
+
+int main(int argc, const char **argv) {
+
+ uint8_t mac[6];
+ oob_nic *nic;
+ oob_intf *intf;
+ struct wedge_eeprom_st eeprom;
+ int rc;
+ int from_eeprom = 0;
+
+ nic = oob_nic_open(0, 0x49);
+ if (!nic) {
+ return -1;
+ }
+
+ /* read EEPROM for the MAC */
+ if (wedge_eeprom_parse(NULL, &eeprom) == 0) {
+ if (eeprom.fbw_mac_size <= 0) {
+ LOG_ERR(EFAULT, "Invalid extended MAC size: %d", eeprom.fbw_mac_size);
+ } else {
+ uint16_t carry;
+ int pos;
+ /* use the last MAC address from the extended MAC range */
+ memcpy(mac, eeprom.fbw_mac_base, sizeof(mac));
+ if (eeprom.fbw_mac_size > 128) {
+ LOG_ERR(EFAULT, "Extended MAC size (%d) is too large.",
+ eeprom.fbw_mac_size);
+ carry = 127;
+ } else {
+ /*
+ * hack around bug device which have the same MAC address on
+ * left and right.
+ */
+ if (strncmp(eeprom.fbw_location, "LEFT", FBW_EEPROM_F_LOCATION) == 0) {
+ carry = eeprom.fbw_mac_size - 2;
+ } else {
+ carry = eeprom.fbw_mac_size - 1;
+ }
+ }
+ for (pos = sizeof(mac) - 1; pos >= 0 && carry; pos--) {
+ uint16_t tmp = mac[pos] + carry;
+ mac[pos] = tmp & 0xFF;
+ carry = tmp >> 8;
+ }
+ from_eeprom = 1;
+ }
+ }
+
+ if (!from_eeprom) {
+ while (oob_nic_get_mac(nic, mac)) {
+ usleep(1000);
+ }
+ /*
+ * increase the last byte of the mac by 1 and turn on the
+ * local administered bit to use it as the oob nic mac
+ */
+ mac[0] |= 0x2;
+ mac[5]++;
+ }
+
+ LOG_INFO("Retrieve MAC %x:%x:%x:%x:%x:%x from %s",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
+ (from_eeprom) ? "EEPROM" : "NIC");
+
+ /* create the tap interface */
+ intf = oob_intf_create("oob", mac);
+ if (!intf) {
+ return -1;
+ }
+
+ while (oob_nic_start(nic, mac)) {
+ usleep(1000);
+ }
+
+ io_loop(nic, intf, mac);
+
+ return 0;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/nic.c b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/nic.c
new file mode 100644
index 0000000..a4dc071
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/nic.c
@@ -0,0 +1,487 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include "nic.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "facebook/i2c-dev.h"
+#include "facebook/log.h"
+
+struct oob_nic_t {
+ int on_bus;
+ uint8_t on_addr;
+ int on_file; /* the file descriptor */
+ uint8_t on_mac[6]; /* the mac address assigned to this NIC */
+};
+
+oob_nic* oob_nic_open(int bus, uint8_t addr) {
+ oob_nic *dev = NULL;
+ char fn[32];
+ int rc;
+
+ /* address must be 7 bits maximum */
+ if ((addr & 0x80)) {
+ LOG_ERR(EINVAL, "Address 0x%x has the 8th bit", addr);
+ return NULL;
+ }
+
+ dev = calloc(1, sizeof(*dev));
+ if (!dev) {
+ return NULL;
+ }
+ dev->on_bus = bus;
+ dev->on_addr = addr;
+
+ /* construct the device file name */
+ snprintf(fn, sizeof(fn), "/dev/i2c-%d", bus);
+ dev->on_file = open(fn, O_RDWR);
+ if (dev->on_file == -1) {
+ LOG_ERR(errno, "Failed to open i2c device %s", fn);
+ goto err_out;
+ }
+
+ /* assign the device address */
+ rc = ioctl(dev->on_file, I2C_SLAVE, dev->on_addr);
+ if (rc < 0) {
+ LOG_ERR(errno, "Failed to open slave @ address 0x%x", dev->on_addr);
+ goto err_out;
+ }
+
+ return dev;
+
+ err_out:
+ oob_nic_close(dev);
+ return NULL;
+}
+
+void oob_nic_close(oob_nic *dev) {
+ if (!dev) {
+ return;
+ }
+ if (dev->on_file != -1) {
+ close(dev->on_file);
+ }
+ free(dev);
+}
+
+int oob_nic_get_mac(oob_nic *dev, uint8_t mac[6]) {
+ int rc;
+ uint8_t buf[64];
+
+ rc = i2c_smbus_read_block_data(dev->on_file, NIC_READ_MAC_CMD, buf);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to get MAC on %d-%x",
+ dev->on_bus, dev->on_addr);
+ return -rc;
+ }
+
+ if (rc != NIC_READ_MAC_RES_LEN) {
+ LOG_ERR(EFAULT, "Unexpected response len (%d) for get MAC on %d-%x",
+ rc, dev->on_bus, dev->on_addr);
+ return -EFAULT;
+ }
+
+ if (buf[0] != NIC_READ_MAC_RES_OPT) {
+ LOG_ERR(EFAULT, "Unexpected response opt code (0x%x) get MAC on %d-%x",
+ buf[0], dev->on_bus, dev->on_addr);
+ return -EFAULT;
+ }
+
+ memcpy(mac, &buf[1], 6);
+
+ LOG_DBG("Get MAC on %d-%x: %x:%x:%x:%x:%x:%x", dev->on_bus, dev->on_addr,
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+ return 0;
+}
+
+int oob_nic_get_status(oob_nic *dev, oob_nic_status *status) {
+ int rc;
+ uint8_t buf[64];
+
+ rc = i2c_smbus_read_block_data(dev->on_file, NIC_READ_STATUS_CMD, buf);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to get status on %d-%x",
+ dev->on_bus, dev->on_addr);
+ return -rc;
+ }
+
+ if (rc != NIC_READ_STATUS_RES_LEN) {
+ LOG_ERR(EFAULT, "Unexpected response len (%d) for get status on %d-%x",
+ rc, dev->on_bus, dev->on_addr);
+ return -EFAULT;
+ }
+
+ if (buf[0] != NIC_READ_STATUS_RES_OPT) {
+ LOG_ERR(EFAULT, "Unexpected response opt code (0x%x) get status on %d-%x",
+ buf[0], dev->on_bus, dev->on_addr);
+ return -EFAULT;
+ }
+
+ memset(status, 0, sizeof(*status));
+ status->ons_byte1 = buf[1];
+ status->ons_byte2 = buf[2];
+
+ LOG_VER("Get status on %d-%x: byte1:0x%x byte2:0x%x",
+ dev->on_bus, dev->on_addr,
+ status->ons_byte1, status->ons_byte2);
+ return 0;
+}
+
+int oob_nic_receive(oob_nic *dev, uint8_t *buf, int len) {
+
+ int rc = 0;
+ uint8_t pkt[I2C_SMBUS_BLOCK_LARGE_MAX];
+ uint8_t opt;
+ int copied = 0;
+ int to_copy;
+ int expect_first = 1;
+ int n_frags = 0;
+
+#define _COPY_DATA(n, data) do { \
+ int to_copy; \
+ if (copied >= len) { \
+ break; \
+ } \
+ to_copy = (n < len - copied) ? n : len - copied; \
+ if (to_copy) { \
+ memcpy(buf + copied, data, to_copy); \
+ } \
+ copied += to_copy; \
+} while(0)
+
+ do {
+ rc = i2c_smbus_read_block_large_data(dev->on_file, NIC_READ_PKT_CMD, pkt);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to get packet on %d-%x",
+ dev->on_bus, dev->on_addr);
+ goto err_out;
+ }
+ if (rc > I2C_SMBUS_BLOCK_LARGE_MAX) {
+ LOG_ERR(EFAULT, "Too large i2c block (%d) received on %d-%x",
+ rc, dev->on_bus, dev->on_addr);
+ rc = EFAULT;
+ goto err_out;
+ }
+ opt = pkt[0];
+ switch (opt) {
+ case NIC_READ_PKT_RES_FIRST_OPT:
+ if (!expect_first) {
+ rc = EFAULT;
+ LOG_ERR(rc, "Received more than one buffer with FIRST set");
+ goto err_out;
+ }
+ expect_first = 0;
+ n_frags++;
+ _COPY_DATA(rc - 1, &pkt[1]);
+ break;
+ case NIC_READ_PKT_RES_MIDDLE_OPT:
+ if (expect_first) {
+ rc = EFAULT;
+ LOG_ERR(rc, "Received MIDDLE before getting FIRST");
+ goto err_out;
+ }
+ _COPY_DATA(rc - 1, &pkt[1]);
+ n_frags++;
+ break;
+ case NIC_READ_PKT_RES_LAST_OPT:
+ if (expect_first) {
+ rc = EFAULT;
+ LOG_ERR(rc, "Received LAST before getting FIRST");
+ goto err_out;
+ }
+ if (rc != NIC_READ_PKT_RES_LAST_LEN) {
+ LOG_ERR(EFAULT, "Expect %d bytes (got %d) for LAST segement",
+ NIC_READ_PKT_RES_LAST_LEN, rc);
+ rc = EFAULT;
+ goto err_out;
+ }
+ /* TODO: pkt status???? */
+ break;
+ case NIC_READ_STATUS_RES_OPT:
+ /* that means no pkt available */
+ if (!expect_first) {
+ rc = EFAULT;
+ LOG_ERR(rc, "Received STATUS in the middle of packet");
+ goto err_out;
+ }
+ //LOG_VER("Received STATUS when receiving the packet");
+ return 0;
+ default:
+ rc = EFAULT;
+ LOG_ERR(rc, "Unexpected opt code 0x%x", opt);
+ goto err_out;
+ }
+ } while (opt != NIC_READ_PKT_RES_LAST_OPT);
+
+ LOG_VER("Received a packet with %d bytes in %d fragments", copied, n_frags);
+ return copied;
+
+ err_out:
+ return -rc;
+#undef _COPY_DATA
+}
+
+int oob_nic_send(oob_nic *dev, const uint8_t *data, int len) {
+
+ int rc;
+ uint8_t to_send;
+ int has_sent = 0;
+ int is_first = 1;
+ uint8_t cmd;
+ int n_frags = 0;
+
+ if (len <= 0 || len > NIC_PKT_SIZE_MAX) {
+ rc = EINVAL;
+ LOG_ERR(rc, "Invalid packet length %d", len);
+ return -rc;
+ }
+
+ while (len) {
+ to_send = (len < OOB_NIC_PKT_FRAGMENT_SIZE)
+ ? len : OOB_NIC_PKT_FRAGMENT_SIZE;
+
+ if (is_first) {
+ if (to_send >= len) {
+ /* this is the last pkt also */
+ cmd = NIC_WRITE_PKT_SINGLE_CMD;
+ } else {
+ cmd = NIC_WRITE_PKT_FIRST_CMD;
+ }
+ is_first = 0;
+ } else {
+ if (to_send >= len) {
+ /* this is the last pkt */
+ cmd = NIC_WRITE_PKT_LAST_CMD;
+ } else {
+ cmd = NIC_WRITE_PKT_MIDDLE_CMD;
+ }
+ }
+
+ rc = i2c_smbus_write_block_large_data(dev->on_file, cmd,
+ to_send, data + has_sent);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to sent packet with cmd 0x%x, has_sent=%d "
+ "to_send=%d", cmd, has_sent, to_send);
+ return -rc;
+ }
+
+ has_sent += to_send;
+ len -= to_send;
+ n_frags++;
+ }
+
+ LOG_VER("Sent a packet with %d bytes in %d fragments", has_sent, n_frags);
+
+ return has_sent;
+}
+
+static int oob_nic_set_mng_ctrl(oob_nic *dev, const uint8_t *data, int len) {
+ int rc;
+
+ if (len <= 0) {
+ rc = EINVAL;
+ LOG_ERR(rc, "Invalid data length: %d", len);
+ return -rc;
+ }
+
+ rc = i2c_smbus_write_block_data(dev->on_file, NIC_WRITE_MNG_CTRL_CMD,
+ len, data);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to send management control command for parameter # %d",
+ data[0]);
+ return -rc;
+ }
+
+ return 0;
+}
+
+static int oob_nic_set_force_up(oob_nic *dev, int enable) {
+ uint8_t cmd[2];
+
+ cmd[0] = NIC_MNG_CTRL_KEEP_LINK_UP_NUM;
+ cmd[1] = enable
+ ? NIC_MNG_CTRL_KEEP_LINK_UP_ENABLE : NIC_MNG_CTRL_KEEP_LINK_UP_DISABLE;
+
+ LOG_DBG("Turn %s link force up", enable ? "on" : "off");
+ return oob_nic_set_mng_ctrl(dev, cmd, sizeof(cmd));
+}
+
+static int oob_nic_setup_filters(oob_nic *dev, const uint8_t mac[6]) {
+ int rc;
+ int i;
+ uint32_t cmd32;
+ uint8_t buf[32];
+ uint8_t *cmd;
+
+ /*
+ * Command to set MAC filter
+ * Seven bytes are required to load the MAC address filters.
+ * Data 2—MAC address filters pair number (3:0).
+ * Data 3—MSB of MAC address.
+ * ...
+ * Data 8: LSB of MAC address.
+ */
+ /* set MAC filter to pair 0 */
+ cmd = buf;
+ *cmd++ = NIC_FILTER_MAC_NUM;
+ *cmd++ = NIC_FILTER_MAC_PAIR0; /* pair 0 */
+ for (i = 0; i < 6; i++) {
+ *cmd++ = mac[i];
+ }
+ rc = i2c_smbus_write_block_data(dev->on_file, NIC_WRITE_FILTER_CMD,
+ cmd - buf, buf);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to set MAC filter");
+ return -rc;
+ }
+
+ /*
+ * Command to enable filter
+ *
+ * 9 bytes to load the extended decision filters (MDEF_EXT & MDEF)
+ * Data 2—MDEF filter index (valid values are 0...6)
+ * Data 3—MSB of MDEF_EXT (DecisionFilter0)
+ * ....
+ * Data 6—LSB of MDEF_EXT (DecisionFilter0)
+ * Data 7—MSB of MDEF (DecisionFilter0)
+ * ....
+ * Data 10—LSB of MDEF (DecisionFilter0)
+ */
+
+ /* enable MAC filter pair 0 on filter 0 */
+ cmd = buf;
+ *cmd++ = NIC_FILTER_DECISION_EXT_NUM;
+ *cmd++ = NIC_FILTER_MDEF0;
+ /* enable filter for traffic from network and host */
+ cmd32 = NIC_FILTER_MDEF_BIT(NIC_FILTER_MDEF_EXT_NET_EN_OFFSET)
+ | NIC_FILTER_MDEF_BIT(NIC_FILTER_MDEF_EXT_HOST_EN_OFFSET);
+ for (i = 0; i < sizeof(cmd32); i++) {
+ *cmd++ = (cmd32 >> (24 - 8 * i)) & 0xFF;
+ }
+ /* enable mac pair 0 */
+ cmd32 = NIC_FILTER_MDEF_BIT_VAL(NIC_FILTER_MDEF_MAC_AND_OFFSET,
+ NIC_FILTER_MAC_PAIR0);
+ for (i = 0; i < sizeof(cmd32); i++) {
+ *cmd++ = (cmd32 >> (24 - 8 * i)) & 0xFF;
+ }
+ rc = i2c_smbus_write_block_data(dev->on_file, NIC_WRITE_FILTER_CMD,
+ cmd - buf, buf);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to set MAC filter to MDEF 0");
+ return -rc;
+ }
+ /* enable ARP and ND on filter 1*/
+ cmd = buf;
+ *cmd++ = NIC_FILTER_DECISION_EXT_NUM;
+ *cmd++ = NIC_FILTER_MDEF1;
+ /* enable filter for traffic from network and host */
+ cmd32 = NIC_FILTER_MDEF_BIT(NIC_FILTER_MDEF_EXT_NET_EN_OFFSET)
+ | NIC_FILTER_MDEF_BIT(NIC_FILTER_MDEF_EXT_HOST_EN_OFFSET);
+ for (i = 0; i < sizeof(cmd32); i++) {
+ *cmd++ = (cmd32 >> (24 - 8 * i)) & 0xFF;
+ }
+ /* enable ARP and ND */
+ cmd32 = NIC_FILTER_MDEF_BIT(NIC_FILTER_MDEF_ARP_REQ_OR_OFFSET)
+ | NIC_FILTER_MDEF_BIT(NIC_FILTER_MDEF_ARP_RES_OR_OFFSET)
+ | NIC_FILTER_MDEF_BIT(NIC_FILTER_MDEF_NBG_OR_OFFSET);
+ for (i = 0; i < sizeof(cmd32); i++) {
+ *cmd++ = (cmd32 >> (24 - 8 * i)) & 0xFF;
+ }
+ rc = i2c_smbus_write_block_data(dev->on_file, NIC_WRITE_FILTER_CMD,
+ cmd - buf, buf);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to set ARP and ND filter to MDEF 1");
+ return -rc;
+ }
+
+ /* make filter 0, matching MAC, to be mng only */
+ cmd = buf;
+ *cmd++ = NIC_FILTER_MNG_ONLY_NUM;
+ cmd32 = NIC_FILTER_MNG_ONLY_FILTER0;
+ for (i = 0; i < sizeof(cmd32); i++) {
+ *cmd++ = (cmd32 >> (24 - 8 * i)) & 0xFF;
+ }
+ rc = i2c_smbus_write_block_data(dev->on_file, NIC_WRITE_FILTER_CMD,
+ cmd - buf, buf);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to enabled management only filter");
+ return -rc;
+ }
+
+ return 0;
+}
+
+int oob_nic_start(oob_nic *dev, const uint8_t mac[6]) {
+ int rc;
+ uint8_t cmd;
+
+ /* force the link up, no matter what the status of the main link */
+ rc = oob_nic_set_force_up(dev, 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ oob_nic_setup_filters(dev, mac);
+
+ /* first byte is the control */
+ cmd = NIC_WRITE_RECV_ENABLE_EN
+ | NIC_WRITE_RECV_ENABLE_STA
+ | NIC_WRITE_RECV_ENABLE_NM_UNSUPP /* TODO, to support ALERT */
+ | NIC_WRITE_RECV_ENABLE_RESERVED;
+
+ rc = i2c_smbus_write_block_data(dev->on_file, NIC_WRITE_RECV_ENABLE_CMD,
+ 1, &cmd);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to start receive function");
+ return -rc;
+ }
+ LOG_DBG("Started receive function");
+ return 0;
+}
+
+int oob_nic_stop(oob_nic *dev) {
+ int rc;
+ uint8_t ctrl;
+ /* don't set any enable bits, which turns off the receive func */
+ ctrl = NIC_WRITE_RECV_ENABLE_RESERVED;
+ rc = i2c_smbus_write_block_data(dev->on_file, NIC_WRITE_RECV_ENABLE_CMD,
+ 1, &ctrl);
+ if (rc < 0) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to stop receive function");
+ return -rc;
+ }
+ LOG_DBG("Stopped receive function");
+ return 0;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/nic.h b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/nic.h
new file mode 100644
index 0000000..1ac7ff8
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/nic.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef NIC_H
+#define NIC_H
+
+#include <stdint.h>
+
+#include "nic_defs.h"
+
+typedef struct oob_nic_t oob_nic;
+
+oob_nic* oob_nic_open(int bus, uint8_t addr);
+void oob_nic_close(oob_nic* dev);
+
+/* MAC */
+int oob_nic_get_mac(oob_nic *dev, uint8_t mac[6]);
+
+/* Status */
+typedef struct oob_nic_status_t oob_nic_status;
+int oob_nic_get_status(oob_nic *dev, oob_nic_status *status);
+
+int oob_nic_start(oob_nic *dev, const uint8_t mac[6]);
+int oob_nic_stop(oob_nic *dev);
+
+int oob_nic_send(oob_nic *dev, const uint8_t *data, int len);
+
+int oob_nic_receive(oob_nic *dev, uint8_t *buf, int len);
+
+#endif
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/nic_defs.h b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/nic_defs.h
new file mode 100644
index 0000000..1ae8721
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic/src/nic_defs.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef NIC_DEFS_H
+#define NIC_DEFS_H
+
+#include <stdint.h>
+
+#define OOB_NIC_PKT_FRAGMENT_SIZE 240
+#define NIC_PKT_SIZE_MAX 1536
+
+/** Get System MAC Address */
+#define NIC_READ_MAC_CMD 0xD4
+#define NIC_READ_MAC_RES_OPT 0xD4
+#define NIC_READ_MAC_RES_LEN 7
+
+/** Read Status */
+#define NIC_READ_STATUS_CMD 0xDE
+#define NIC_READ_STATUS_RES_OPT 0xDD
+#define NIC_READ_STATUS_RES_LEN 3
+
+struct oob_nic_status_t {
+ uint8_t ons_byte1;
+ uint8_t ons_byte2;
+};
+
+#define NIC_STATUS_D1_POWER_DR 0x00
+#define NIC_STATUS_D1_POWER_D0U 0x01
+#define NIC_STATUS_D1_POWER_D0 0x10
+#define NIC_STATUS_D1_POWER_D3 0x11
+#define NIC_STATUS_D1_PORT_MSB (0x1 << 2)
+#define NIC_STATUS_D1_INIT (0x1 << 3)
+#define NIC_STATUS_D1_FORCE_UP (0x1 << 4)
+#define NIC_STATUS_D1_LINK (0x1 << 5)
+#define NIC_STATUS_D1_TCO_CMD_ABORT (0x1 << 6)
+#define NIC_STATUS_D1_PORT_LSB (0x1 << 7)
+
+#define NIC_STATUS_D2_ICR (0x1 << 1)
+#define NIC_STATUS_D2_IPI (0x1 << 2)
+#define NIC_STATUS_D2_DRV_VALID (0x1 << 3)
+
+/** Receive TCO Packet */
+#define NIC_READ_PKT_CMD 0xC0
+#define NIC_READ_PKT_RES_FIRST_OPT 0x90
+#define NIC_READ_PKT_RES_MIDDLE_OPT 0x10
+#define NIC_READ_PKT_RES_LAST_OPT 0x50
+#define NIC_READ_PKT_RES_LAST_LEN 17
+
+/** Transmit Packet */
+#define NIC_WRITE_PKT_SINGLE_CMD 0xC4
+#define NIC_WRITE_PKT_FIRST_CMD 0x84
+#define NIC_WRITE_PKT_MIDDLE_CMD 0x04
+#define NIC_WRITE_PKT_LAST_CMD 0x44
+
+/** Management Control */
+#define NIC_WRITE_MNG_CTRL_CMD 0xC1
+
+#define NIC_MNG_CTRL_KEEP_LINK_UP_NUM 0x00
+#define NIC_MNG_CTRL_KEEP_LINK_UP_ENABLE 0x01
+#define NIC_MNG_CTRL_KEEP_LINK_UP_DISABLE 0x00
+
+/** Update MNG RCV Filter Parameters */
+#define NIC_WRITE_FILTER_CMD 0xCC
+
+#define NIC_FILTER_MAC_NUM 0x66
+#define NIC_FILTER_MAC_PAIR0 0
+#define NIC_FILTER_MAC_PAIR1 1
+#define NIC_FILTER_MAC_PAIR2 2
+#define NIC_FILTER_MAC_PAIR3 3
+
+#define NIC_FILTER_MNG_ONLY_NUM 0xF
+#define NIC_FILTER_MNG_ONLY_FILTER0 (0x1)
+#define NIC_FILTER_MNG_ONLY_FILTER1 (0x1 << 1)
+#define NIC_FILTER_MNG_ONLY_FILTER2 (0x1 << 2)
+#define NIC_FILTER_MNG_ONLY_FILTER3 (0x1 << 3)
+#define NIC_FILTER_MNG_ONLY_FILTER4 (0x1 << 4)
+
+#define NIC_FILTER_DECISION_EXT_NUM 0x68
+#define NIC_FILTER_MDEF0 0 /* index 0 */
+#define NIC_FILTER_MDEF1 1 /* index 1 */
+#define NIC_FILTER_MDEF2 2 /* index 2 */
+#define NIC_FILTER_MDEF3 3 /* index 3 */
+#define NIC_FILTER_MDEF4 4 /* index 4 */
+#define NIC_FILTER_MDEF5 5 /* index 5 */
+#define NIC_FILTER_MDEF6 6 /* index 6 */
+#define NIC_FILTER_MDEF7 7 /* index 7 */
+
+#define NIC_FILTER_MDEF_MAC_AND_OFFSET 0
+#define NIC_FILTER_MDEF_BCAST_AND_OFFSET 4
+#define NIC_FILTER_MDEF_VLAN_AND_OFFSET 5
+#define NIC_FILTER_MDEF_IPV4_AND_OFFSET 13
+#define NIC_FILTER_MDEF_IPV6_AND_OFFSET 17
+#define NIC_FILTER_MDEF_MAC_OR_OFFSET 21
+#define NIC_FILTER_MDEF_BCAST_OR_OFFSET 25
+#define NIC_FILTER_MDEF_MCAST_AND_OFFSET 26
+#define NIC_FILTER_MDEF_ARP_REQ_OR_OFFSET 27
+#define NIC_FILTER_MDEF_ARP_RES_OR_OFFSET 28
+#define NIC_FILTER_MDEF_NBG_OR_OFFSET 29
+#define NIC_FILTER_MDEF_PORT298_OR_OFFSET 30
+#define NIC_FILTER_MDEF_PORT26F_OR_OFFSET 31
+
+#define NIC_FILTER_MDEF_EXT_ETHTYPE_AND_OFFSET 0
+#define NIC_FILTER_MDEF_EXT_ETHTYPE_OR_OFFSET 8
+#define NIC_FILTER_MDEF_EXT_FLEX_PORT_OR_OFFSET 16
+#define NIC_FILTER_MDEF_EXT_FLEX_TCO_OR_OFFSET 24
+#define NIC_FILTER_MDEF_EXT_NCSI_DISABLE_OFFSET 28
+#define NIC_FILTER_MDEF_EXT_FLOW_CONTROL_DISCARD_OFFSET 29
+#define NIC_FILTER_MDEF_EXT_NET_EN_OFFSET 30
+#define NIC_FILTER_MDEF_EXT_HOST_EN_OFFSET 31
+
+#define NIC_FILTER_MDEF_BIT(offset) ((0x1) << (offset))
+#define NIC_FILTER_MDEF_BIT_VAL(offset, val) ((0x1) << ((offset) + (val)))
+
+/** Receive Enable */
+#define NIC_WRITE_RECV_ENABLE_CMD 0xCA
+#define NIC_WRITE_RECV_ENABLE_LEN_MAX 14
+
+#define NIC_WRITE_RECV_ENABLE_EN 0x1
+#define NIC_WRITE_RECV_ENABLE_ALL (0x1 << 1)
+#define NIC_WRITE_RECV_ENABLE_STA (0x1 << 2)
+#define NIC_WRITE_RECV_ENABLE_ARP_RES (0x1 << 3)
+#define NIC_WRITE_RECV_ENABLE_NM_ALERT (0x00 << 4)
+#define NIC_WRITE_RECV_ENABLE_NM_ASYNC (0x01 << 4)
+#define NIC_WRITE_RECV_ENABLE_NM_DIRECT (0x02 << 4)
+#define NIC_WRITE_RECV_ENABLE_NM_UNSUPP (0x03 << 4)
+#define NIC_WRITE_RECV_ENABLE_RESERVED (0x1 << 6)
+#define NIC_WRITE_RECV_ENABLE_CBDM (0x1 << 7)
+
+#endif
diff --git a/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic_0.1.bb b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic_0.1.bb
new file mode 100644
index 0000000..1df83dc
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/oob-nic/oob-nic_0.1.bb
@@ -0,0 +1,29 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+
+SUMMARY = "OOB Shared NIC driver"
+DESCRIPTION = "The shared-nic driver"
+SECTION = "base"
+PR = "r2"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://main.c;beginline=4;endline=16;md5=da35978751a9d71b73679307c4d296ec"
+
+SRC_URI = "file://src \
+ "
+
+S = "${WORKDIR}/src"
+
+DEPENDS += "fbutils libwedge-eeprom"
+
+RDEPENDS_${PN} += "libwedge-eeprom"
+
+do_install() {
+ install -d ${D}${sbindir}
+ install -m 755 oob-nic ${D}${sbindir}/oob-nic
+ install -m 755 i2craw ${D}${sbindir}/i2craw
+ install -d ${D}${sysconfdir}/init.d
+ install -d ${D}${sysconfdir}/rcS.d
+ install -m 755 etc/oob-nic.sh ${D}${sysconfdir}/init.d/oob-nic.sh
+ update-rc.d -r ${D} oob-nic.sh start 80 S .
+}
+
+FILES_${PN} = " ${sbindir} ${sysconfdir} "
diff --git a/meta-facebook/meta-wedge/recipes-wedge/po-eeprom/files/Makefile b/meta-facebook/meta-wedge/recipes-wedge/po-eeprom/files/Makefile
new file mode 100644
index 0000000..408de95
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/po-eeprom/files/Makefile
@@ -0,0 +1,10 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+all: po-eeprom
+
+po-eeprom: po-eeprom.o
+ $(CC) -o $@ $^ $(LDFLAGS)
+
+.PHONY: clean
+
+clean:
+ rm -rf *.o po-eeprom
diff --git a/meta-facebook/meta-wedge/recipes-wedge/po-eeprom/files/po-eeprom.c b/meta-facebook/meta-wedge/recipes-wedge/po-eeprom/files/po-eeprom.c
new file mode 100644
index 0000000..46debee
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/po-eeprom/files/po-eeprom.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#define SERIAL_LEN 17
+
+usage()
+{
+ fprintf(stderr, "Usage: po-eeprom filename [filename...]\n"
+ "\twhere filename is the location of the PowerOne EEPROM, likely\n"
+ "\t/sys/bus/i2c/drivers/at24/7-0051/eeprom or\n"
+ "\t/sys/bus/i2c/drivers/at24/7-0052/eeprom\n");
+ exit(2);
+}
+
+int safe_read(int fd, void *buf, size_t size, char *msg)
+{
+ if (read(fd, buf, size) != size) {
+ perror(msg);
+ exit(3);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ int fd;
+ int file = 1;
+ uint8_t size;
+ uint8_t junk;
+ uint16_t crc;
+ char serial[SERIAL_LEN + 1];
+
+ if (argc < 2)
+ usage();
+
+ while (file < argc) {
+ fd = open(argv[file], O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "Couldn't open %s for EEPROM reading\n", argv[1]);
+ exit(1);
+ }
+
+ safe_read(fd, &size, sizeof(size), "read size");
+ safe_read(fd, &junk, sizeof(junk), "read junk");
+ /*
+ * Should probably check CRC here, PowerOne provided some code where
+ * it looks like a pretty standard CCITT CRC16.
+ */
+ safe_read(fd, &crc, sizeof(crc), "read CRC len");
+ safe_read(fd, serial, SERIAL_LEN, "read serial number");
+
+ serial[SERIAL_LEN] = 0;
+ printf("Serial number: %s\n", serial);
+ close(fd);
+ file++;
+ }
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/po-eeprom/po-eeprom_0.1.bb b/meta-facebook/meta-wedge/recipes-wedge/po-eeprom/po-eeprom_0.1.bb
new file mode 100644
index 0000000..e1d8b32
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/po-eeprom/po-eeprom_0.1.bb
@@ -0,0 +1,25 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+SUMMARY = "PowerOne EEPROM Utilities"
+DESCRIPTION = "Util for PowerOne eeprom"
+SECTION = "base"
+PR = "r1"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://po-eeprom.c;beginline=4;endline=16;md5=da35978751a9d71b73679307c4d296ec"
+
+SRC_URI = "file://po-eeprom.c \
+ file://Makefile \
+ "
+
+S = "${WORKDIR}"
+
+do_install() {
+ install -d ${D}${bindir}
+ install -m 0755 po-eeprom ${D}${bindir}/po-eeprom
+}
+
+FILES_${PN} = "${bindir}"
+
+# Inhibit complaints about .debug directories
+
+INHIBIT_PACKAGE_DEBUG_SPLIT = "1"
+INHIBIT_PACKAGE_STRIP = "1"
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/Makefile b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/Makefile
new file mode 100644
index 0000000..4a3c25d
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/Makefile
@@ -0,0 +1,16 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+all: modbuscmd gpiowatch modbussim
+
+modbuscmd: modbuscmd.c modbus.c
+ $(CC) -D_BSD_SOURCE -Wall -Werror -std=c99 -o $@ $^ $(LDFLAGS)
+
+modbussim: modbussim.c modbus.c
+ $(CC) -D_BSD_SOURCE -Wall -Werror -std=c99 -o $@ $^ $(LDFLAGS)
+
+gpiowatch: gpiowatch.c
+ $(CC) -D_BSD_SOURCE -Wall -Werror -std=c99 -o $@ $^ $(LDFLAGS)
+
+.PHONY: clean
+
+clean:
+ rm -rf *.o modbuscmd gpiowatch modbussim
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/gpiowatch.c b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/gpiowatch.c
new file mode 100644
index 0000000..11b5471
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/gpiowatch.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#define CHECK(x) { if((x) < 0) { \
+ error = x; \
+ goto cleanup; \
+} }
+
+#define CHECKNULL(x) { if((x) == NULL) { \
+ error = -1; \
+ goto cleanup; \
+} }
+
+typedef struct {
+ int fd;
+ int gpio;
+ char value;
+} wgpio;
+
+int main(int argc, char **argv) {
+ int error = 0;
+ int nfds = argc - 1;
+ int i = 0;
+ int polliv = 10000;
+ wgpio* wgpios = NULL;
+
+ if(argc < 2) {
+ fprintf(stderr, "Usage: %s <gpio num> [gpio num] [gpio num...]\n", argv[0]);
+ exit(1);
+ }
+ if(getenv("POLL_US")) {
+ polliv = atoi(getenv("POLL_US"));
+ }
+
+ wgpios = calloc(nfds, sizeof(wgpio));
+ CHECKNULL(wgpios)
+
+ fprintf(stderr, "Watching %d gpios:", nfds);
+ for(i = 1; i < argc; i++) {
+ char filename[255];
+ int gpio_num = atoi(argv[i]);
+ wgpios[i - 1].gpio = gpio_num;
+ snprintf(filename, 255, "/sys/class/gpio/gpio%d/value", gpio_num);
+ wgpios[i - 1].fd = open(filename, O_RDONLY);
+ CHECK(wgpios[i - 1].fd);
+ CHECK(read(wgpios[i - 1].fd, &(wgpios[i - 1].value), 1));
+ fprintf(stderr, " %d (currently: %c)", gpio_num, wgpios[i - 1].value);
+ }
+ fprintf(stderr, "\n");
+
+ do {
+ usleep(polliv);
+ for(i = 0; i < nfds; i++) {
+ wgpio* w = &wgpios[i];
+ char value;
+ lseek(w->fd, 0, SEEK_SET);
+ CHECK(read(w->fd, &value, 1));
+ if(value != w->value) {
+ printf("GPIO%d: %c -> %c\n", w->gpio, w->value, value);
+ w->value = value;
+ }
+ }
+ } while (1);
+
+cleanup:
+ if(error != 0) {
+ fprintf(stderr, "Error %d: %s\n", errno, strerror(errno));
+ error = 1;
+ }
+ return error;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbus.c b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbus.c
new file mode 100644
index 0000000..46618a9
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbus.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "modbus.h"
+#include <termios.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+static int loops = 0;
+void waitfd(int fd) {
+ while(1) {
+ int lsr;
+ int ret = ioctl(fd, TIOCSERGETLSR, &lsr);
+ if(ret == -1) {
+ fprintf(stderr, "Error checking ioctl: %s\n", strerror(errno));
+ break;
+ }
+ if(lsr & TIOCSER_TEMT) break;
+ loops++;
+ }
+}
+
+void gpio_on(int fd) {
+ lseek(fd, 0, SEEK_SET);
+ write(fd, "1", 1);
+}
+
+void gpio_off(int fd) {
+ lseek(fd, 0, SEEK_SET);
+ write(fd, "0", 1);
+}
+
+void decode_hex_in_place(char* buf, size_t* len) {
+ for(int i = 0; i < *len; i+=2) {
+ sscanf(buf + i, "%2hhx", buf + (i / 2));
+ }
+ *len = *len / 2;
+}
+
+void append_modbus_crc16(char* buf, size_t* len) {
+ uint16_t crc = modbus_crc16(buf, *len);
+ if (verbose)
+ fprintf(stderr, "[*] Append Modbus CRC16 %04x\n", crc);
+ buf[(*len)++] = crc >> 8;
+ buf[(*len)++] = crc & 0x00FF;
+}
+
+void print_hex(FILE* f, char* buf, size_t len) {
+ for(int i = 0; i < len; i++)
+ fprintf(f, "%02x ", buf[i]);
+}
+
+size_t read_wait(int fd, char* dst, size_t maxlen, int mdelay_us) {
+ fd_set fdset;
+ struct timeval timeout;
+ char read_buf[16];
+ size_t read_size = 0;
+ size_t pos = 0;
+ memset(dst, 0, maxlen);
+ for(;;) {
+ FD_ZERO(&fdset);
+ FD_SET(fd, &fdset);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = mdelay_us;
+ int rv = select(fd + 1, &fdset, NULL, NULL, &timeout);
+ if(rv == -1) {
+ perror("select()");
+ } else if (rv == 0) {
+ break;
+ }
+ read_size = read(fd, read_buf, 16);
+ if(read_size < 0) {
+ if(errno == EAGAIN) continue;
+ fprintf(stderr, "read error: %s\n", strerror(errno));
+ exit(1);
+ }
+ if((pos + read_size) < maxlen) {
+ memcpy(dst + pos, read_buf, read_size);
+ pos += read_size;
+ } else {
+ fprintf(stderr, "Response buffer overflowed!\n");
+ }
+ }
+ return pos;
+}
+
+/* From libmodbus, https://github.com/stephane/libmodbus
+ * Under LGPL. */
+/* Table of CRC values for high-order byte */
+static const uint8_t table_crc_hi[] = {
+ 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
+ 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+ 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
+ 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+ 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
+ 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
+ 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
+ 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+ 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
+ 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
+ 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
+ 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+ 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
+ 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
+ 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
+ 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+ 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
+ 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+ 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
+ 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+ 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
+ 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
+ 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
+ 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+ 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
+ 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
+};
+
+/* Table of CRC values for low-order byte */
+static const uint8_t table_crc_lo[] = {
+ 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
+ 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
+ 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
+ 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
+ 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
+ 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
+ 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
+ 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
+ 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
+ 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
+ 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
+ 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
+ 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
+ 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
+ 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
+ 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
+ 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
+ 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
+ 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
+ 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
+ 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
+ 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
+ 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
+ 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
+ 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
+ 0x43, 0x83, 0x41, 0x81, 0x80, 0x40
+};
+
+uint16_t modbus_crc16(char* buffer, size_t buffer_length) {
+ uint8_t crc_hi = 0xFF; /* high CRC byte initialized */
+ uint8_t crc_lo = 0xFF; /* low CRC byte initialized */
+ unsigned int i; /* will index into CRC lookup */
+
+ /* pass through message buffer */
+ while (buffer_length--) {
+ i = crc_hi ^ *((uint8_t*)(buffer++)); /* calculate the CRC */
+ crc_hi = crc_lo ^ table_crc_hi[i];
+ crc_lo = table_crc_lo[i];
+ }
+
+ return (crc_hi << 8 | crc_lo);
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbus.h b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbus.h
new file mode 100644
index 0000000..435d518
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbus.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef MODBUS_H_
+#define MODBUS_H_
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+uint16_t modbus_crc16(char* buffer, size_t length);
+
+#define DEFAULT_TTY "/dev/ttyS3"
+#define DEFAULT_GPIO 45
+
+#define CHECK(x) { if((x) < 0) { \
+ error = x; \
+ goto cleanup; \
+} }
+
+void waitfd(int fd);
+void gpio_on(int fd);
+void gpio_off(int fd);
+void decode_hex_in_place(char* buf, size_t* len);
+void append_modbus_crc16(char* buf, size_t* len);
+void print_hex(FILE* f, char* buf, size_t len);
+
+// Read until maxlen bytes or no bytes in mdelay_us microseconds
+size_t read_wait(int fd, char* dst, size_t maxlen, int mdelay_us);
+
+extern int verbose;
+
+#endif
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbuscmd.c b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbuscmd.c
new file mode 100644
index 0000000..2d33039
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbuscmd.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <termios.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <getopt.h>
+#include "modbus.h"
+
+int verbose;
+
+void usage() {
+ fprintf(stderr,
+ "modbuscmd [-v] [-t <tty>] [-g <gpio>] modbus_command\n"
+ "\ttty defaults to %s\n"
+ "\tgpio defaults to %d\n"
+ "\tmodbus command should be specified in hex\n"
+ "\teg:\ta40300000008\n",
+ DEFAULT_TTY, DEFAULT_GPIO);
+ exit(1);
+}
+
+int main(int argc, char **argv) {
+ int error = 0;
+ int fd;
+ struct termios tio;
+ char gpio_filename[255];
+ int gpio_fd = 0;
+ int gpio_n = DEFAULT_GPIO;
+ char *tty = DEFAULT_TTY;
+ char *modbus_cmd = NULL;
+ size_t cmd_len = 0;
+ verbose = 0;
+
+ int opt;
+ while((opt = getopt(argc, argv, "t:g:v")) != -1) {
+ switch (opt) {
+ case 't':
+ tty = optarg;
+ break;
+ case 'g':
+ gpio_n = atoi(optarg);
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+ if(optind < argc) {
+ modbus_cmd = argv[optind];
+ }
+ if(modbus_cmd == NULL) {
+ usage();
+ }
+
+ if (verbose)
+ fprintf(stderr, "[*] Opening TTY\n");
+ fd = open(tty, O_RDWR | O_NOCTTY);
+ CHECK(fd);
+
+ if (verbose)
+ fprintf(stderr, "[*] Opening GPIO %d\n", gpio_n);
+ snprintf(gpio_filename, 255, "/sys/class/gpio/gpio%d/value", gpio_n);
+ gpio_fd = open(gpio_filename, O_WRONLY | O_SYNC);
+ CHECK(gpio_fd);
+
+ if (verbose)
+ fprintf(stderr, "[*] Setting TTY flags!\n");
+ memset(&tio, 0, sizeof(tio));
+ cfsetspeed(&tio,B19200);
+ tio.c_cflag |= PARENB;
+ tio.c_cflag |= CLOCAL;
+ tio.c_cflag |= CS8;
+ tio.c_iflag |= INPCK;
+ tio.c_cc[VMIN] = 1;
+ tio.c_cc[VTIME] = 0;
+ CHECK(tcsetattr(fd,TCSANOW,&tio));
+
+ //convert hex to bytes
+ cmd_len = strlen(modbus_cmd);
+ if(cmd_len < 4) {
+ fprintf(stderr, "Modbus command too short!\n");
+ exit(1);
+ }
+ decode_hex_in_place(modbus_cmd, &cmd_len);
+ append_modbus_crc16(modbus_cmd, &cmd_len);
+ // print command as sent
+ if (verbose) {
+ fprintf(stderr, "Will send: ");
+ print_hex(stderr, modbus_cmd, cmd_len);
+ fprintf(stderr, "\n");
+ }
+
+ if (verbose)
+ fprintf(stderr, "[*] Writing!\n");
+
+ // gpio on, write, wait, gpio off
+ gpio_on(gpio_fd);
+ write(fd, modbus_cmd, cmd_len);
+ waitfd(fd);
+ gpio_off(gpio_fd);
+
+ // Enable UART read
+ tio.c_cflag |= CREAD;
+ CHECK(tcsetattr(fd,TCSANOW,&tio));
+
+ if(verbose)
+ fprintf(stderr, "[*] reading any response...\n");
+ // Read back response
+ char modbus_buf[255];
+ size_t mb_pos = 0;
+ memset(modbus_buf, 0, sizeof(modbus_buf));
+ mb_pos = read_wait(fd, modbus_buf, sizeof(modbus_buf), 90000);
+ if(mb_pos >= 4) {
+ uint16_t crc = modbus_crc16(modbus_buf, mb_pos - 2);
+ if(verbose)
+ fprintf(stderr, "Modbus response CRC: %04X\n ", crc);
+ if((modbus_buf[mb_pos - 2] == (crc >> 8)) &&
+ (modbus_buf[mb_pos - 1] == (crc & 0x00FF))) {
+ if(verbose)
+ fprintf(stderr, "CRC OK!\n");
+ print_hex(stdout, modbus_buf, mb_pos);
+ printf("\n");
+ } else {
+ fprintf(stderr, "BAD CRC :(\n");
+ return 5;
+ }
+ } else {
+ fprintf(stderr, "No response :(\n");
+ return 4;
+ }
+
+cleanup:
+ if(error != 0) {
+ error = 1;
+ fprintf(stderr, "%s\n", strerror(errno));
+ }
+ return error;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbussim.c b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbussim.c
new file mode 100644
index 0000000..bf8c6c8
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbussim.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <termios.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <getopt.h>
+#include "modbus.h"
+
+int verbose = 0;
+
+void usage() {
+ fprintf(stderr,
+ "modbussim [-v] [-t <tty>] [-g <gpio>] modbus_request modbus_reply\n"
+ "\ttty defaults to %s\n"
+ "\tgpio defaults to %d\n"
+ "\tmodbus request/reply should be specified in hex\n"
+ "\teg:\ta40300000008\n",
+ DEFAULT_TTY, DEFAULT_GPIO);
+ exit(1);
+}
+
+int main(int argc, char **argv) {
+ int error = 0;
+ int fd;
+ struct termios tio;
+ char gpio_filename[255];
+ int gpio_fd = 0;
+ int gpio_n = DEFAULT_GPIO;
+ char *tty = DEFAULT_TTY;
+ char *modbus_cmd = NULL;
+ char *modbus_reply = NULL;
+ size_t cmd_len = 0;
+ size_t reply_len = 0;
+ verbose = 0;
+
+ int opt;
+ while((opt = getopt(argc, argv, "t:g:v"))) {
+ if (opt == -1) break;
+ switch (opt) {
+ case 't':
+ tty = optarg;
+ break;
+ case 'g':
+ gpio_n = atoi(optarg);
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+ if(optind < argc) {
+ modbus_cmd = argv[optind++];
+ modbus_reply = argv[optind++];
+ }
+ if(modbus_cmd == NULL || modbus_reply == NULL) {
+ usage();
+ }
+
+ if (verbose)
+ fprintf(stderr, "[*] Opening TTY\n");
+ fd = open(tty, O_RDWR | O_NOCTTY);
+ CHECK(fd);
+
+ if (verbose)
+ fprintf(stderr, "[*] Opening GPIO %d\n", gpio_n);
+ snprintf(gpio_filename, 255, "/sys/class/gpio/gpio%d/value", gpio_n);
+ gpio_fd = open(gpio_filename, O_WRONLY | O_SYNC);
+ CHECK(gpio_fd);
+
+ if (verbose)
+ fprintf(stderr, "[*] Setting TTY flags!\n");
+ memset(&tio, 0, sizeof(tio));
+ cfsetspeed(&tio,B19200);
+ tio.c_cflag |= PARENB;
+ tio.c_cflag |= CLOCAL;
+ tio.c_cflag |= CS8;
+ tio.c_iflag |= INPCK;
+ tio.c_cc[VMIN] = 1;
+ tio.c_cc[VTIME] = 0;
+ CHECK(tcsetattr(fd,TCSANOW,&tio));
+
+ //convert hex to bytes
+ cmd_len = strlen(modbus_cmd);
+ if(cmd_len < 2) {
+ fprintf(stderr, "Command too short!\n");
+ exit(1);
+ }
+ decode_hex_in_place(modbus_cmd, &cmd_len);
+ append_modbus_crc16(modbus_cmd, &cmd_len);
+ if (verbose) {
+ fprintf(stderr, "expect: ");
+ print_hex(stderr, modbus_cmd, cmd_len);
+ fprintf(stderr, "\n");
+ }
+ reply_len = strlen(modbus_reply);
+ decode_hex_in_place(modbus_reply, &reply_len);
+ append_modbus_crc16(modbus_reply, &reply_len);
+ // print full expected reply
+ if (verbose) {
+ fprintf(stderr, "reply: ");
+ print_hex(stderr, modbus_reply, reply_len);
+ fprintf(stderr, "\n");
+ }
+
+ // Enable UART read
+ tio.c_cflag |= CREAD;
+ CHECK(tcsetattr(fd,TCSANOW,&tio));
+ gpio_off(gpio_fd);
+
+ if(verbose)
+ fprintf(stderr, "[*] Wait for matching command...\n");
+
+ char modbus_buf[255];
+ size_t mb_pos;
+wait_for_command:
+ mb_pos = read_wait(fd, modbus_buf, sizeof(modbus_buf), 30000);
+ if(mb_pos >= 4) {
+ printf("Received: ");
+ print_hex(stdout, modbus_buf, mb_pos);
+ uint16_t crc = modbus_crc16(modbus_buf, mb_pos - 2);
+ if((modbus_buf[mb_pos - 2] == (crc >> 8)) &&
+ (modbus_buf[mb_pos - 1] == (crc & 0x00FF))) {
+ if(verbose)
+ fprintf(stderr, "CRC OK!\n");
+ if(memcmp(modbus_buf, modbus_cmd, cmd_len) == 0) {
+ fprintf(stderr, "Command matched!\n");
+ } else {
+ fprintf(stderr, "Got modbus cmd that didn't match.\n");
+ goto wait_for_command;
+ }
+ } else {
+ fprintf(stderr, "Got data that failed modbus CRC.\n");
+ goto wait_for_command;
+ }
+ } else {
+ goto wait_for_command;
+ }
+
+
+ if (verbose)
+ fprintf(stderr, "[*] Writing reply!\n");
+
+ // Disable UART read
+ tio.c_cflag &= ~CREAD;
+ CHECK(tcsetattr(fd,TCSANOW,&tio));
+ // gpio on, write, wait, gpio off
+ gpio_on(gpio_fd);
+ write(fd, modbus_reply, reply_len);
+ waitfd(fd);
+ gpio_off(gpio_fd);
+
+cleanup:
+ if(error != 0) {
+ error = 1;
+ fprintf(stderr, "%s\n", strerror(errno));
+ }
+ return error;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon_0.1.bb b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon_0.1.bb
new file mode 100644
index 0000000..d3e79e4
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon_0.1.bb
@@ -0,0 +1,48 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+SUMMARY = "Rackmon Functionality"
+DESCRIPTION = "Rackmon Functionality"
+SECTION = "base"
+PR = "r1"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://modbus.c;beginline=4;endline=16;md5=da35978751a9d71b73679307c4d296ec"
+
+#DEPENDS_append = " update-rc.d-native"
+
+SRC_URI = "file://Makefile \
+ file://modbuscmd.c \
+ file://modbussim.c \
+ file://modbus.c \
+ file://modbus.h \
+ file://gpiowatch.c \
+ "
+
+S = "${WORKDIR}"
+
+binfiles = "modbuscmd \
+ modbussim \
+ gpiowatch \
+ "
+
+#otherfiles = "README"
+
+pkgdir = "rackmon"
+
+do_install() {
+ dst="${D}/usr/local/fbpackages/${pkgdir}"
+ bin="${D}/usr/local/bin"
+ install -d $dst
+ install -d $bin
+ for f in ${binfiles}; do
+ install -m 755 $f ${dst}/$f
+ ln -snf ../fbpackages/${pkgdir}/$f ${bin}/$f
+ done
+}
+
+FBPACKAGEDIR = "${prefix}/local/fbpackages"
+
+FILES_${PN} = "${FBPACKAGEDIR}/rackmon ${prefix}/local/bin ${sysconfdir} "
+
+# Inhibit complaints about .debug directories for the rackmon binaries:
+
+INHIBIT_PACKAGE_DEBUG_SPLIT = "1"
+INHIBIT_PACKAGE_STRIP = "1"
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rest-api/files/rest.py b/meta-facebook/meta-wedge/recipes-wedge/rest-api/files/rest.py
new file mode 100644
index 0000000..6d59fd6
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/rest-api/files/rest.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+
+from ctypes import *
+from bottle import route, run, template, request, response
+from bottle import abort
+import json
+from rest_fruid import *
+from rest_server import *
+from rest_sensors import *
+from rest_bmc import *
+from rest_gpios import *
+
+# Handler for root resource endpoint
+@route('/api')
+def rest_api():
+ result = {
+ "Information": {
+ "Description": "Wedge RESTful API Entry",
+ },
+ "Actions": [],
+ "Resources": [ "sys"],
+ }
+
+ return result
+
+# Handler for sys resource endpoint
+@route('/api/sys')
+def rest_sys():
+ result = {
+ "Information": {
+ "Description": "Wedge System",
+ },
+ "Actions": [],
+ "Resources": [ "mb", "bmc", "server", "sensors", "gpios"],
+ }
+
+ return result
+
+# Handler for sys/mb resource endpoint
+@route('/api/sys/mb')
+def rest_sys():
+ result = {
+ "Information": {
+ "Description": "System Motherboard",
+ },
+ "Actions": [],
+ "Resources": [ "fruid"],
+ }
+
+ return result
+
+# Handler for sys/mb/fruid resource endpoint
+@route('/api/sys/mb/fruid')
+def rest_fruid():
+ return get_fruid()
+
+# Handler for sys/bmc resource endpoint
+@route('/api/sys/bmc')
+def rest_bmc():
+ return get_bmc()
+
+# Handler for sys/server resource endpoint
+@route('/api/sys/server')
+def rest_bmc():
+ return get_server()
+
+# Handler for uServer resource endpoint
+@route('/api/sys/server', method='POST')
+def rest_server():
+ data = json.load(request.body)
+ return server_action(data)
+
+# Handler for sensors resource endpoint
+@route('/api/sys/sensors')
+def rest_sensors():
+ return get_sensors()
+
+# Handler for sensors resource endpoint
+@route('/api/sys/gpios')
+def rest_gpios():
+ return get_gpios()
+
+run(host = "::", port = 8080)
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rest-api/files/rest_bmc.py b/meta-facebook/meta-wedge/recipes-wedge/rest-api/files/rest_bmc.py
new file mode 100644
index 0000000..d9600ae
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/rest-api/files/rest_bmc.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+
+from subprocess import *
+
+# Handler for FRUID resource endpoint
+def get_bmc():
+ # Get BMC Reset Reason
+ wdt_counter = Popen('devmem 0x1e785010', \
+ shell=True, stdout=PIPE).stdout.read()
+ wdt_counter = int(wdt_counter, 0)
+
+ wdt_counter &= 0xff00
+
+ if wdt_counter:
+ por_flag = 0
+ else:
+ por_flag = 1
+
+ if por_flag:
+ reset_reason = "Power ON Reset"
+ else:
+ reset_reason = "User Initiated Reset or WDT Reset"
+
+ # Get BMC's Up Time
+ uptime = Popen('uptime', \
+ shell=True, stdout=PIPE).stdout.read()
+
+ # Get Usage information
+ data = Popen('top -b n1', \
+ shell=True, stdout=PIPE).stdout.read()
+ adata = data.split('\n')
+ mem_usage = adata[0]
+ cpu_usage = adata[1]
+
+ result = {
+ "Information": {
+ "Description": "Wedge BMC",
+ "Reset Reason": reset_reason,
+ "Uptime": uptime,
+ "Memory Usage": mem_usage,
+ "CPU Usage": cpu_usage,
+ },
+ "Actions": [],
+ "Resources": [],
+ }
+
+ return result;
+
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rest-api/files/rest_fruid.py b/meta-facebook/meta-wedge/recipes-wedge/rest-api/files/rest_fruid.py
new file mode 100644
index 0000000..167e1fa
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/rest-api/files/rest_fruid.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+
+from ctypes import *
+
+# Handler for FRUID resource endpoint
+fru = CDLL("libwedge_eeprom.so")
+
+class FRU(Structure):
+ _fields_ = [ ("fbw_version", c_ubyte),
+ ("fbw_product_name", c_char * 13),
+ ("fbw_product_number", c_char * 10),
+ ("fbw_assembly_number", c_char * 15),
+ ("fbw_facebook_pcb_number", c_char * 15),
+ ("fbw_odm_pcb_number", c_char * 14),
+ ("fbw_odm_pcb_serial", c_char * 13),
+ ("fbw_production_state", c_ubyte),
+ ("fbw_product_version", c_ubyte),
+ ("fbw_product_subversion", c_ubyte),
+ ("fbw_product_serial", c_char * 13),
+ ("fbw_product_asset", c_char * 13),
+ ("fbw_system_manufacturer", c_char * 9),
+ ("fbw_system_manufacturing_date", c_char * 10),
+ ("fbw_pcb_manufacturer", c_char * 9),
+ ("fbw_assembled", c_char * 9),
+ ("fbw_local_mac", c_ubyte * 6),
+ ("fbw_mac_base", c_ubyte * 6),
+ ("fbw_dummy", c_char),
+ ("fbw_mac_size", c_ushort),
+ ("fbw_location", c_char * 9),
+ ("fbw_crc8", c_ubyte) ]
+
+def get_fruid():
+ myfru = FRU()
+ p_myfru = pointer(myfru)
+ fru.wedge_eeprom_parse(None, p_myfru)
+
+ mac2str = lambda mac: ':'.join(['{:02X}'.format(b) for b in mac])
+
+ fruinfo = { "Version": myfru.fbw_version,
+ "Product Name": myfru.fbw_product_name,
+ "Product Part Number": myfru.fbw_product_number,
+ "System Assembly Part Number": myfru.fbw_assembly_number,
+ "Facebook PCB Part Number": myfru.fbw_facebook_pcb_number,
+ "ODM PCB Part Number": myfru.fbw_odm_pcb_number,
+ "ODM PCB Serial Number": myfru.fbw_odm_pcb_serial,
+ "Product Production State": myfru.fbw_production_state,
+ "Product Version": myfru.fbw_product_version,
+ "Product Sub-Version": myfru.fbw_product_subversion,
+ "Product Serial Number": myfru.fbw_product_serial,
+ "Product Asset Tag": myfru.fbw_product_asset,
+ "System Manufacturer": myfru.fbw_system_manufacturer,
+ "System Manufacturing Date": myfru.fbw_system_manufacturing_date,
+ "PCB Manufacturer": myfru.fbw_pcb_manufacturer,
+ "Assembled At": myfru.fbw_assembled,
+ "Local MAC": mac2str(myfru.fbw_local_mac),
+ "Extended MAC Base": mac2str(myfru.fbw_mac_base),
+ "Extended MAC Address Size": myfru.fbw_mac_size,
+ "Location on Fabric": myfru.fbw_location,
+ "CRC8": hex((myfru.fbw_crc8))
+ }
+
+ result = {
+ "Information": fruinfo,
+ "Actions": [],
+ "Resources": [],
+ }
+
+ return result
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rest-api/files/rest_gpios.py b/meta-facebook/meta-wedge/recipes-wedge/rest-api/files/rest_gpios.py
new file mode 100644
index 0000000..340c5c3
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/rest-api/files/rest_gpios.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+from rest_fruid import get_fruid
+
+WEDGES = ["Wedge-AC-F", "Wedge-DC-F"]
+
+
+def read_gpio_sysfs(gpio):
+ with open('/sys/class/gpio/gpio%d/value' % gpio, 'r') as f:
+ val_string = f.read()
+ if val_string == '1\n':
+ return 1
+ if val_string == '0\n':
+ return 0
+ return None
+
+
+def read_wedge_back_ports():
+ bhinfo = { "port_1": { "pin_1": read_gpio_sysfs(120),
+ "pin_2": read_gpio_sysfs(121),
+ "pin_3": read_gpio_sysfs(122),
+ "pin_4": read_gpio_sysfs(123)
+ },
+ "port_2": { "pin_1": read_gpio_sysfs(124),
+ "pin_2": read_gpio_sysfs(125),
+ "pin_3": read_gpio_sysfs(126),
+ "pin_4": read_gpio_sysfs(52)
+ }
+ }
+ return bhinfo
+
+
+def get_gpios():
+ fruinfo = get_fruid()
+ gpioinfo = {}
+ if fruinfo["Information"]["Product Name"] in WEDGES:
+ gpioinfo["back_ports"] = read_wedge_back_ports()
+ return gpioinfo
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rest-api/files/rest_sensors.py b/meta-facebook/meta-wedge/recipes-wedge/rest-api/files/rest_sensors.py
new file mode 100644
index 0000000..f4f83d3
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/rest-api/files/rest_sensors.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+
+import json
+import os
+import re
+
+# Handler for sensors resource endpoint
+def get_sensors():
+ result = []
+ data = os.popen('sensors').read()
+ data = re.sub(r'\(.+?\)', '', data)
+ for edata in data.split('\n\n'):
+ adata = edata.split('\n', 1)
+ sresult = {}
+ if (len(adata) < 2):
+ break;
+ sresult['name'] = adata[0]
+ for sdata in adata[1].split('\n'):
+ tdata = sdata.split(':')
+ if (len(tdata) < 2):
+ continue
+ sresult[tdata[0].strip()] = tdata[1].strip()
+ result.append(sresult)
+
+ fresult = {
+ "Information": result,
+ "Actions": [],
+ "Resources": [],
+ }
+
+ return fresult
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rest-api/files/rest_server.py b/meta-facebook/meta-wedge/recipes-wedge/rest-api/files/rest_server.py
new file mode 100644
index 0000000..0acba27
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/rest-api/files/rest_server.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+
+import os
+from subprocess import *
+
+# Handler for uServer resource endpoint
+def get_server():
+ ret = Popen('/usr/local/bin/wedge_power.sh status', \
+ shell=True, stdout=PIPE).stdout.read()
+ status = ret.rsplit()[-1]
+
+ result = {
+ "Information": { "status": status },
+ "Actions": ["power-on", "power-off", "power-reset"],
+ "Resources": [],
+ }
+
+ return result
+
+def server_action(data):
+ if data["action"] == 'power-on':
+ ret = Popen('/usr/local/bin/wedge_power.sh status', \
+ shell=True, stdout=PIPE).stdout.read()
+ status = ret.rsplit()[-1]
+ if status == 'on':
+ res = 'failure'
+ reason = 'already on'
+ else:
+ ret = Popen('/usr/local/bin/wedge_power.sh on', \
+ shell=True, stdout=PIPE).stdout.read()
+ res = "success"
+ elif data["action"] == 'power-off':
+ ret = Popen('/usr/local/bin/wedge_power.sh off', \
+ shell=True, stdout=PIPE).stdout.read()
+ res = "success"
+ elif data["action"] == 'power-reset':
+ ret = Popen('/usr/local/bin/wedge_power.sh reset', \
+ shell=True, stdout=PIPE).stdout.read()
+ res = "success"
+ else:
+ res = 'failure'
+ reason = 'invalid action'
+
+ if res == 'failure':
+ result = { "result": res, "reason": reason}
+ else:
+ result = { "result": res }
+
+ return result
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rest-api/files/setup-rest-api.sh b/meta-facebook/meta-wedge/recipes-wedge/rest-api/files/setup-rest-api.sh
new file mode 100644
index 0000000..fe01c23
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/rest-api/files/setup-rest-api.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+### BEGIN INIT INFO
+# Provides: setup-rest-api
+# Required-Start:
+# Required-Stop:
+# Default-Start: S
+# Default-Stop:
+# Short-Description: Set REST API handler
+### END INIT INFO
+
+echo -n "Setup REST API handler... "
+/usr/local/bin/rest.py > /tmp/rest.log 2>&1 &
+echo "done."
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rest-api/rest-api_0.1.bb b/meta-facebook/meta-wedge/recipes-wedge/rest-api/rest-api_0.1.bb
new file mode 100644
index 0000000..2753f30
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/rest-api/rest-api_0.1.bb
@@ -0,0 +1,52 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+SUMMARY = "Rest API Daemon"
+DESCRIPTION = "Daemon to handle RESTful interface."
+SECTION = "base"
+PR = "r1"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://rest.py;beginline=5;endline=18;md5=0b1ee7d6f844d472fa306b2fee2167e0"
+
+
+DEPENDS_append = " update-rc.d-native"
+
+SRC_URI = "file://setup-rest-api.sh \
+ file://rest.py \
+ file://rest_bmc.py \
+ file://rest_fruid.py \
+ file://rest_gpios.py \
+ file://rest_server.py \
+ file://rest_sensors.py \
+ "
+
+S = "${WORKDIR}"
+
+binfiles = "rest.py rest_bmc.py rest_fruid.py rest_gpios.py rest_server.py rest_sensors.py setup-rest-api.sh"
+
+pkgdir = "rest-api"
+
+do_install() {
+ dst="${D}/usr/local/fbpackages/${pkgdir}"
+ bin="${D}/usr/local/bin"
+ install -d $dst
+ install -d $bin
+ for f in ${binfiles}; do
+ install -m 755 $f ${dst}/$f
+ ln -snf ../fbpackages/${pkgdir}/$f ${bin}/$f
+ done
+ for f in ${otherfiles}; do
+ install -m 644 $f ${dst}/$f
+ done
+ install -d ${D}${sysconfdir}/init.d
+ install -d ${D}${sysconfdir}/rcS.d
+ install -m 755 setup-rest-api.sh ${D}${sysconfdir}/init.d/setup-rest-api.sh
+ update-rc.d -r ${D} setup-rest-api.sh start 95 2 3 4 5 .
+}
+
+FBPACKAGEDIR = "${prefix}/local/fbpackages"
+
+FILES_${PN} = "${FBPACKAGEDIR}/rest-api ${prefix}/local/bin ${sysconfdir} "
+
+# Inhibit complaints about .debug directories for the fand binary:
+
+INHIBIT_PACKAGE_DEBUG_SPLIT = "1"
+INHIBIT_PACKAGE_STRIP = "1"
diff --git a/meta-facebook/meta-wedge/recipes-wedge/sensor-setup/files/sensor-setup.sh b/meta-facebook/meta-wedge/recipes-wedge/sensor-setup/files/sensor-setup.sh
new file mode 100644
index 0000000..0027828
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/sensor-setup/files/sensor-setup.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+### BEGIN INIT INFO
+# Provides: sensor-setup
+# Required-Start: power-on
+# Required-Stop:
+# Default-Start: S
+# Default-Stop:
+# Short-Description: Power on micro-server
+### END INIT INFO
+
+# Eventually, this will be used to configure the various (mostly
+# i2c-based) sensors, once we have a kernel version that supports
+# doing this more dynamically.
+#
+# For now, we're using it to install the lm75 and pmbus module so that it
+# can detect the fourth temperature sensor, which is located
+# on the uServer, which doesn't get power until power-on executes.
+#
+# Similarly, the pmbus sensor seems to have an easier time of
+# detecting the NCP4200 buck converters after poweron. This has not
+# been carefully explored.
+
+modprobe lm75
+modprobe pmbus
+
+# Enable the ADC inputs; adc5 - adc9 should be connected to
+# 1V, 1.03V, 5V, 3.3V, and 2.5V.
+
+echo 1 > /sys/devices/platform/ast_adc.0/adc5_en
+echo 1 > /sys/devices/platform/ast_adc.0/adc6_en
+echo 1 > /sys/devices/platform/ast_adc.0/adc7_en
+echo 1 > /sys/devices/platform/ast_adc.0/adc8_en
+echo 1 > /sys/devices/platform/ast_adc.0/adc9_en
diff --git a/meta-facebook/meta-wedge/recipes-wedge/sensor-setup/sensor-setup_0.1.bb b/meta-facebook/meta-wedge/recipes-wedge/sensor-setup/sensor-setup_0.1.bb
new file mode 100644
index 0000000..ad6bb0c
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/sensor-setup/sensor-setup_0.1.bb
@@ -0,0 +1,23 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+SUMMARY = "Configure the sensors"
+DESCRIPTION = "The script configure sensors"
+SECTION = "base"
+PR = "r1"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://sensor-setup.sh;beginline=5;endline=18;md5=0b1ee7d6f844d472fa306b2fee2167e0"
+
+DEPENDS_append = " update-rc.d-native"
+
+SRC_URI = "file://sensor-setup.sh \
+ "
+
+S = "${WORKDIR}"
+
+do_install() {
+ install -d ${D}${sysconfdir}/init.d
+ install -d ${D}${sysconfdir}/rcS.d
+ install -m 755 sensor-setup.sh ${D}${sysconfdir}/init.d/sensor-setup.sh
+ update-rc.d -r ${D} sensor-setup.sh start 90 S .
+}
+
+FILES_${PN} = " ${sysconfdir} "
diff --git a/meta-facebook/meta-wedge/recipes-wedge/sms-kcsd/sms-kcsd/Makefile b/meta-facebook/meta-wedge/recipes-wedge/sms-kcsd/sms-kcsd/Makefile
new file mode 100644
index 0000000..6425261
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/sms-kcsd/sms-kcsd/Makefile
@@ -0,0 +1,10 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+all: sms-kcsd
+
+sms-kcsd: sms-kcsd.c
+ $(CC) -pthread -lalert_control -lipmi -std=c99 -o $@ $^ $(LDFLAGS)
+
+.PHONY: clean
+
+clean:
+ rm -rf *.o sms-kcsd
diff --git a/meta-facebook/meta-wedge/recipes-wedge/sms-kcsd/sms-kcsd/setup-sms-kcs.sh b/meta-facebook/meta-wedge/recipes-wedge/sms-kcsd/sms-kcsd/setup-sms-kcs.sh
new file mode 100644
index 0000000..d369f5c
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/sms-kcsd/sms-kcsd/setup-sms-kcs.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+### BEGIN INIT INFO
+# Provides: setup-sms-kcs
+# Required-Start:
+# Required-Stop:
+# Default-Start: S
+# Default-Stop:
+# Short-Description: Set SMS KCS handler
+### END INIT INFO
+
+echo -n "Setup SMS KCS message handler... "
+/usr/local/bin/sms-kcsd
+echo "done."
diff --git a/meta-facebook/meta-wedge/recipes-wedge/sms-kcsd/sms-kcsd/sms-kcsd.c b/meta-facebook/meta-wedge/recipes-wedge/sms-kcsd/sms-kcsd/sms-kcsd.c
new file mode 100644
index 0000000..a264fb7
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/sms-kcsd/sms-kcsd/sms-kcsd.c
@@ -0,0 +1,144 @@
+/*
+ * sms-kcsd
+ *
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * Daemon to monitor traffic coming from sms-kcs interface
+ * and respond to the command using IPMI stack
+ *
+ * TODO: Determine if the daemon is already started.
+ * TODO: Cache the file descriptors instead of fopen/fclose everytime
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <syslog.h>
+#include <facebook/alert_control.h>
+#include <facebook/ipmi.h>
+
+
+#define PATH_SMS_KCS "/sys/bus/i2c/drivers/panther_plus/4-0040/sms_kcs"
+#define MAX_ALERT_CONTROL_RETRIES 3
+
+typedef struct {
+ unsigned char fbid;
+ unsigned char length;
+ unsigned char buf[];
+} kcs_msg_t;
+
+/*
+ * Function to check if there is any new KCS message available
+ * TODO: Will be replaced by interrupt-driven approach
+ */
+static bool
+is_new_kcs_msg(void) {
+ return is_alert_present(FBID_SMS_KCS);
+}
+
+/*
+ * KCS Message Handler:
+ * - Reads the incoming request on KCS channel
+ * - Invokes IPMI handler to provide response
+ * - Writes reply back to KCS channel
+ */
+static int
+handle_kcs_msg(void) {
+ FILE *fp;
+ kcs_msg_t *msg;
+ unsigned char rbuf[256] = {0};
+ unsigned char tbuf[256] = {0};
+ unsigned char tlen = 0;
+ int count = 0;
+ int i = 0;
+
+ // Reads incoming request
+ fp = fopen(PATH_SMS_KCS, "r");
+ if (!fp) {
+ syslog(LOG_ALERT, "failed to open file %s\n", PATH_SMS_KCS);
+ return -1;
+ }
+
+ count = fread(rbuf, sizeof(unsigned char), sizeof(rbuf), fp);
+ if (count == 0) {
+ syslog(LOG_INFO, "fread returns zero bytes\n");
+ fclose(fp);
+ return -1;
+ }
+
+ fclose(fp);
+
+ msg = (kcs_msg_t*)rbuf;
+
+ // Invoke IPMI handler
+ ipmi_handle(msg->buf, msg->length, &tbuf[1], &tlen);
+
+ // Fill the length as returned by IPMI stack
+ tbuf[0] = tlen;
+
+ //Write Reply back to KCS channel
+ fp = fopen(PATH_SMS_KCS, "w");
+ if (!fp) {
+ syslog(LOG_ALERT, "failed to open file %s\n", PATH_SMS_KCS);
+ return -1;
+ }
+
+ count = fwrite(tbuf, sizeof(unsigned char), tlen+1, fp);
+ if (count != tlen+1) {
+ syslog(LOG_ALERT, "fwrite returns: %d, expected: %d\n", count, tlen+1);
+ fclose(fp);
+ return -1;
+ }
+
+ fclose(fp);
+
+ return 0;
+}
+
+/*
+ * Daemon Main loop
+ */
+int main(int argc, char **argv) {
+ int i;
+ int ret;
+ daemon(1, 0);
+ openlog("sms-kcs", LOG_CONS, LOG_DAEMON);
+
+ // Enable alert for SMS KCS Function Block
+ for (i = 0; i < MAX_ALERT_CONTROL_RETRIES; i++) {
+ ret = alert_control(FBID_SMS_KCS, FLAG_ENABLE);
+ if (!ret) {
+ break;
+ }
+ sleep(2);
+ }
+
+ // Exit with error in case we can not set the Alert
+ if(ret) {
+ syslog(LOG_ALERT, "Can not enable SMS KCS Alert\n");
+ exit(-1);
+ }
+
+ // Forever loop to poll and process KCS messages
+ while (1) {
+ if (is_new_kcs_msg()) {
+ handle_kcs_msg();
+ }
+ sleep(1);
+ }
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/sms-kcsd/sms-kcsd_0.1.bb b/meta-facebook/meta-wedge/recipes-wedge/sms-kcsd/sms-kcsd_0.1.bb
new file mode 100644
index 0000000..1f3ea08
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/sms-kcsd/sms-kcsd_0.1.bb
@@ -0,0 +1,46 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+SUMMARY = "SMS KCS Daemon"
+DESCRIPTION = "Daemon to handle SMS KCS interface."
+SECTION = "base"
+PR = "r1"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://sms-kcsd.c;beginline=12;endline=24;md5=da35978751a9d71b73679307c4d296ec"
+
+
+DEPENDS_append = " update-rc.d-native"
+
+DEPENDS += "libalert-control"
+DEPENDS += "libipmi"
+
+SRC_URI = "file://Makefile \
+ file://setup-sms-kcs.sh \
+ file://sms-kcsd.c \
+ "
+
+S = "${WORKDIR}"
+
+binfiles = "sms-kcsd"
+
+pkgdir = "sms-kcsd"
+
+do_install() {
+ dst="${D}/usr/local/fbpackages/${pkgdir}"
+ bin="${D}/usr/local/bin"
+ install -d $dst
+ install -d $bin
+ install -m 755 sms-kcsd ${dst}/sms-kcsd
+ ln -snf ../fbpackages/${pkgdir}/sms-kcsd ${bin}/sms-kcsd
+ install -d ${D}${sysconfdir}/init.d
+ install -d ${D}${sysconfdir}/rcS.d
+ install -m 755 setup-sms-kcs.sh ${D}${sysconfdir}/init.d/setup-sms-kcs.sh
+ update-rc.d -r ${D} setup-sms-kcs.sh start 65 S .
+}
+
+FBPACKAGEDIR = "${prefix}/local/fbpackages"
+
+FILES_${PN} = "${FBPACKAGEDIR}/sms-kcsd ${prefix}/local/bin ${sysconfdir} "
+
+# Inhibit complaints about .debug directories for the fand binary:
+
+INHIBIT_PACKAGE_DEBUG_SPLIT = "1"
+INHIBIT_PACKAGE_STRIP = "1"
diff --git a/meta-facebook/meta-wedge/recipes-wedge/usb-console/files/usbcons.sh b/meta-facebook/meta-wedge/recipes-wedge/usb-console/files/usbcons.sh
new file mode 100755
index 0000000..de284bb
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/usb-console/files/usbcons.sh
@@ -0,0 +1,80 @@
+#! /bin/sh
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+### BEGIN INIT INFO
+# Provides: usbcons
+# Required-Start:
+# Required-Stop:
+# Default-Start: S
+# Default-Stop: 0 6
+# Short-Description: Creates a virtual USB serial device and starts a console
+# on it.
+#
+### END INIT INFO
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin
+NAME=usbcons
+PIDFILE=/run/usbcons.pid
+DESC="USB Serial Console"
+
+# source function library
+. /etc/init.d/functions
+
+STOPPER=
+ACTION="$1"
+
+case "$ACTION" in
+ start)
+ # Ability to prevent this from starting by editing cmdline in u-boot.
+ # Keeping this here until I get gadget switching working properly. (t4906522)
+ if grep "nousbcons" /proc/cmdline > /dev/null 2>&1
+ then
+ echo "USB Console Disabled."
+ exit 0
+ fi
+ echo -n "Starting $DESC: "
+ /usr/local/bin/usbmon.sh > /dev/null 2>&1 &
+ echo "$NAME."
+ ;;
+ stop)
+ echo -n "Stopping $DESC: "
+ killall usbmon.sh
+ echo "$NAME."
+ ;;
+ restart|force-reload)
+ echo -n "Restarting $DESC: "
+ killall usbmon.sh
+ sleep 1
+ /usr/local/bin/usbmon.sh > /dev/null 2>&1 &
+ echo "$NAME."
+ ;;
+ status)
+ status $DAEMON
+ exit $?
+ ;;
+ *)
+ N=${0##*/}
+ N=${N#[SK]??}
+ echo "Usage: $N {start|stop|status|restart|force-reload}" >&2
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/meta-facebook/meta-wedge/recipes-wedge/usb-console/files/usbmon.sh b/meta-facebook/meta-wedge/recipes-wedge/usb-console/files/usbmon.sh
new file mode 100755
index 0000000..0030775
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/usb-console/files/usbmon.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+
+modprobe g_cdc host_addr=02:00:00:00:00:02 dev_addr=02:00:00:00:00:01
+while true; do
+ getty /dev/ttyGS0 57600
+ sleep 1
+done
diff --git a/meta-facebook/meta-wedge/recipes-wedge/usb-console/usb-console_0.1.bb b/meta-facebook/meta-wedge/recipes-wedge/usb-console/usb-console_0.1.bb
new file mode 100644
index 0000000..f92511c
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/usb-console/usb-console_0.1.bb
@@ -0,0 +1,27 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+SUMMARY = "Set up a USB serial console"
+DESCRIPTION = "Sets up a USB serial console"
+SECTION = "base"
+PR = "r1"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://usbcons.sh;beginline=5;endline=18;md5=0b1ee7d6f844d472fa306b2fee2167e0"
+
+DEPENDS_append = " update-rc.d-native"
+
+SRC_URI = "file://usbcons.sh \
+ file://usbmon.sh \
+ "
+
+S = "${WORKDIR}"
+
+do_install() {
+ install -d ${D}${sysconfdir}/init.d
+ install -d ${D}${sysconfdir}/rcS.d
+ install -m 755 usbcons.sh ${D}${sysconfdir}/init.d/usbcons.sh
+ update-rc.d -r ${D} usbcons.sh start 90 S .
+ localbindir="${D}/usr/local/bin"
+ install -d ${localbindir}
+ install -m 755 usbmon.sh ${localbindir}/usbmon.sh
+}
+
+FILES_${PN} = " ${sysconfdir} /usr/local"
diff --git a/meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/files/lib/Makefile b/meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/files/lib/Makefile
new file mode 100644
index 0000000..0264efe
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/files/lib/Makefile
@@ -0,0 +1,11 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+lib: libwedge_eeprom.so
+
+libwedge_eeprom.so: wedge_eeprom.c
+ $(CC) $(CCFLAGS) -fPIC -c -o wedge_eeprom.o wedge_eeprom.c
+ $(CC) -shared -o libwedge_eeprom.so wedge_eeprom.o -lc
+
+.PHONY: clean
+
+clean:
+ rm -rf *.o libwedge_eeprom.so
diff --git a/meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/files/lib/wedge_eeprom.c b/meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/files/lib/wedge_eeprom.c
new file mode 100644
index 0000000..44f708d
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/files/lib/wedge_eeprom.c
@@ -0,0 +1,403 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "wedge_eeprom.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <facebook/log.h>
+
+#define FBW_EEPROM_FILE "/sys/class/i2c-adapter/i2c-6/6-0050/eeprom"
+
+#define FBW_EEPROM_VERSION 0
+#define FBW_EEPROM_V0_SIZE 162
+
+/*
+ * The eeprom size is 8K, we only use 157 bytes for v1 format.
+ * Read 256 for now.
+ */
+#define FBW_EEPROM_SIZE 256
+
+
+static inline uint8_t fbw_crc8(uint8_t crc, uint8_t data)
+{
+ /* donot verify crc now, always return 0 */
+ return 0;
+}
+
+static uint8_t fbw_crc8_buf(const uint8_t *buf, int len)
+{
+ uint8_t crc = 0;
+ int i;
+
+ for (i = 0, crc = 0; i < len; i++) {
+ crc = fbw_crc8(crc, buf[i]);
+ }
+
+ return crc;
+}
+
+static inline void fbw_copy_uint8(uint8_t *val, const uint8_t** src,
+ int src_len)
+{
+ assert(src_len >= sizeof(*val));
+ *val = **src;
+ (*src) += src_len;
+}
+
+static inline void fbw_copy_uint16(uint16_t *val, const uint8_t** src,
+ int src_len)
+{
+ assert(src_len >= sizeof(*val));
+ *val = (**src) | ((*(*src + 1)) << 8);
+ (*src) += src_len;
+}
+
+static inline void fbw_copy_uint32(uint32_t *val, const uint8_t** src,
+ int src_len)
+{
+ assert(src_len >= sizeof(*val));
+ *val = (**src)
+ | ((*(*src + 1)) << 8)
+ | ((*(*src + 2)) << 16)
+ | ((*(*src + 3)) << 24);
+ (*src) += src_len;
+}
+
+static inline void fbw_strcpy(char *dst, int dst_len,
+ const uint8_t **src, int src_len)
+{
+ assert(dst_len >= src_len + 1); /* larger because of '\0' */
+ strncpy(dst, (char *)*src, src_len);
+ dst[src_len + 1] = '\0';
+ (*src) += src_len;
+}
+
+static inline void fbw_copy_product_number(
+ char *dst, int dst_len, const uint8_t **src, int src_len)
+{
+ int i;
+ const uint8_t *cur = *src;
+ /* 8 letter in the format of XX-XXXXXX, 2 additional letters */
+ assert(dst_len >= src_len + 2);
+ for (i = 0; i < 2; i++) {
+ *dst++ = *cur++;
+ }
+ *dst++ = '-';
+ for (i = 0; i < 6; i++) {
+ *dst++ = *cur++;
+ }
+ *dst = '\0';
+ (*src) += src_len;
+}
+
+static inline void fbw_copy_assembly_number(
+ char *dst, int dst_len, const uint8_t **src, int src_len)
+{
+ int i;
+ const uint8_t *cur = *src;
+ /* 11 letter in the format of XXX-XXXXXX-XX, 3 additional letters */
+ assert(dst_len >= src_len + 3);
+ for (i = 0; i < 3; i++) {
+ *dst++ = *cur++;
+ }
+ *dst++ = '-';
+ for (i = 0; i < 6; i++) {
+ *dst++ = *cur++;
+ }
+ *dst++ = '-';
+ for (i = 0; i < 2; i++) {
+ *dst++ = *cur++;
+ }
+ *dst = '\0';
+ (*src) += src_len;
+}
+
+static inline void fbw_copy_facebook_pcb_part(
+ char *dst, int dst_len, const uint8_t **src, int src_len)
+{
+ int i;
+ const uint8_t *cur = *src;
+ /* 11 letter in the format of XXX-XXXXXX-XX, 3 additional letters */
+ assert(dst_len >= src_len + 3);
+ for (i = 0; i < 3; i++) {
+ *dst++ = *cur++;
+ }
+ *dst++ = '-';
+ for (i = 0; i < 6; i++) {
+ *dst++ = *cur++;
+ }
+ *dst++ = '-';
+ for (i = 0; i < 2; i++) {
+ *dst++ = *cur++;
+ }
+ *dst = '\0';
+ (*src) += src_len;
+}
+
+static inline void fbw_copy_date(
+ char *dst, int dst_len, const uint8_t **src, int src_len)
+{
+ const uint8_t *cur = *src;
+ uint16_t year;
+ uint8_t month;
+ uint8_t day;
+ /* mm-dd-yy in output */
+ assert(dst_len >= 9);
+ /* input is 4 bytes YY YY MM DD */
+ assert(src_len >= 4);
+ fbw_copy_uint16(&year, &cur, 2);
+ fbw_copy_uint8(&month, &cur, 1);
+ fbw_copy_uint8(&day, &cur, 1);
+ snprintf(dst, dst_len, "%02d-%02d-%02d", month % 13, day % 32, year % 100);
+ (*src) += src_len;
+}
+
+static inline uint8_t _a2v(const uint8_t *a)
+{
+ uint8_t v = *a;
+ if ('0' <= v && v <= '9') {
+ return v - '0';
+ }
+ if ('a' <= v && v <= 'z') {
+ return v - 'a' + 10;
+ }
+ if ('A' <= v && v <= 'Z') {
+ return v - 'A' + 10;
+ }
+ return 0;
+}
+
+static inline void fbw_copy_mac(
+ uint8_t* dst, int dst_len, const uint8_t **src, int src_len)
+{
+ int i;
+ const uint8_t *cur = *src;
+
+ assert(dst_len >= 6);
+ assert(src_len >= 12);
+
+ for (i = 0; i < 6; i++) {
+ *dst = (_a2v(cur) << 4) | _a2v(cur + 1);
+ dst++;
+ cur +=2 ;
+ }
+ (*src) += src_len;
+}
+
+static int fbw_parse_buffer(
+ const uint8_t *buf, int len, struct wedge_eeprom_st *eeprom) {
+ int rc = 0;
+ const uint8_t* cur = buf;
+ uint16_t magic;
+ int crc_len;
+ uint8_t crc8;
+
+ memset(eeprom, 0, sizeof(*eeprom));
+
+ /* make sure the magic number */
+ fbw_copy_uint16(&magic, &cur, FBW_EEPROM_F_MAGIC);
+ if (magic != 0xfbfb) {
+ rc = EFAULT;
+ LOG_ERR(rc, "Unexpected magic word 0x%x", magic);
+ goto out;
+ }
+
+ /* confirm the version number, only version is supported */
+ fbw_copy_uint8(&eeprom->fbw_version, &cur, FBW_EEPROM_F_VERSION);
+ if (eeprom->fbw_version != FBW_EEPROM_VERSION) {
+ rc = EFAULT;
+ LOG_ERR(rc, "Unsupported version number %u", eeprom->fbw_version);
+ goto out;
+ } else {
+ crc_len = FBW_EEPROM_V0_SIZE;
+ assert(crc_len <= len);
+ }
+
+ /* check CRC */
+ crc8 = fbw_crc8_buf(buf, crc_len);
+ if (crc8 != 0) {
+ rc = EFAULT;
+ LOG_ERR(rc, "CRC check failed");
+ goto out;
+ }
+
+ /* Product name: ASCII for 12 characters */
+ fbw_strcpy(eeprom->fbw_product_name,
+ sizeof(eeprom->fbw_product_name),
+ &cur, FBW_EEPROM_F_PRODUCT_NAME);
+
+ /* Product Part #: 8 byte data shown as XX-XXXXXXX */
+ fbw_copy_product_number(eeprom->fbw_product_number,
+ sizeof(eeprom->fbw_product_number),
+ &cur, FBW_EEPROM_F_PRODUCT_NUMBER);
+
+ /* System Assembly Part Number: XXX-XXXXXX-XX */
+ fbw_copy_assembly_number(eeprom->fbw_assembly_number,
+ sizeof(eeprom->fbw_assembly_number),
+ &cur, FBW_EEPROM_F_ASSEMBLY_NUMBER);
+
+ /* Facebook PCB Part Number: XXX-XXXXXXX-XX */
+ fbw_copy_facebook_pcb_part(eeprom->fbw_facebook_pcb_number,
+ sizeof(eeprom->fbw_facebook_pcb_number),
+ &cur, FBW_EEPROM_F_FACEBOOK_PCB_NUMBER);
+
+ /* ODM PCB Part Number: XXXXXXXXXXXX */
+ fbw_strcpy(eeprom->fbw_odm_pcb_number,
+ sizeof(eeprom->fbw_odm_pcb_number),
+ &cur, FBW_EEPROM_F_ODM_PCB_NUMBER);
+
+ /* ODM PCB Serial Number: XXXXXXXXXXXX */
+ fbw_strcpy(eeprom->fbw_odm_pcb_serial,
+ sizeof(eeprom->fbw_odm_pcb_serial),
+ &cur, FBW_EEPROM_F_ODM_PCB_SERIAL);
+
+ /* Product Production State */
+ fbw_copy_uint8(&eeprom->fbw_production_state,
+ &cur, FBW_EEPROM_F_PRODUCT_STATE);
+
+ /* Product Version */
+ fbw_copy_uint8(&eeprom->fbw_product_version,
+ &cur, FBW_EEPROM_F_PRODUCT_VERSION);
+
+ /* Product Sub Version */
+ fbw_copy_uint8(&eeprom->fbw_product_subversion,
+ &cur, FBW_EEPROM_F_PRODUCT_SUBVERSION);
+
+ /* Product Serial Number: XXXXXXXX */
+ fbw_strcpy(eeprom->fbw_product_serial,
+ sizeof(eeprom->fbw_product_serial),
+ &cur, FBW_EEPROM_F_PRODUCT_SERIAL);
+
+ /* Product Assert Tag: XXXXXXXX */
+ fbw_strcpy(eeprom->fbw_product_asset,
+ sizeof(eeprom->fbw_product_asset),
+ &cur, FBW_EEPROM_F_PRODUCT_ASSET);
+
+ /* System Manufacturer: XXXXXXXX */
+ fbw_strcpy(eeprom->fbw_system_manufacturer,
+ sizeof(eeprom->fbw_system_manufacturer),
+ &cur, FBW_EEPROM_F_SYSTEM_MANUFACTURER);
+
+ /* System Manufacturing Date: mm-dd-yy */
+ fbw_copy_date(eeprom->fbw_system_manufacturing_date,
+ sizeof(eeprom->fbw_system_manufacturing_date),
+ &cur, FBW_EEPROM_F_SYSTEM_MANU_DATE);
+
+ /* PCB Manufacturer: XXXXXXXXX */
+ fbw_strcpy(eeprom->fbw_pcb_manufacturer,
+ sizeof(eeprom->fbw_pcb_manufacturer),
+ &cur, FBW_EEPROM_F_PCB_MANUFACTURER);
+
+ /* Assembled At: XXXXXXXX */
+ fbw_strcpy(eeprom->fbw_assembled,
+ sizeof(eeprom->fbw_assembled),
+ &cur, FBW_EEPROM_F_ASSEMBLED);
+
+ /* Local MAC Address */
+ fbw_copy_mac(eeprom->fbw_local_mac,
+ sizeof(eeprom->fbw_local_mac),
+ &cur, FBW_EEPROM_F_LOCAL_MAC);
+
+ /* Extended MAC Address */
+ fbw_copy_mac(eeprom->fbw_mac_base,
+ sizeof(eeprom->fbw_mac_base),
+ &cur, FBW_EEPROM_F_EXT_MAC_BASE);
+
+ /* Extended MAC Address Size */
+ fbw_copy_uint16(&eeprom->fbw_mac_size,
+ &cur,FBW_EEPROM_F_EXT_MAC_SIZE);
+
+ /* Location on Fabric: "LEFT"/"RIGHT", "WEDGE", "LC" */
+ fbw_strcpy(eeprom->fbw_location,
+ sizeof(eeprom->fbw_location),
+ &cur, FBW_EEPROM_F_LOCATION);
+
+ /* CRC8 */
+ fbw_copy_uint8(&eeprom->fbw_crc8,
+ &cur, FBW_EEPROM_F_CRC8);
+
+ assert((cur - buf) <= len);
+
+ out:
+ return rc;
+}
+
+int wedge_eeprom_parse(const char *fn, struct wedge_eeprom_st *eeprom)
+{
+ int rc = 0;
+ uint32_t len;
+ FILE *fin;
+ char buf[FBW_EEPROM_SIZE];
+
+ if (!eeprom) {
+ return -EINVAL;
+ }
+
+ if (!fn) {
+ fn = FBW_EEPROM_FILE;
+ }
+
+ fin = fopen(fn, "r");
+ if (fin == NULL) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to open %s", FBW_EEPROM_FILE);
+ goto out;
+ }
+
+ /* check the file size */
+ rc = fseek(fin, 0, SEEK_END);
+ if (rc) {
+ rc = errno;
+ LOG_ERR(rc, "Failed to seek to the end of %s", FBW_EEPROM_FILE);
+ goto out;
+ }
+
+ len = ftell(fin);
+ if (len < FBW_EEPROM_SIZE) {
+ rc = ENOSPC;
+ LOG_ERR(rc, "File '%s' is too small (%u < %u)", FBW_EEPROM_FILE,
+ len, FBW_EEPROM_SIZE);
+ goto out;
+ }
+
+ /* go back to the beginning of the file */
+ rewind(fin);
+
+ rc = fread(buf, 1, sizeof(buf), fin);
+ if (rc < sizeof(buf)) {
+ LOG_ERR(ENOSPC, "Failed to complete the read. Only got %d", rc);
+ rc = ENOSPC;
+ goto out;
+ }
+
+ rc = fbw_parse_buffer((const uint8_t *)buf, sizeof(buf), eeprom);
+ if (rc) {
+ goto out;
+ }
+
+ out:
+ if (fin) {
+ fclose(fin);
+ }
+
+ return -rc;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/files/lib/wedge_eeprom.h b/meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/files/lib/wedge_eeprom.h
new file mode 100644
index 0000000..2c4f39e
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/files/lib/wedge_eeprom.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef FBW_EEPROM_H
+#define FBW_EEPROM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+#define FBW_EEPROM_F_MAGIC 2
+#define FBW_EEPROM_F_VERSION 1
+#define FBW_EEPROM_F_PRODUCT_NAME 12
+#define FBW_EEPROM_F_PRODUCT_NUMBER 8
+#define FBW_EEPROM_F_ASSEMBLY_NUMBER 12
+#define FBW_EEPROM_F_FACEBOOK_PCB_NUMBER 12
+#define FBW_EEPROM_F_ODM_PCB_NUMBER 13
+#define FBW_EEPROM_F_ODM_PCB_SERIAL 12
+#define FBW_EEPROM_F_PRODUCT_STATE 1
+#define FBW_EEPROM_F_PRODUCT_VERSION 1
+#define FBW_EEPROM_F_PRODUCT_SUBVERSION 1
+#define FBW_EEPROM_F_PRODUCT_SERIAL 12
+#define FBW_EEPROM_F_PRODUCT_ASSET 12
+#define FBW_EEPROM_F_SYSTEM_MANUFACTURER 8
+#define FBW_EEPROM_F_SYSTEM_MANU_DATE 4
+#define FBW_EEPROM_F_PCB_MANUFACTURER 8
+#define FBW_EEPROM_F_ASSEMBLED 8
+#define FBW_EEPROM_F_LOCAL_MAC 12
+#define FBW_EEPROM_F_EXT_MAC_BASE 12
+#define FBW_EEPROM_F_EXT_MAC_SIZE 2
+#define FBW_EEPROM_F_LOCATION 8
+#define FBW_EEPROM_F_CRC8 1
+
+struct wedge_eeprom_st {
+ /* version number of the eeprom. Must be the first element */
+ uint8_t fbw_version;
+
+ /* Product Name */
+ char fbw_product_name[FBW_EEPROM_F_PRODUCT_NAME + 1];
+
+ /* Top Level 20 - Product Part Number: XX-XXXXXX */
+ char fbw_product_number[FBW_EEPROM_F_PRODUCT_NUMBER + 2];
+
+ /* System Assembly Part Number XXX-XXXXXX-XX */
+ char fbw_assembly_number[FBW_EEPROM_F_ASSEMBLY_NUMBER + 3];
+
+ /* Facebook PCB Part Number: XXX-XXXXXXX-XX */
+ char fbw_facebook_pcb_number[FBW_EEPROM_F_FACEBOOK_PCB_NUMBER + 3];
+
+ /* ODM PCB Part Number: XXXXXXXXXXXX */
+ char fbw_odm_pcb_number[FBW_EEPROM_F_ODM_PCB_NUMBER + 1];
+
+ /* ODM PCB Serial Number: XXXXXXXXXXXX */
+ char fbw_odm_pcb_serial[FBW_EEPROM_F_ODM_PCB_SERIAL + 1];
+
+ /* Product Production State */
+ uint8_t fbw_production_state;
+
+ /* Product Version */
+ uint8_t fbw_product_version;
+
+ /* Product Sub Version */
+ uint8_t fbw_product_subversion;
+
+ /* Product Serial Number: XXXXXXXX */
+ char fbw_product_serial[FBW_EEPROM_F_PRODUCT_SERIAL + 1];
+
+ /* Product Asset Tag: XXXXXXXX */
+ char fbw_product_asset[FBW_EEPROM_F_PRODUCT_ASSET + 1];
+
+ /* System Manufacturer: XXXXXXXX */
+ char fbw_system_manufacturer[FBW_EEPROM_F_SYSTEM_MANUFACTURER + 1];
+
+ /* System Manufacturing Date: mm-dd-yy */
+ char fbw_system_manufacturing_date[10];
+
+ /* PCB Manufacturer: XXXXXXXXX */
+ char fbw_pcb_manufacturer[FBW_EEPROM_F_PCB_MANUFACTURER + 1];
+
+ /* Assembled At: XXXXXXXX */
+ char fbw_assembled[FBW_EEPROM_F_ASSEMBLED + 1];
+
+ /* Local MAC Address */
+ uint8_t fbw_local_mac[6];
+
+ /* Extended MAC Address */
+ uint8_t fbw_mac_base[6];
+
+ /* Extended MAC Address Size */
+ uint16_t fbw_mac_size;
+
+ /* Location on Fabric: "LEFT"/"RIGHT", "WEDGE", "LC" */
+ char fbw_location[FBW_EEPROM_F_LOCATION + 1];
+
+ /* CRC8 */
+ uint8_t fbw_crc8;
+};
+
+int wedge_eeprom_parse(const char *fn, struct wedge_eeprom_st *eeprom);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/files/utils/Makefile b/meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/files/utils/Makefile
new file mode 100644
index 0000000..30aac75
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/files/utils/Makefile
@@ -0,0 +1,10 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+utils: weutil
+
+weutil: weutil.o
+ $(CC) $(LDFLAGS) -o $@ $^ -lwedge_eeprom
+
+.PHONY: clean
+
+clean:
+ rm -rf *.o weutil
diff --git a/meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/files/utils/weutil.c b/meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/files/utils/weutil.c
new file mode 100644
index 0000000..9e932aa
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/files/utils/weutil.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2014-present Facebook. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+
+#include <facebook/wedge_eeprom.h>
+#include <facebook/log.h>
+
+int main(int argc, const char *argv[])
+{
+ const char *fn;
+ struct wedge_eeprom_st eeprom;
+ int rc;
+
+ if (argc >= 2) {
+ fn = argv[1];
+ } else {
+ fn = NULL;
+ }
+
+ rc = wedge_eeprom_parse(fn, &eeprom);
+ if (rc) {
+ fprintf(stderr, "Failed to parse %s EEPROM\n", fn ? fn : "default");
+ return -1;
+ }
+
+ printf("Wedge EEPROM %s:\n", fn ? fn : "");
+ printf("Version: %d\n", eeprom.fbw_version);
+ printf("Product Name: %s\n", eeprom.fbw_product_name);
+ printf("Product Part Number: %s\n", eeprom.fbw_product_number);
+ printf("System Assembly Part Number: %s\n", eeprom.fbw_assembly_number);
+ printf("Facebook PCB Part Number: %s\n", eeprom.fbw_facebook_pcb_number);
+ printf("ODM PCB Part Number: %s\n", eeprom.fbw_odm_pcb_number);
+ printf("ODM PCB Serial Number: %s\n", eeprom.fbw_odm_pcb_serial);
+ printf("Product Production State: %d\n", eeprom.fbw_production_state);
+ printf("Product Version: %d\n", eeprom.fbw_product_version);
+ printf("Product Sub-Version: %d\n", eeprom.fbw_product_subversion);
+ printf("Product Serial Number: %s\n", eeprom.fbw_product_serial);
+ printf("Product Asset Tag: %s\n", eeprom.fbw_product_asset);
+ printf("System Manufacturer: %s\n", eeprom.fbw_system_manufacturer);
+ printf("System Manufacturing Date: %s\n",
+ eeprom.fbw_system_manufacturing_date);
+ printf("PCB Manufacturer: %s\n", eeprom.fbw_pcb_manufacturer);
+ printf("Assembled At: %s\n", eeprom.fbw_assembled);
+ printf("Local MAC: %X:%X:%X:%X:%X:%X\n",
+ eeprom.fbw_local_mac[0], eeprom.fbw_local_mac[1],
+ eeprom.fbw_local_mac[2], eeprom.fbw_local_mac[3],
+ eeprom.fbw_local_mac[4], eeprom.fbw_local_mac[5]);
+ printf("Extended MAC Base: %X:%X:%X:%X:%X:%X\n",
+ eeprom.fbw_mac_base[0], eeprom.fbw_mac_base[1],
+ eeprom.fbw_mac_base[2], eeprom.fbw_mac_base[3],
+ eeprom.fbw_mac_base[4], eeprom.fbw_mac_base[5]);
+ printf("Extended MAC Address Size: %d\n", eeprom.fbw_mac_size);
+ printf("Location on Fabric: %s\n", eeprom.fbw_location);
+ printf("CRC8: 0x%x\n", eeprom.fbw_crc8);
+
+ return 0;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/libwedge-eeprom_0.1.bb b/meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/libwedge-eeprom_0.1.bb
new file mode 100644
index 0000000..781cf76
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/libwedge-eeprom_0.1.bb
@@ -0,0 +1,26 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+SUMMARY = "Wedge EEPROM Library"
+DESCRIPTION = "library for wedge eeprom"
+SECTION = "base"
+PR = "r1"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://wedge_eeprom.c;beginline=4;endline=16;md5=da35978751a9d71b73679307c4d296ec"
+
+SRC_URI = "file://lib \
+ "
+
+DEPENDS += "fbutils"
+
+S = "${WORKDIR}/lib"
+
+do_install() {
+ install -d ${D}${libdir}
+ install -m 0644 libwedge_eeprom.so ${D}${libdir}/libwedge_eeprom.so
+
+ install -d ${D}${includedir}/facebook
+ install -m 0644 wedge_eeprom.h ${D}${includedir}/facebook/wedge_eeprom.h
+}
+
+FILES_${PN} = "${libdir}/libwedge_eeprom.so"
+FILES_${PN}-dbg = "${libdir}/.debug"
+FILES_${PN}-dev = "${includedir}/facebook/wedge_eeprom.h"
diff --git a/meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/wedge-eeprom_0.1.bb b/meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/wedge-eeprom_0.1.bb
new file mode 100644
index 0000000..51bdbb9
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/wedge-eeprom/wedge-eeprom_0.1.bb
@@ -0,0 +1,21 @@
+# Copyright 2014-present Facebook. All Rights Reserved.
+SUMMARY = "Wedge EEPROM Utilities"
+DESCRIPTION = "Util for wedge eeprom"
+SECTION = "base"
+PR = "r1"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://weutil.c;beginline=4;endline=16;md5=da35978751a9d71b73679307c4d296ec"
+
+SRC_URI = "file://utils \
+ "
+
+S = "${WORKDIR}/utils"
+
+do_install() {
+ install -d ${D}${bindir}
+ install -m 0755 weutil ${D}${bindir}/weutil
+}
+
+DEPENDS += "libwedge-eeprom"
+
+FILES_${PN} = "${bindir}"
OpenPOWER on IntegriCloud