/**
 * Module: 		StorageControllers.ycp
 *
 * Authors:		Klaus Kaempf <kkaempf@suse.de> (initial)
 *
 * Purpose:
 * This module does all floppy disk related stuff:
 * - Detect the floppy devices
 *
 * SCR: Read(.probe.storage)
 *
 * $Id: StorageControllers.ycp 19964 2004-10-26 15:37:35Z fehr $
 */
{
    module "StorageControllers";

    import "Arch";
    import "Mode";
    import "ModulesConf";
    import "ModuleLoading";
    import "HwStatus";
    import "Initrd";
    import "Kernel";
    import "Storage";
    import "StorageDevices";
    import "Label";
    import "Popup";
    import "Linuxrc";

    textdomain "storage";

    // list of loaded modules and arguments 
    // needed for modules.conf writing
    // must be kept in order (-> no map !)
    // must be searchable (-> separate lists for names and args)

    global list moduleNames = [];
    global list moduleArgs = [];
    global list moduleIDs = [];		//unique_id from probing

    list<list> ModToInitrdLx = [];
    list<list> ModToInitrd = [];

    global string dasdParam = "";

    // --------------------------------------------------------------
    // local values

    list controllers = [];	// set by "Probe"

    // remember postinstall modules (like imm and ppa)
    //   which must be installed *last*

    list postinstall_names = [ "imm", "ppa" ];

    // postinstall modules information
    // list of [ boolean modprobe,
    //		 string module_name,
    //		 string module_arg,
    //		 string vendor,
    //		 string device]

    list<list> postinstall_info = [];

    // --------------------------------------------------------------


define list GetDasdList()
    ``{
    list ret = [];
    list<map> dasd = (list<map>) SCR::Read(.proc.dasddev);
    y2milestone( "dasd=%1", dasd );
    foreach( map entry, dasd,
	``{
	term a = `item( `id(entry["name"]:"") );
	a = add( a, entry["name"]:"" );
	a = add( a, entry["address"]:"" );
	a = add( a, entry["state"]:"" );
	ret = add( ret, a );
	});
    return( ret );
    }

define string S390DasdParams()
    ``{
    SCR::UnmountAgent (.proc.modules);
    map mod = (map)SCR::Read(.proc.modules);
    boolean mod_loaded = haskey(mod, "dasd_mod");
    y2milestone( "mod %1", mod );
    string param = (string)SCR::Read(.etc.install_inf.DASD_Parameter);
    if( param==nil )
	{
	param = "";
	}
    y2milestone( "param %1 size:%2", param, size(param) );
    boolean do_load = !mod_loaded || size(param)==0;
    if( do_load )
	{
	string okb = Label::AcceptButton();
	// button text
	string loadb = _("&Load Module");
	// popup text
	string text = sformat( 
_("Here, enter the parameters with which to load
the dasd module, such as dasd=FD00-FD0F,FD40.
Press \"%1\" to load the 
module.
If the table shows the correct DASDs available,
press \"%2\".
"), deletechars(loadb,"&"), deletechars(okb,"&") );

	list cont = GetDasdList();
	UI::OpenDialog(
	    `opt(`decorated),
	    `HBox(
	        `HSpacing(1),
	        `VBox(
		    `VSpacing(0.5),
		    // heading text
		    `Left(`Heading(_("DASD Module Parameter Setting"))),
		    `VSpacing(0.2),
		    `Left(`Label(text)),
		    `VSpacing(0.2),
		    `HBox(
			// label text
			`Left(`TextEntry(`id(`param), _("&DASD Parameter"), 
					 param )),
			`VSpacing(1),
			`Bottom(`PushButton(`id(`load), loadb ))
			 ),
		    `VSpacing(1),
		    `HBox( 
			`Table( `id(`dd_table), 
				// heading text
			        `header( _("DASD Name"), 
				// heading text
				         _("DASD Address"), 
				// heading text
				         _("Status") ),
				cont ),
			`VSpacing(10)
			 ),
		    `VSpacing(1),
		    `HBox(
			`HStretch(),
			`PushButton(`id(`abort), Label::AbortButton() ),
			`HStretch(),
			`PushButton(`id(`accept), Label::AcceptButton() ),
			`HStretch()
			 ),
		    `VSpacing(0.5)
		    ),
	        `HSpacing(1)
		 ));
	UI::SetFocus(`id(`param));
	symbol ret = `none;
	do
	    {
	    ret = (symbol)UI::UserInput();
	    y2milestone( "ret = %1", ret );
	    if( ret == `load )
		{
		if( mod_loaded )
		    {
		    SCR::Execute( .target.bash, 
		                  "/sbin/rmmod dasd_eckd_mod dasd_fba_mod dasd_mod" );
		    }
		param = (string)UI::QueryWidget( `id(`param), `Value );
		y2milestone( "param %1", param );
		boolean ret = (boolean)SCR::Execute(.target.modprobe, "dasd_mod", param );
		mod_loaded = true;
		y2milestone( "ret=%1", ret );
		if( !ret )
		    {
		    // popup text
		    Popup::Error( sformat(_("Error loading module dasd with 
parameter: %1"), param ));
		    }
		SCR::Execute(.target.modprobe, "dasd_eckd_mod", "" );
		SCR::Execute(.target.modprobe, "dasd_fba_mod", "" );
		cont = GetDasdList();
		UI::ChangeWidget( `id(`dd_table), `Items, cont );
		}
	    else if( ret == `accept && size(cont)==0 )
		{
		ret = `continue;
		}
	    if( (ret == `accept||ret == `abort) && size(cont)==0 )
		{
		// popup text
		Popup::Error( _("There are no DASD devices active.
It is not possible to install without
active DASD devices."));
		}
	    }
	while( ret != `accept && ret != `abort );
	UI::CloseDialog();
	}
    y2milestone( "ret %1", param );
    return( param );
    }

    /**
     * Probe storage controllers
     * probing, loading modules
     *
     * @returns integer	number of controllers, 0 = none found 
     */
