LINUX.ORG.RU

Простой вопрос знатокам в СИ

 ,


0

1

Нашел программу которая контролирует работу светодиодов сетевой платы на Banana Pi, вот код:

/*
 * Configure ethernet adapter LEDs on BananaPi.
 *
 * Based on Roman Reichel's code and David A. Hinds's mii-tool <dhinds@pcmcia.sourceforge.org>
 * More information on http://forum.lemaker.org/thread-1057-1-1-switch_off_the_leds_.html
 *
 * Copyright 2014 Laurent Faillie
 *
 * 		BananaLEDd is covered by 
 *      Creative Commons Attribution-NonCommercial 3.0 License
 *      (http://creativecommons.org/licenses/by-nc/3.0/) 
 *      Consequently, you're free to use if for personal or non-profit usage,
 *      professional or commercial usage REQUIRES a commercial licence.
 *  
 *      bPI_LEDd 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.
 *
 * 		-- V 1.0 --
 *	16/11/2014 - LF - First version
 *	17/11/2014 - LF - change default place for the configuration file
 *
 * 		-- V 1.1 ("Je suis Charlie" version) --
 * 	08/01/2014 - LF - Correct LOAD blinking
 * 					Add MultBlink option
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <linux/sockios.h>
#include <linux/mii.h>
#include <time.h>
#include <sys/sysinfo.h>


#define VERSION "1.1 (\"Je suis Charlie\" version)"
#define DEFAULT_CONFIGURATION_FILE "/usr/local/etc/BananaLEDd.conf"
#define MAXLINE 1024

typedef enum { false=0, true } bool;
bool debug = false;
char *statfile = NULL;

enum LED { LGreen=0, LYellow, LBlue };
enum LMODE { M10=0, M100, G1, LACTIVITY, LSATA, LLOAD };
#define LSPEEDMASK ((1 << LACTIVITY) - 1)
#define LCUMULATIVE ((1 << LSATA) -1)		/* mask for cumulative mode */
#define LMODESATA (1 << LSATA )
#define LMODELOAD (1 << LLOAD )

struct Config {
	short Green;
	short Yellow;
	short Blue;

	const char *device;
	const char *disk;
	short Sample;
	short MinBlink;
	short MultBlink;
} cfg;


	/*
	 * Access to the device
	 * "not my code" :)
	 */
static int skfd = -1;
static struct ifreq ifr;

static int mdio_read(int skfd, __u16 location) {
	
	struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&ifr.ifr_data;
	mii->reg_num = location;
	if (ioctl(skfd, SIOCGMIIREG, &ifr) < 0) {
		fprintf(stderr, "SIOCGMIIREG on %s failed: %s\n", ifr.ifr_name, strerror(errno));
		return -1;
	}
	return mii->val_out;
}

static void mdio_write(int skfd, __u16 location, __u16 value) {
	
	struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&ifr.ifr_data;
	mii->reg_num = location;
	mii->val_in = value;
	if (ioctl(skfd, SIOCSMIIREG, &ifr) < 0) {
		fprintf(stderr, "SIOCGMIIREG on %s failed: %s\n", ifr.ifr_name, strerror(errno));
	}
}

	/*
	 * Helper functions
	 */
char *removeLF(char *s){
	size_t l=strlen(s);
	if(l && s[--l] == '\n')
		s[l] = 0;
	return s;
}

char *striKWcmp( char *s, const char *kw ){
/* compare string s against kw
 * Return :
 * 	- remaining string if the keyword matches
 * 	- NULL if the keyword is not found
 */
	size_t klen = strlen(kw);
	if( strncasecmp(s,kw,klen) )
		return NULL;
	else
		return s+klen;
}


	/*
	 * Reading the configuration
	 */

