diff options
Diffstat (limited to 'meta-facebook')
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}" |