global define integer Probe ()
    ``{
    y2milestone( "StorageControllers::Probe()" );

    // probe 'storage' list

    controllers = (list<map>)SCR::Read (.probe.storage);

    if (!Arch::s390 () && size (controllers) == 0)
	{
	y2milestone("no controllers");
	}
    return size (controllers);
    }



// --------------------------------------------------------------

// parportInitialize

define void parportInitialize ()
    ``{
    // post-load parport module
    if ((size (postinstall_info) == 0) || Mode::test ())
	{
	return;
	}

    boolean parport_loaded = false;
    boolean parport_pc_loaded = false;

    foreach (list postlist, postinstall_info,
	``{
	boolean post_modprobe = postlist[0]:false;
	string post_name      = postlist[1]:"";
	string post_arg	      = postlist[2]:"";
	string post_vendor    = postlist[3]:"";
	string post_device    = postlist[4]:"";
	if (!parport_loaded)
	    {
	    if( `ok == ModuleLoading::Load( "parport", "", post_vendor, 
	                                    post_device, Linuxrc::manual (), true ))
		{
		ModulesConf::ModuleArgs ("parport", "");
		parport_loaded = true;
		}
	    }
	if( !parport_pc_loaded)
	    {
	    if( `ok == ModuleLoading::Load( "parport_pc", "", post_vendor, 
	                                    post_device, Linuxrc::manual (), true ))
		{
		ModulesConf::ModuleArgs ("parport_pc", "");
		parport_pc_loaded = true;
		}
	    }

	if( `ok == ModuleLoading::Load( post_name, post_arg, post_vendor, 
	                                post_device, Linuxrc::manual (), post_modprobe))
	    {
	    ModulesConf::ModuleArgs (post_name, post_arg);
	    Kernel::AddModuleToLoad(post_name);

	    moduleNames = add( moduleNames, post_name );
	    moduleArgs  = add( moduleArgs, post_arg );
	    y2milestone( "moduleNames %1", moduleNames );
	    y2milestone( "moduleArgs %1", moduleArgs );
	    }
	});
    return;
    }

// start a controller (by loading its module)
// return true if all necessary modules were actually loaded
// return false if loading failed or was not necessary at all

define boolean startController (map controller)
    ``{
    // check module information
    // skip controller if no module info available

    list<map> module_drivers = controller["drivers"]:[];
    string module_id = controller["unique_key"]:"";

    if (size (module_drivers) == 0)
	return false;

    // get list of modules from /proc/modules
    SCR::UnmountAgent (.proc.modules);
    map loaded_modules = (map)SCR::Read(.proc.modules);		

    // loop through all drivers checking if one is already active
    // or if one is already listed in /proc/modules

    boolean already_active = false;
    foreach (map modulemap, module_drivers,
	``{
	if( modulemap["active"]:true ||
	    size( loaded_modules[modulemap["modules",0,0]:""]:$[] ) > 0 )
	    {
	    already_active = true;
	    if( modulemap["active"]:true )
		{
		ModToInitrdLx = add( ModToInitrdLx, 
		                     [ modulemap["modules",0,0]:"",
				       modulemap["modules",0,1]:"" ] );
		y2milestone( "ModToInitrdLx %1", ModToInitrdLx );
		y2milestone( "ModToInitrd %1", ModToInitrd );
		}
	    }
	});

    // save unique key for HwStatus::Set()
    string unique_key = controller["unique_key"]:"";

    if (already_active)
	{
	HwStatus::Set (unique_key, `yes);
	return false;
	}

    boolean stop_loading = false;
    boolean one_module_failed = false;

    // loop through all drivers defined for this controller
    // break after first successful load
    //   no need to check "active", already done before !
    foreach (map modulemap, module_drivers,
	``{
	y2milestone ("modulemap: %1", modulemap);
	boolean module_modprobe = modulemap["modprobe"]:false;

	boolean all_modules_loaded = true;

	if (!stop_loading)
	    {
	    foreach (list module_entry, modulemap["modules"]:[],
		``{
		string module_name = module_entry[0]:"";
		string module_args = module_entry[1]:"";

		// remember postinstall modules

		if( contains( postinstall_names, module_name) )
		    {
		    y2milestone("name: %1 args: %2", module_name, module_args);

		    list vendor_device = 
			ModuleLoading::prepareVendorDeviceInfo(controller);

		    // save data for parportInitialize
		    postinstall_info = 
			add( postinstall_info,
			     [module_modprobe, module_name, module_args,
			      vendor_device[0]:"", vendor_device[1]:""]);
		    }
		else
		    {
		    // load module if not yet loaded
		    if( !contains (moduleNames, module_name) )
			{
			symbol load_result = `ok;
			if( Linuxrc::manual () )
			    {
			    list vendor_device = 
				ModuleLoading::prepareVendorDeviceInfo(controller);
			    load_result = 
				ModuleLoading::Load( module_name, module_args,
						     vendor_device[0]:"",
						     vendor_device[1]:"", true,
						     module_modprobe );
			    }
			else
			    {
			    load_result = 
				ModuleLoading::Load( module_name, module_args,
						     "", "", false, 
						     module_modprobe );
			    }
			y2milestone( "load_result %1", load_result );

			if (load_result == `fail)
			    {
			    all_modules_loaded = false;
			    }
			else if (load_result == `dont)
			    {
			    all_modules_loaded = true;
			    }
			else		// load ok
			    {
			    // save data for modules.conf writing
			    moduleNames = add (moduleNames, module_name);
			    moduleArgs  = add (moduleArgs, module_args);
			    moduleIDs  = add (moduleIDs, module_id);

			    y2milestone( "moduleNames %1", moduleNames );
			    y2milestone( "moduleArgs %1", moduleArgs );
			    y2milestone( "moduleIDs %1", moduleIDs );
			    ModToInitrd = add( ModToInitrd, [ module_name,
			                                      module_args ] );
			    y2milestone( "ModToInitrd %1", ModToInitrd );
			    y2milestone( "ModToInitrdLx %1", ModToInitrdLx );
			    }
			} // not yet loaded
		    } // not postinstall

		// break out of module load loop if one module failed

		if (!all_modules_loaded)
		    {
		    one_module_failed = true;
		    }
		}); // foreach module of current driver info

	    } // stop_loading

	// break out of driver load loop if all modules of
	//   the current driver loaded successfully

	if (all_modules_loaded)
	    {
	    stop_loading = true;
	    }

	});  // foreach driver

    HwStatus::Set (unique_key, one_module_failed?`no:`yes);

    return (!one_module_failed);
    }


// local function to go through list of resources (list of maps)
// checking if '"active":true' is set.

define boolean anyActive (list<map> resources)
``{
    boolean active = size(resources)==0;

    foreach (map res, resources,
    ``{
	if (res["active"]:false)
	    active = true;
    });

    return active;
}

/**
 * Start storage related USB and FireWire stuff
 *
 */
global define void StartHotplugStorage ()
    ``{
    import "Hotplug";

    // If USB capable, there might be an usb storage device (i.e. ZIP)
    // activate the module _last_ since it might interfere with other
    // controllers (i.e. having usb-storage first might result in
    // /dev/sda == zip which is bad if the zip drive is removed :-}).

    if (Hotplug::haveUSB)
    {
	// if loading of usb-storage is successful, re-probe for floppies
	// again since USB ZIP drives are regarded as floppies.

	if (ModuleLoading::Load ("usb-storage", "", "", "USB Storage", Linuxrc::manual (), true) == `ok)
	{
	    StorageDevices::FloppyReady();
	}
    }

    if (Hotplug::haveFireWire)
    {
	// load sbp2
	ModuleLoading::Load ("sbp2", "", "", "SBP2 Protocol", Linuxrc::manual (), true);
    }

    return;
}

/**
 * @param	none
 * @returns void
 * Init storage controllers (module loading)
 * Must have called StorageControllers::probe() before !
// O: list of [ loaded modules, module argument ]
 */

global define void Initialize ()
    ``{
    moduleNames = [];
    moduleArgs = [];
    map controller = $[];
    integer cindex = 0;
    list ioresources = [];

    ModToInitrd = [];
    ModToInitrdLx = [];

    y2milestone("controllers: %1", controllers);

    // loop through all controller descriptions from hwprobe

    // use while(), continue not allowed in foreach()
    while( !Arch::s390 () && cindex < size(controllers) )
	{
	controller = controllers[cindex]:$[];
	y2milestone( "controller %1", controller );

	cindex = cindex + 1;

	// check BIOS resources on 'wintel' compatible systems
	if (Arch::board_wintel ())
	{
	    // for every controller it is checked whether
	    // the controller is disabled in BIOS
	    // this is done by checking for an active IO or memory resource

	    if( !(anyActive (controller["resource", "io"]:[])
		  ||  anyActive (controller["resource", "mem"]:[])))
	    {
		y2milestone( "controller %1 disabled in BIOS", 
	        		controller["device"]:"" );
		// continue if disabled in BIOS
		continue;
	    }
	}

	startController(controller);
	} // while (controller)

    y2milestone( "Initialize ModToInitrdLx %1 ModToInitrd %2 ", ModToInitrdLx,
                 ModToInitrd );
    ModToInitrd = (list<list>)union( ModToInitrdLx, ModToInitrd );
    y2milestone( "Initialize ModToInitrd %1", ModToInitrd );

    if( size(ModToInitrd)>1 )
	{
	list<string> ls = filter( string s, 
	                          splitstring( (string)SCR::Read( .etc.install_inf.InitrdModules ), 
				               " " ),
				  ``(size(s)>0) );
	y2milestone( "Initialize ls=%1", ls );
	integer i = 0;
	map lrmods = listmap( string s, ls, ``{i=i+1; return( $[ s : i ] );});
	y2milestone( "Initialize lrmods=%1", lrmods );
	i = 0;
	while( i<size(ModToInitrd) )
	    {
	    integer j=i+1;
	    while( j<size(ModToInitrd) )
		{
		if( haskey( lrmods, ModToInitrd[i,0]:"" ) &&
		    haskey( lrmods, ModToInitrd[j,0]:"" ) &&
		    lrmods[ModToInitrd[i,0]:""]:0 > 
			lrmods[ModToInitrd[j,0]:""]:0 )
		    {
		    list tmp = ModToInitrd[i]:[];
		    ModToInitrd[i] = ModToInitrd[j]:[];
		    ModToInitrd[j] = tmp;
		    }
		j = j+1;
		}
	    i=i+1;
	    }
	y2milestone( "Initialize ModToInitrd %1", ModToInitrd );
	}
    foreach( list s, ModToInitrd,
	``{
	Initrd::AddModule( s[0]:"", s[1]:"" );
	});

    if( Arch::s390 () )
	{
	UI::CloseDialog();
	WFM::call( "s390-disk-controller" );
	string cmd = "/sbin/dasd_reload";
	y2milestone( "cmd %1 ret %2", cmd,
	             SCR::Execute( .target.bash_output, cmd ));
	UI::OpenDialog( `Empty() );
	}

    y2milestone( "postinstall_info %1", postinstall_info );

    parportInitialize ();

    // load all raid personalities
    SCR::Execute(.target.modprobe, "raid0", "" );
    SCR::Execute(.target.modprobe, "raid1", "" );
    SCR::Execute(.target.modprobe, "raid5", "" );
    SCR::Execute(.target.modprobe, "raid6", "" );
    SCR::Execute(.target.modprobe, "multipath", "" );

    // init sw-RAID subsystem in the kernel
    SCR::Execute( .target.bash, "/sbin/raidautorun");

    StartHotplugStorage();

    y2milestone("all controllers initialized" );
    StorageDevices::InitDone();

    return;

}; // Initialize ()

}