short confled( char *a, const char *id ){
	char *tok = strtok(a," \t");
	short res=0;

	while(tok){
		if(!strcmp(tok, "10"))
			res |= 1 << M10;
		else if(!strcmp(tok, "100"))
			res |= 1 << M100;
		else if(!strcmp(tok, "1000"))
			res |= 1 << G1;
		else if(!strcasecmp(tok, "LINKED"))
			res |= LSPEEDMASK;		/* All speeds */
		else if(!strcasecmp(tok, "NETWORK"))
			res |= 1 << LACTIVITY;
		else if(!strcasecmp(tok, "SATA"))
			res |= 1 << LSATA;
		else if(!strcasecmp(tok, "LOAD"))
			res |= 1 << LLOAD;
		else
			printf("*W*\t%s : '%s' mode is ignored\n", id, tok);

		tok = strtok(NULL, " \t");
	}

	if(debug)
		printf("\t%s mode : %02x\n", id, res);

		/* Check for compatibilies */
	if(res & LCUMULATIVE && res & ~LCUMULATIVE){
		fprintf(stderr,"%s : incompatible mode set\n", id);
		exit(EXIT_FAILURE);
	}

	return res;
}

void read_configuration( const char *fch ){
	FILE *f;
	char l[MAXLINE];
	char *arg;

		/* Cleaning configuration */
	cfg.Green = cfg.Yellow = cfg.Blue = 0;
	if(cfg.device)
		free((void *)cfg.device);
	cfg.device = NULL;
	if(cfg.disk)
		free((void *)cfg.disk);
	cfg.disk = NULL;
	cfg.Sample=100;
	cfg.MinBlink=2;
	cfg.MultBlink=100;

		/* Reading ... */
	if(debug)
		printf("Reading configuration file '%s'\n", fch);

	if(!(f=fopen(fch, "r"))){
		perror(fch);
		exit(EXIT_FAILURE);
	}

	while(fgets(l, MAXLINE, f)){
		if(*l == '#' || *l == '\n')
			continue;

		if((arg = striKWcmp(l,"Green="))) 
			cfg.Green = confled( removeLF(arg), "Green" );
		else if((arg = striKWcmp(l,"Yellow=")))
			cfg.Yellow = confled( removeLF(arg), "Yellow" );
		else if((arg = striKWcmp(l,"Blue="))) 
			cfg.Blue = confled( removeLF(arg), "Blue" );
		else if((arg = striKWcmp(l,"LEDDevice="))){
			assert( cfg.device = strdup( removeLF(arg)) );
			if(debug)
				printf("\tUsing '%s' as LED device.\n", cfg.device);
		} else if((arg = (char *)striKWcmp(l,"Disk="))){
			assert( cfg.disk = strdup( removeLF(arg)) );
			if(debug)
				printf("\tMonitoring disk '%s'.\n", cfg.disk);
		} else if((arg = striKWcmp(l,"Sample="))){
			int res = atoi(arg);
			if(res > 0){
				cfg.Sample = res;
				if(debug)
					printf("\tUsing %dms sample time.\n", cfg.Sample);
			}
		} else if((arg = striKWcmp(l,"MinBlink="))){
			int res = atoi(arg);
			if(res > 0){
				cfg.MinBlink = res;
				if(debug)
					printf("\tMinBlink is %d\n", cfg.MinBlink);
/*					printf("\tMinBlink is %d -> %.1f blink(s) per second.\n", cfg.MinBlink, 500.0/(cfg.Sample * cfg.MinBlink)); */
			}
		} else if((arg = striKWcmp(l,"MultBlink="))){
			int res = atoi(arg);
			if(res > 0){
				cfg.MultBlink = res;
				if(debug)
					printf("\tMultBlink is %d\n", cfg.MultBlink);
			}		
		} else if(debug)
			printf("*W* configuration line '%s' is ignored\n", removeLF(l));
	}

	fclose(f);
}


