#!/usr/bin/perl

#   Copyright (C) 2010 Mauro Carvalho Chehab
#
#   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, 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.
#
# This small script parses register dumps generated by cx231xx driver
# with debug options enabled, generating a source code with the results
# of the dump.
#
# To use it, you may modprobe cx231xx with reg_debug=1, and do:
# dmesg | ./parse_em28xx.pl
#
# Also, there are other utilities that produce similar outputs, and it
# is not hard to parse some USB analyzers log into the expected format.
#

use strict;

my %cfg_reg_map = (
	0x0 => "BOARD_CFG_STAT",
	0x4 => "TS_MODE_REG",
	0x8 => "TS1_CFG_REG",
	0xc => "TS1_LENGTH_REG",
	0x10 => "TS2_CFG_REG",
	0x14 => "TS2_LENGTH_REG",
	0x18 => "EP_MODE_SET",
	0x1c => "CIR_PWR_PTN1",
	0x20 => "CIR_PWR_PTN2",
	0x24 => "CIR_PWR_PTN3",
	0x28 => "CIR_PWR_MASK0",
	0x2c => "CIR_PWR_MASK1",
	0x30 => "CIR_PWR_MASK2",
	0x34 => "CIR_GAIN",
	0x38 => "CIR_CAR_REG",
	0x40 => "CIR_OT_CFG1",
	0x44 => "CIR_OT_CFG2",
	0x68 => "GBULK_BIT_EN",
	0x74 => "PWR_CTL_EN",
);

sub parse_i2c($$$$$$)
{
	my $reqtype = shift;
	my $req = shift;
	my $wvalue = shift;
	my $windex = shift;
	my $wlen = shift;
	my $payload = shift;

	my $daddr = $wvalue >> 9;
	my $reserved = ($wvalue >>6 ) & 0x07;
	my $period = ($wvalue >> 4) & 0x03;
	my $addr_len = ($wvalue >> 2) & 0x03;
	my $nostop = ($wvalue >>1) & 0x01;
	my $sync = $wvalue & 0x01;

	if ($nostop) {
		$nostop="nostop ";
	} else {
		$nostop="";
	}
	if ($sync) {
		$sync="sync ";
	} else {
		$sync="";
	}
	my $type;
	my $i2c_channel;
	if ($reqtype > 128) {
		$type = "IN ";
		$i2c_channel = $req - 4;
	} else {
		$type = "OUT";
		$i2c_channel = $req;
	}
	if ($period == 0) {
		$period = "1Mbps";
	} elsif ($period == 1) {
		$period = "400kbps";
	} elsif ($period == 2) {
		$period = "100kbps";
	} else {
		$period = "???kbps";
	}
	printf("$type i2c channel#%d daddr 0x%02x %s addr_len %d %s%slen %d = ",
		$i2c_channel, $daddr, $period, $addr_len, $nostop, $sync,
		$wlen);
	if ($addr_len == 1) {
		printf("(saddr)%02x ", $windex & 0xff);
	} elsif ($addr_len == 2) {
		printf("(saddr)%04x ", $windex);
	}
	printf("$payload\n");
}

sub parse_gpio($$$$$$)
{
	my $reqtype = shift;
	my $req = shift;
	my $wvalue = shift;
	my $windex = shift;
	my $wlen = shift;
	my $payload = shift;

	my $type;
	if ($req == 8) {
		$type .= "SET gpio";
	} elsif ($req == 9) {
		$type .= "GET gpio";
	} elsif ($req == 0xa) {
		$type .= "SET gpie";
	} elsif ($req == 0xb) {
		$type .= "GET gpie";
	}

	my $gpio_bit = ($wvalue << 16) | $windex;

	$payload =~ s/([a-f\d].)\s([a-f\d].)\s([a-f\d].)\s([a-f\d].)/0x$4$3$2$1/;

	printf("$type: Reqtype %3d Req %3d len %d wvalue 0x%04x windex 0x%02x => direction=0x%04x val = %s\n",
		$reqtype, $req, $wlen, $wvalue, $windex, $gpio_bit, $payload);
}

while (<>) {
	tr/A-F/a-f/;
	if (m/([4c]0) ([0-9a-f].) ([0-9a-f].) ([0-9a-f].) ([0-9a-f].) ([0-9a-f].) ([0-9a-f].) ([0-9a-f].)[\<\>\s]+(.*)/) {
		my $reqtype = hex($1);
		my $req = hex($2);
		my $wvalue = hex("$4$3");
		my $windex = hex("$6$5");
		my $wlen = hex("$8$7");
		my $payload = $9;

		if ($reqtype > 128 && (($req >= 4) && ($req <= 6))) {
			parse_i2c($reqtype, $req, $wvalue, $windex, $wlen, $payload);
		} elsif ($req < 3) {
			parse_i2c($reqtype, $req, $wvalue, $windex, $wlen, $payload);
		} elsif ($req >= 8 && $req <= 0xb) {
			parse_gpio($reqtype, $req, $wvalue, $windex, $wlen, $payload);
		} elsif ($req == 0xc) {
			my $cfg_len;
			if ($wvalue == 1) {
				$cfg_len = 1;
			} elsif($wvalue == 3) {
				$cfg_len = 2;
			} elsif($wvalue == 7) {
				$cfg_len = 3;
			} elsif($wvalue == 0xf) {
				$cfg_len = 4;
			} else {
				printf("Invalid get len for ");
				printf("Reqtype: %3d, Req %3d, wValue: 0x%04x, wIndex 0x%04x, wlen %d: %s\n",
					$reqtype, $req, $wvalue, $windex, $wlen, $payload);
			}

			if ($cfg_len) {
				my $reg = $windex;
				$reg = $cfg_reg_map{$windex} if defined($cfg_reg_map{$windex});

				printf "cx231xx_write_ctrl_reg(dev, $reg, $payload, $cfg_len);\n";
			}
		} elsif ($req == 0xd) {
			my $cfg_len;
			if ($wvalue == 1) {
				$cfg_len = 1;
			} elsif($wvalue == 3) {
				$cfg_len = 2;
			} elsif($wvalue == 7) {
				$cfg_len = 3;
			} elsif($wvalue == 0xf) {
				$cfg_len = 4;
			} else {
				printf("Invalid get len for ");
				printf("Reqtype: %3d, Req %3d, wValue: 0x%04x, wIndex 0x%04x, wlen %d: %s\n",
					$reqtype, $req, $wvalue, $windex, $wlen, $payload);
			}
			if ($cfg_len) {
				my $reg = $windex;
				$reg = $cfg_reg_map{$windex} if defined($cfg_reg_map{$windex});

				printf "cx231xx_read_ctrl_reg(dev, $reg, $cfg_len);\t\t/* read %s */\n",
					$payload;
			}
		} else {
			printf("Reqtype: %3d, Req %3d, wValue: 0x%04x, wIndex 0x%04x, wlen %d: %s\n",
				$reqtype, $req, $wvalue, $windex, $wlen, $payload);
		}
	}
}
