/*
**  %Z% %I% %W% %G% %U%
**
**  ZZ_Copyright_BEGIN
**
**
**  Licensed Materials - Property of IBM
**
**  IBM Linear Tape File System Single Drive Edition Version 2.2.0.2 for Linux and Mac OS X
**
**  Copyright IBM Corp. 2010, 2014
**
**  This file is part of the IBM Linear Tape File System Single Drive Edition for Linux and Mac OS X
**  (formally known as IBM Linear Tape File System)
**
**  The IBM Linear Tape File System Single Drive Edition for Linux and Mac OS X is free software;
**  you can redistribute it and/or modify it under the terms of the GNU Lesser
**  General Public License as published by the Free Software Foundation,
**  version 2.1 of the License.
**
**  The IBM Linear Tape File System Single Drive Edition for Linux and Mac OS X 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 Lesser General Public License for more details.
**
**  You should have received a copy of the GNU Lesser General Public
**  License along with this library; if not, write to the Free Software
**  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
**  or download the license from <http://www.gnu.org/licenses/>.
**
**
**  ZZ_Copyright_END
**
*************************************************************************************
**
** COMPONENT NAME:  IBM Linear Tape File System
**
** FILE NAME:       reed_solomon_crc.c
**
** DESCRIPTION:     Implements Reed-Solomon GF(256) CRC usd by LTO based drives
**
** AUTHOR:          Atsushi Abe
**                  IBM Yamato, Japan
**                  PISTE@jp.ibm.com
**
*************************************************************************************
*/

#define __reed_solomon_crc_c

#include <inttypes.h>
#include <sys/types.h>

#include "libltfs/ltfslogging.h"
#include "libltfs/ltfs_endian.h"

static const uint32_t rs_gf256_table[256] =
{
	0x00000000,0x38CF3801,0x70837002,0x484C4803,0xE01BE004,0xD8D4D805,
	0x90989006,0xA857A807,0xDD36DD08,0xE5F9E509,0xADB5AD0A,0x957A950B,
	0x3D2D3D0C,0x05E2050D,0x4DAE4D0E,0x7561750F,0xA76CA710,0x9FA39F11,
	0xD7EFD712,0xEF20EF13,0x47774714,0x7FB87F15,0x37F43716,0x0F3B0F17,
	0x7A5A7A18,0x42954219,0x0AD90A1A,0x3216321B,0x9A419A1C,0xA28EA21D,
	0xEAC2EA1E,0xD20DD21F,0x53D85320,0x6B176B21,0x235B2322,0x1B941B23,
	0xB3C3B324,0x8B0C8B25,0xC340C326,0xFB8FFB27,0x8EEE8E28,0xB621B629,
	0xFE6DFE2A,0xC6A2C62B,0x6EF56E2C,0x563A562D,0x1E761E2E,0x26B9262F,
	0xF4B4F430,0xCC7BCC31,0x84378432,0xBCF8BC33,0x14AF1434,0x2C602C35,
	0x642C6436,0x5CE35C37,0x29822938,0x114D1139,0x5901593A,0x61CE613B,
	0xC999C93C,0xF156F13D,0xB91AB93E,0x81D5813F,0xA6ADA640,0x9E629E41,
	0xD62ED642,0xEEE1EE43,0x46B64644,0x7E797E45,0x36353646,0x0EFA0E47,
	0x7B9B7B48,0x43544349,0x0B180B4A,0x33D7334B,0x9B809B4C,0xA34FA34D,
	0xEB03EB4E,0xD3CCD34F,0x01C10150,0x390E3951,0x71427152,0x498D4953,
	0xE1DAE154,0xD915D955,0x91599156,0xA996A957,0xDCF7DC58,0xE438E459,
	0xAC74AC5A,0x94BB945B,0x3CEC3C5C,0x0423045D,0x4C6F4C5E,0x74A0745F,
	0xF575F560,0xCDBACD61,0x85F68562,0xBD39BD63,0x156E1564,0x2DA12D65,
	0x65ED6566,0x5D225D67,0x28432868,0x108C1069,0x58C0586A,0x600F606B,
	0xC858C86C,0xF097F06D,0xB8DBB86E,0x8014806F,0x52195270,0x6AD66A71,
	0x229A2272,0x1A551A73,0xB202B274,0x8ACD8A75,0xC281C276,0xFA4EFA77,
	0x8F2F8F78,0xB7E0B779,0xFFACFF7A,0xC763C77B,0x6F346F7C,0x57FB577D,
	0x1FB71F7E,0x2778277F,0x51475180,0x69886981,0x21C42182,0x190B1983,
	0xB15CB184,0x89938985,0xC1DFC186,0xF910F987,0x8C718C88,0xB4BEB489,
	0xFCF2FC8A,0xC43DC48B,0x6C6A6C8C,0x54A5548D,0x1CE91C8E,0x2426248F,
	0xF62BF690,0xCEE4CE91,0x86A88692,0xBE67BE93,0x16301694,0x2EFF2E95,
	0x66B36696,0x5E7C5E97,0x2B1D2B98,0x13D21399,0x5B9E5B9A,0x6351639B,
	0xCB06CB9C,0xF3C9F39D,0xBB85BB9E,0x834A839F,0x029F02A0,0x3A503AA1,
	0x721C72A2,0x4AD34AA3,0xE284E2A4,0xDA4BDAA5,0x920792A6,0xAAC8AAA7,
	0xDFA9DFA8,0xE766E7A9,0xAF2AAFAA,0x97E597AB,0x3FB23FAC,0x077D07AD,
	0x4F314FAE,0x77FE77AF,0xA5F3A5B0,0x9D3C9DB1,0xD570D5B2,0xEDBFEDB3,
	0x45E845B4,0x7D277DB5,0x356B35B6,0x0DA40DB7,0x78C578B8,0x400A40B9,
	0x084608BA,0x308930BB,0x98DE98BC,0xA011A0BD,0xE85DE8BE,0xD092D0BF,
	0xF7EAF7C0,0xCF25CFC1,0x876987C2,0xBFA6BFC3,0x17F117C4,0x2F3E2FC5,
	0x677267C6,0x5FBD5FC7,0x2ADC2AC8,0x121312C9,0x5A5F5ACA,0x629062CB,
	0xCAC7CACC,0xF208F2CD,0xBA44BACE,0x828B82CF,0x508650D0,0x684968D1,
	0x200520D2,0x18CA18D3,0xB09DB0D4,0x885288D5,0xC01EC0D6,0xF8D1F8D7,
	0x8DB08DD8,0xB57FB5D9,0xFD33FDDA,0xC5FCC5DB,0x6DAB6DDC,0x556455DD,
	0x1D281DDE,0x25E725DF,0xA432A4E0,0x9CFD9CE1,0xD4B1D4E2,0xEC7EECE3,
	0x442944E4,0x7CE67CE5,0x34AA34E6,0x0C650CE7,0x790479E8,0x41CB41E9,
	0x098709EA,0x314831EB,0x991F99EC,0xA1D0A1ED,0xE99CE9EE,0xD153D1EF,
	0x035E03F0,0x3B913BF1,0x73DD73F2,0x4B124BF3,0xE345E3F4,0xDB8ADBF5,
	0x93C693F6,0xAB09ABF7,0xDE68DEF8,0xE6A7E6F9,0xAEEBAEFA,0x962496FB,
	0x3E733EFC,0x06BC06FD,0x4EF04EFE,0x763F76FF
};