void configure_led( short v, int id ){
/* Set led's hardware configuration bits
 * 	-> v : configuration bits
 * 	-> id : Led
 */
	__u16 reg, org;

		/* Switch to "extension page 44" */
	mdio_write(skfd, 0x1f, 0x0007);
	mdio_write(skfd, 0x1e, 0x002c);

		/* Speed */
	org = reg = mdio_read(skfd, 28);	/* Read current value */
	reg &= ~(LSPEEDMASK << (id *4));	/* mask out this led bits */
	reg |= (v & LSPEEDMASK) << (id *4);	/* Set value for this led */
/* printf("speed for\t: %d : %02x => %04x\n", id, v & LSPEEDMASK, reg); */

	if(org != reg)
		mdio_write(skfd, 28, reg);

		/* Activities */
	org = reg = mdio_read(skfd, 26);	/* Read current value */
	reg &= ~(1 << (id + 4));			/* mask out this led bits */
	if( v & (1 << LACTIVITY))
		reg |= 1 << (id + 4);			/* Set value for this led */
/* printf("activity for\t: %d : %02x => %04x\n", id, 1 << (id + 4), reg); */

	if(org != reg)
		mdio_write(skfd, 26, reg);

		/* Back to normal register bank */
	mdio_write(skfd, 0x1f, 0x0000);
}

unsigned long int diskcounter(const char *fch){
	unsigned long int cr, cw, bidon;
	FILE *f;

	if(!(f = fopen(fch, "r"))){
		if(debug)
			perror(fch);
		return 0;
	}

	assert(fscanf(f, "%ld %ld %ld %ld %ld", &cr, &bidon, &bidon, &bidon, &cw));
	fclose(f);

	return(cr + cw);
}

int getload(){
	struct sysinfo sys_info;
	if(sysinfo(&sys_info)){
		if(debug)
			perror("sysinfo()");
		return 0;
	}

	return((int)(cfg.MultBlink *sys_info.loads[0] / (float)(1 << SI_LOAD_SHIFT)));
}

void cleaning(){
	if(skfd >= 0)
		close(skfd);

	if(statfile)
		free(statfile);
}