static inline void enc4(const unsigned char in, uint32_t *reg)
{
	*reg = (*reg << 8) ^ rs_gf256_table[in ^ (*reg >> 24)];
}

void *memcpy_rs_gf256_enc(void *dest, const void *src, size_t n)
{
	unsigned char *dest_cur = (unsigned char *)dest, *src_cur = (unsigned char *)src;
	uint32_t reg = 0;
	size_t i;

	for(i = 0; i < n; i++){
		/* Inject CRC value to the end of the destination buffer */
		*dest_cur = *src_cur;

		/* Calculate CRC */
		enc4(*src_cur, &reg);

		/* Increment pointers */
		dest_cur++;
		src_cur++;
	}

	/* Inject CRC value to the end of the destination buffer */
	ltfs_u32tobe(dest_cur, reg);
	ltfsmsg(LTFS_DEBUG, "12203D", "encode", n, reg);

	return dest;
}

void rs_gf256_enc(void *buf, size_t n)
{
	unsigned char *buf_cur = (unsigned char *)buf;
	unsigned char *reg_cur = (unsigned char *)buf + n;
	uint32_t reg = 0;
	size_t i;

	for(i = 0; i < n; i++){
		/* Calculate CRC */
		enc4(*buf_cur, &reg);
		buf_cur++;
	}

	/* Inject CRC value */
	ltfs_u32tobe(reg_cur, reg);
	ltfsmsg(LTFS_DEBUG, "12203D", "encode", n, reg);

	return;
}

int memcpy_rs_gf256_check(void *dest, const void *src, size_t n)
{
	unsigned char *dest_cur = (unsigned char *)dest, *src_cur = (unsigned char *)src;
	uint32_t reg = 0, crc;
	size_t i;

	for(i = 0; i < n; i++){
		/* Inject CRC value to the end of the destination buffer */
		*dest_cur = *src_cur;

		/* Calculate CRC */
		enc4(*src_cur, &reg);

		/* Increment pointers */
		dest_cur++;
		src_cur++;
	}

	/* Check CRC value in the end of the source buffer */
	crc = ltfs_betou32(src_cur);
	if(crc != reg) {
		ltfsmsg(LTFS_ERR, "12202E", n, reg, crc);
		return -1;
	}
	ltfsmsg(LTFS_DEBUG, "12203D", "check", n, crc);

	return n;
}

int rs_gf256_check(void *buf, size_t n)
{
	unsigned char *buf_cur = (unsigned char *)buf;
	uint32_t reg = 0, crc;
	size_t i;

	for(i = 0; i < n; i++){
		/* Calculate CRC */
		enc4(*buf_cur, &reg);
		buf_cur++;
	}

	/* Check CRC value in the end of the buffer */
	crc = ltfs_betou32(buf_cur);
	if(crc != reg) {
		ltfsmsg(LTFS_ERR, "12202E", n, reg, crc);
		return -1;
	}
	ltfsmsg(LTFS_DEBUG, "12203D", "check", n, crc);

	return n;
}