int main(int ac, char **av){
	const char *conf_file = DEFAULT_CONFIGURATION_FILE;
	int val, blkcnt = 0;
	bool blkstatus = false;
	unsigned long int diskcnt;	/* Disk activity counter */
	bool prevdisk = false;
	const char *const statfilestring = "/sys/block/%s/stat";
	struct timespec sleepvalue = {0};

	if(ac > 0){
		int i;
		for(i=1; i<ac; i++){
			if(!strcmp(av[i], "-h")){
				fprintf(stderr, "%s (%s)\n"
					"Control BananaPI ethernet LEDs\n"
					"Known options are :\n"
					"\t-h : this online help\n"
					"\t-d : enable debug messages\n"
					"\t-f<file> : read <file> for configuration\n"
					"\t\t(default is '%s')\n",
					basename(av[0]), VERSION, DEFAULT_CONFIGURATION_FILE
				);
				exit(EXIT_FAILURE);
			} else if(!strcmp(av[i], "-d")){
				debug = true;
				puts("BananaLEDd (c) L.Faillie 2014");
				puts("https://sourceforge.net/projects/bananacompanions/");
				printf("%s (%s) starting ...\n", basename(av[0]), VERSION);
			} else if(!strncmp(av[i], "-f", 2))
				conf_file = av[i] + 2;
			else {
				fprintf(stderr, "Unknown option '%s'\n%s -h\n\tfor some help\n", av[i], av[0]);
				exit(EXIT_FAILURE);
			}
		}
	}

	read_configuration( conf_file );


		/* Open stat file */
	val = strlen(statfilestring) + strlen(cfg.disk);	/* EOS if compensated by '%s' */
	assert(statfile = malloc(val));
	sprintf(statfile, statfilestring, cfg.disk);
	atexit(cleaning);

		/*
		 * Accessing to the hardware
		 */
	if((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
		perror("socket");
		exit(EXIT_FAILURE);
	}
	strncpy(ifr.ifr_name, cfg.device, IFNAMSIZ);

	if(ioctl(skfd, SIOCGMIIPHY, &ifr) < 0){
		fprintf(stderr, "SIOCGMIIPHY on %s failed: %s\n", cfg.device, strerror(errno));
		exit(EXIT_FAILURE);
	}

		/*
		 * Sanity check : ensure it's the right device
		 * Have a look on RTL8211E datasheet
		 */
	mdio_write(skfd, 0x1f, 0x0000);
	val = mdio_read(skfd, 2);
	if((val & 0xFFFF) != 0x001c){
		fprintf(stderr, "unexpected PHYID1: 0x%x\n", val);
		exit(EXIT_FAILURE);
	}

	val = mdio_read(skfd, 3);
	if((val & 0xFC00) != 0xc800){
		fprintf(stderr, "unexpected PHYID2: 0x%x\n", val);
		exit(EXIT_FAILURE);
	}

	if(debug)
		puts("Ok, let's go ...");

		/* set one shots */
	if(cfg.Green & LCUMULATIVE)
		configure_led( cfg.Green, LGreen );
	else
		configure_led( 0, LGreen );
	if(cfg.Yellow & LCUMULATIVE)
		configure_led( cfg.Yellow, LYellow );
	else
		configure_led( 0, LYellow );
	if(cfg.Blue & LCUMULATIVE)
		configure_led( cfg.Blue, LBlue );
	else
		configure_led( 0, LBlue );

	diskcnt = diskcounter( statfile );
	sleepvalue.tv_nsec = cfg.Sample * 1000000L;
	for(;;){
			/* Handle disk activities */
		unsigned long int dsk = diskcounter( statfile );
		if(dsk){
			if(dsk != diskcnt){	/* some disk activities */
				diskcnt = dsk;
				if(!prevdisk){
					prevdisk = true;
					if(cfg.Green == LMODESATA)
						configure_led(LSPEEDMASK, LGreen);
					if(cfg.Yellow == LMODESATA)
						configure_led(LSPEEDMASK, LYellow);
					if(cfg.Blue == LMODESATA)
						configure_led(LSPEEDMASK, LBlue);
					if(debug)
						puts("disk on");
				}
			} else if(dsk == diskcnt && prevdisk) {	/* Disk is quiet */
				prevdisk = false;
				if(cfg.Green == LMODESATA)
					configure_led(0, LGreen);
				if(cfg.Yellow == LMODESATA)
					configure_led(0, LYellow);
				if(cfg.Blue == LMODESATA)
					configure_led(0, LBlue);
				if(debug)
					puts("disk off");
			}
		}


			/* Handle system load */
		if((val = getload()) < cfg.MinBlink)
			val = cfg.MinBlink;

		if(++blkcnt > val){
			blkcnt = 0;
			if(blkstatus){
				blkstatus = false;
				if(cfg.Green == LMODELOAD)
					configure_led(0, LGreen);
				if(cfg.Yellow == LMODELOAD)
					configure_led(0, LYellow);
				if(cfg.Blue == LMODELOAD)
					configure_led(0, LBlue);
				if(debug)
					puts("Load off");
			} else {
				blkstatus = true;
				if(cfg.Green == LMODELOAD)
					configure_led(LSPEEDMASK, LGreen);
				if(cfg.Yellow == LMODELOAD)
					configure_led(LSPEEDMASK, LYellow);
				if(cfg.Blue == LMODELOAD)
					configure_led(LSPEEDMASK, LBlue);
				if(debug)
					puts("Load on");
			}
		}
		nanosleep(&sleepvalue, NULL);
	}

	exit(EXIT_SUCCESS);
}

конфиг:

# BananaLEDd configuration file.

# LED's mode can be
#	10   	- on when linked to 10M network
#	100  	- on when linked to 100M network
#	1000 	- on when linked to 1G network
#	LINKED	- on when linked to a network
#	NETWORK - follow network activities
#	SATA	- follow SATA disk activities
#	LOAD	- System load

Blue=SATA
Yellow=LOAD 
Green=LINKED NETWORK

# Adapter to use
LEDDevice=eth0

# Disk to monitor
Disk=sda

# Sample time (in ms)
# default 100 ms == 1/10s
Sample=100

# Minimum load blink time (ms)
# has to be a multiple of Sample 
# Result is : MultBlink/2/(MinBlink * Sample)
# So default is about 2.5 blink per second
MinBlink=2
MultBlink=50

Принцип прост: проц простаивает - моргает быстро, нагружаю проц - моргает медленно, а мне нужно что бы работало наоборот. При простое время свечения = (MultBlink/2) / (MinBlink * Sample).

Не пойму где именно происходит эта математическая операция...

в getload он получает значение загрузки системы и дальше его юзает в цикле для переключения состояния светодиодов.

Iron_Bug ★★★★★ ()
Ответ на: комментарий от Iron_Bug

При простое ″val = cfg.MinBlink″?

″val″ определяет кол-во циклов длительностью ″sleepvalue″, которая:

sleepvalue.tv_nsec = cfg.Sample * 1000000L
Как там получается (MultBlink/2) / (MinBlink * Sample) ?

mky ★★★★★ ()

При простое время свечения (MultBlink/2) / (MinBlink * Sample)

Это вобще не время, размерность не правильная, Sample измеряется в мс и стоит в знаменателе.

Время свечения при простое: MinBlink * Sample = 200 мс.

Время не свечения равно времени свечения, поэтому время цикла 400 мс, что даёт 1000/400 = 2,5 моргания / секунда.

mky ★★★★★ ()
Ответ на: комментарий от mky

Как там получается (MultBlink/2) / (MinBlink * Sample) ?

Этого я не знаю, так автор написал.

Так что мне нужно изменить чтобы инвертировать работу программы?

lucky_guy ★★★ ()
Ответ на: комментарий от lucky_guy

Нужно изменить строку:

return((int)(cfg.MultBlink *sys_info.loads[0] / (float)(1 << SI_LOAD_SHIFT)));
в функци getload(). На что менять эту строку я сказать не могу, это не Си, это арифметика, вам решать.

″sys_info.loads[0] / (float)(1 << SI_LOAD_SHIFT)″ это load average. В исходной программе светодиод горит в течении MultBlink * Sample * LA. Наверное, нужно делить на LA, только сделать ограничение, чтобы не было деления на ноль.

mky ★★★★★ ()

Вот это

return((int)(cfg.MultBlink *sys_info.loads[0] / (float)(1 << SI_LOAD_SHIFT)));
}
возвращает число от 0 до cfg.MultBlink; чем больше нагрузка, тем выше значение.

Вот это

if(++blkcnt > val){
дает задержку: чем выше нагрузка, тем больше будет задержка.

Чтобы сделать наоборот, тебе нужно в int getload() переделать return. Скажем, так:

return((int)(cfg.MultBlink - cfg.MultBlink*sys_info.loads[0] / (float)(1 << SI_LOAD_SHIFT)));
}

Eddy_Em ☆☆☆☆☆ ()
Ответ на: комментарий от I-Love-Microsoft

Открой для себя http://paste.org.ru

На самом деле не ясно, почему бы ЛОР-у не хостить аттачменты к сообщениям. В том числе, портянки кода.

Manhunt ★★★★★ ()
Последнее исправление: Manhunt (всего исправлений: 1)
Ответ на: комментарий от Manhunt

На самом деле не ясно, почему бы ЛОР-у не хостить аттачменты к сообщениям. В том числе, портянки кода.
Manhunt

Абсолютно ясно - иначе завалят порнухой, малварью и фотками котиков ...

anonymous ()
Ответ на: комментарий от anonymous

Абсолютно ясно - иначе завалят порнухой, малварью и фотками котиков ...

Давать такую возможность начиная с одной звезды, и не завалят.

Manhunt ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.