[USERSPACE 2/4] pcmcia: pcmcia-socket-startup

Dominik Brodowski linux at dominikbrodowski.net
Sun Feb 27 05:12:02 EST 2005


The pcmcia-socket-startup tool. It requires a patch to libsysfs (also
attached) to work properly.

	Dominik
-------------- next part --------------
/* Special state for handling include files */
%x src

%{
/*
 * Startup tool for non statically mapped PCMCIA sockets
 *
 * (C) 2005		Dominik Brodowski <linux at dominikbrodowski.net>
 *
 *  The initial developer of the original code is David A. Hinds
 *  <dahinds at users.sourceforge.net>.  Portions created by David A. Hinds
 *  are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
 *
 * License: GPL v2
 */

#undef src

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <syslog.h>

#ifdef HAS_WORDEXP
#include <wordexp.h>
#else
#include <glob.h>
#endif

#define src 1

#include "yacc_config.h"

/* For assembling nice error messages */
char *current_file;
int current_lineno;

static int lex_number(char *s);
static int lex_string(char *s);
static void do_source(char *fn);
static int do_eof(void);

%}

int	[0-9]+
hex	0x[0-9a-fA-F]+
str	\"([^"]|\\.)*\"

%%

source[ \t]+	BEGIN(src); return SOURCE;
<src>[^\n]+	do_source(yytext); BEGIN(INITIAL);
<<EOF>>		if (do_eof()) yyterminate();

\n		current_lineno++;
[ \t]*		/* skip */ ;
[ ]*[#;].*	/* skip */ ;

exclude		return EXCLUDE;
include		return INCLUDE;
irq		return IRQ_NO;
port		return PORT;
memory		return MEMORY;
module		/* skip */ ;

{int}		return lex_number(yytext);

{hex}		return lex_number(yytext);

{str}		return lex_string(yytext);

.		return yytext[0];

%%

#ifndef yywrap
int yywrap() { return 1; }
#endif

/*======================================================================

    Stuff to parse basic data types

======================================================================*/

static int lex_number(char *s)
{
    yylval.num = strtoul(s, NULL, 0);
    return NUMBER;
}

static int lex_string(char *s)
{
    int n = strlen(s);
    yylval.str = malloc(n-1);
    strncpy(yylval.str, s+1, n-2);
    yylval.str[n-2] = '\0';
    return STRING;
}

/*======================================================================

    Code to support nesting of configuration files

======================================================================*/

#define MAX_SOURCE_DEPTH 4
struct source_stack {
    YY_BUFFER_STATE	buffer;
    char		*filename;
    int			lineno, fileno;
    FILE		*file;
#ifdef HAS_WORDEXP
    wordexp_t		word;
#else
    glob_t		glob;
#endif
} source_stack[MAX_SOURCE_DEPTH];
static int source_stack_ptr = 0;
static int parse_env = 0;

static int get_glob(void)
{
    struct source_stack *s = &source_stack[source_stack_ptr];
#ifdef HAS_WORDEXP
    while (s->fileno < s->word.we_wordc) {
	char *fn = s->word.we_wordv[s->fileno];
#else
    while (s->fileno < s->glob.gl_pathc) {
	char *fn = s->glob.gl_pathv[s->fileno];
#endif
	s->file = fopen(fn, "r");
	if (s->file == NULL) {
	    if (strpbrk(fn, "?*[") == NULL)
		syslog(LOG_ERR, "could not open '%s': %m", fn);
	    s->fileno++;
	} else {
	    current_lineno = 1;
	    current_file = strdup(fn);
	    yy_switch_to_buffer(yy_create_buffer(s->file, YY_BUF_SIZE));
	    source_stack_ptr++;
	    s->fileno++;
	    return 0;
	}
    }
    return -1;
}

static void do_source(char *fn)
{
    struct source_stack *s = &source_stack[source_stack_ptr];

    if (source_stack_ptr >= MAX_SOURCE_DEPTH) {
	syslog(LOG_ERR, "source depth limit exceeded");
	return;
    }
#ifdef HAS_WORDEXP
    wordexp(fn, &s->word, 0);
#else
    glob(fn, GLOB_NOCHECK, NULL, &s->glob);
#endif
    s->fileno = 0;
    s->buffer = YY_CURRENT_BUFFER;
    s->lineno = current_lineno;
    s->filename = current_file;
    get_glob();
}

static int do_eof(void)
{
    struct source_stack *s = &source_stack[--source_stack_ptr];
    if (source_stack_ptr < 0) {
	if (parse_env == 0) {
	    char *t = getenv("PCMCIA_OPTS");
	    if (t == NULL) return -1;
	    parse_env = 1;
	    source_stack_ptr = 0;
	    current_file = "PCMCIA_OPTS";
	    current_lineno = 1;
	    yy_scan_string(t);
	    return 0;
	} else
	    return -1;
    }
    fclose(s->file);
    free(current_file);
    yy_delete_buffer(YY_CURRENT_BUFFER);
    if (get_glob() != 0) {
	yy_switch_to_buffer(s->buffer);
	current_lineno = s->lineno;
	current_file = s->filename;
    }
    return 0;
}

/*======================================================================

    The main entry point... returns -1 if the file can't be accessed.

======================================================================*/

int parse_configfile(char *fn)
{
    FILE *f;
    
    f = fopen(fn, "r");
    if (!f) {
	syslog(LOG_ERR, "could not open '%s': %m", fn);
	return -1;
    }
    current_lineno = 1;
    current_file = fn;
    source_stack_ptr = 0;
    yyrestart(f);
    yyparse();
    fclose(f);
    return 0;
}

-------------- next part --------------
pcmcia-socket-startup: startup.o yacc_config.o lex_config.o
	$(CC) -lsysfs -o pcmcia-socket-startup startup.o yacc_config.o lex_config.o

yacc_config.o lex_config.o: %.o: %.c
	$(CC) -c -MD -O -pipe $(CPPFLAGS) $<

clean:
	rm -f *.o *.d lex_config.c yacc_config.c pcmcia-socket-startup
-------------- next part --------------
/*
 * Startup tool for non statically mapped PCMCIA sockets
 *
 * (C) 2005		Dominik Brodowski <linux at dominikbrodowski.net>
 *
 *  The initial developer of the original code is David A. Hinds
 *  <dahinds at users.sourceforge.net>.  Portions created by David A. Hinds
 *  are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
 *
 * License: GPL v2
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>

#include <sysfs/libsysfs.h>

#include "startup.h"

/* uncomment for debug output */
//#define dprintf printf


/* Linked list of resource adjustments */
struct adjust_list_t *root_adjust = NULL;

/* path for config file, device scripts */
static char *configpath = "/etc/pcmcia";

enum {
        RESOURCE_MEM,
        RESOURCE_IO,
        MAX_RESOURCE_FILES
};


static const char *resource_files[MAX_RESOURCE_FILES] = {
	[RESOURCE_MEM]	= "available_resources_mem",
	[RESOURCE_IO]	= "available_resources_io",
};

#define PATH_TO_SOCKET "/sys/class/pcmcia_socket/"


static int add_available_resource(unsigned int socket_no, unsigned int type,
				  unsigned long start, unsigned long end)
{
	char file[SYSFS_PATH_MAX];
	char content[SYSFS_PATH_MAX];
	struct sysfs_attribute *attr;
	int ret;
	size_t len;

	if (type >= MAX_RESOURCE_FILES)
		return -EINVAL;

	if (end <= start)
		return -EINVAL;

	dprintf("%d %d %lx %lx\n", socket_no, type, start, end);

	snprintf(file, SYSFS_PATH_MAX, PATH_TO_SOCKET "pcmcia_socket%u/%s",
		socket_no, resource_files[type]);

	len = snprintf(content, SYSFS_PATH_MAX, "0x%08lx - 0x%08lx", start, end);

	dprintf("content is %s\n", content);

	dprintf("file is %s\n", file);

	attr = sysfs_open_attribute(file);
	if (!attr)
		return -ENODEV;

	dprintf("open, len %d\n", len);

	ret = sysfs_write_attribute(attr, content, len);

	dprintf("ret is %d\n", ret);

	sysfs_close_attribute(attr);

	return (ret);
}

static int setup_done(unsigned int socket_no)
{
	int ret;
	char file[SYSFS_PATH_MAX];
	struct sysfs_attribute *attr;

	snprintf(file, SYSFS_PATH_MAX, PATH_TO_SOCKET
		 "pcmcia_socket%u/available_resources_setup_done",
		 socket_no);

	attr = sysfs_open_attribute(file);
	if (!attr)
		return -ENODEV;

	sysfs_write_attribute(attr, "42", 2);

	sysfs_close_attribute(attr);

	return (ret);
}


static int disallow_irq(unsigned int socket_no, unsigned int irq)
{
	char file[SYSFS_PATH_MAX];
	char content[SYSFS_PATH_MAX];
	struct sysfs_attribute *attr;
	unsigned int mask = 0xfff;
	unsigned int new_mask;
	int ret;
	size_t len;

	if (irq >= 32)
		return -EINVAL;

	snprintf(file, SYSFS_PATH_MAX, PATH_TO_SOCKET
		 "pcmcia_socket%u/card_irq_mask",
		 socket_no);

	attr = sysfs_open_attribute(file);
	if (!attr)
		return -ENODEV;

	dprintf("open, len %d\n", len);

	ret = sysfs_read_attribute(attr);
	if (ret) {
		ret = -EINVAL;
		goto out;
	}

	if (!attr->value || (attr->len < 6)) {
		ret = -EIO;
		goto out;
	}

	ret = sscanf(attr->value, "0x%x\n", &mask);

	new_mask = 1 << irq;

	mask &= ~new_mask;

	len = snprintf(content, SYSFS_PATH_MAX, "0x%04x", mask);

	dprintf("content is %s\n", content);

	ret = sysfs_write_attribute(attr, content, len);

 out:
	sysfs_close_attribute(attr);

	return (ret);
}


static void load_config(void)
{
    if (chdir(configpath) != 0) {
	syslog(LOG_ERR, "chdir to %s failed: %m", configpath);
	exit(EXIT_FAILURE);
    }
    parse_configfile("config.opts");
    return;
}


static void adjust_resources(unsigned int socket_no)
{
    adjust_list_t *al;
    char tmp[64];

    for (al = root_adjust; al; al = al->next) {
	    switch (al->adj.Resource) {
	    case RES_MEMORY_RANGE:
		    add_available_resource(socket_no, RESOURCE_MEM,
					   al->adj.resource.memory.Base,
					   al->adj.resource.memory.Base +
					   al->adj.resource.memory.Size - 1);
		    break;
	    case RES_IO_RANGE:
		    add_available_resource(socket_no, RESOURCE_IO,
					   al->adj.resource.io.BasePort,
					   al->adj.resource.io.BasePort +
					   al->adj.resource.io.NumPorts - 1);
		    break;
	    case RES_IRQ:
		    disallow_irq(socket_no, al->adj.resource.irq.IRQ);
		    break;
	    }
	    syslog(LOG_WARNING, "could not adjust resource: %s: %m", tmp);
    }
}


int main(int argc, char *argv[])
{
	unsigned long socket;

	if (argc != 2)
		return -EINVAL;

	socket = strtoul(argv[1], NULL, 0);

	load_config();

	adjust_resources(socket);

	setup_done(socket);

	return 0;
}
-------------- next part --------------
/*
 * Startup tool for non statically mapped PCMCIA sockets
 *
 *  The initial developer of the original code is David A. Hinds
 *  <dahinds at users.sourceforge.net>.  Portions created by David A. Hinds
 *  are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
 *
 * License: GPL v2
 *
 */

#define MAX_SOCKS	8
#define MAX_BINDINGS	4
#define MAX_MODULES	4

/* for AdjustResourceInfo */
typedef struct adjust_t {
    unsigned int	Action;
    unsigned int	Resource;
    unsigned int	Attributes;
    union {
	struct memory {
	    unsigned long	Base;
	    unsigned long	Size;
	} memory;
	struct io {
	    unsigned long	BasePort;
	    unsigned long	NumPorts;
	    unsigned int	IOAddrLines;
	} io;
	struct irq {
	    unsigned int	IRQ;
	} irq;
    } resource;
} adjust_t;


typedef struct adjust_list_t {
	adjust_t		adj;
    struct adjust_list_t *next;
} adjust_list_t;


extern adjust_list_t	*root_adjust;

int parse_configfile(char *fn);


#define RES_MEMORY_RANGE		1
#define RES_IO_RANGE			2
#define RES_IRQ				3
#define RES_RESERVED			0x10
#define REMOVE_MANAGED_RESOURCE		1
#define ADD_MANAGED_RESOURCE		2
-------------- next part --------------
#define DEVICE 257
#define CARD 258
#define ANONYMOUS 259
#define TUPLE 260
#define MANFID 261
#define VERSION 262
#define FUNCTION 263
#define PCI 264
#define BIND 265
#define CIS 266
#define TO 267
#define NEEDS_MTD 268
#define MODULE 269
#define OPTS 270
#define CLASS 271
#define REGION 272
#define JEDEC 273
#define DTYPE 274
#define DEFAULT 275
#define MTD 276
#define INCLUDE 277
#define EXCLUDE 278
#define RESERVE 279
#define IRQ_NO 280
#define PORT 281
#define MEMORY 282
#define STRING 283
#define NUMBER 284
#define SOURCE 285
typedef union {
    char *str;
    u_long num;
    struct adjust_list_t *adjust;
} YYSTYPE;
extern YYSTYPE yylval;
-------------- next part --------------
%{
/*
 * Startup tool for non statically mapped PCMCIA sockets - config file parsing
 *
 * (C) 2005		Dominik Brodowski <linux at dominikbrodowski.net>
 *
 *  The initial developer of the original code is David A. Hinds
 *  <dahinds at users.sourceforge.net>.  Portions created by David A. Hinds
 *  are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
 *
 * License: GPL v2
 */
    
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <sys/types.h>

#include "startup.h"

/* If bison: generate nicer error messages */ 
#define YYERROR_VERBOSE 1
 
/* from lex_config, for nice error messages */
extern char *current_file;
extern int current_lineno;

void yyerror(char *msg, ...);

%}

%token DEVICE CARD ANONYMOUS TUPLE MANFID VERSION FUNCTION PCI
%token BIND CIS TO NEEDS_MTD MODULE OPTS CLASS
%token REGION JEDEC DTYPE DEFAULT MTD
%token INCLUDE EXCLUDE RESERVE IRQ_NO PORT MEMORY
%token STRING NUMBER SOURCE

%union {
    char *str;
    u_long num;
    struct adjust_list_t *adjust;
}

%type <str> STRING
%type <num> NUMBER
%type <adjust> adjust resource
%%
list:	  /* nothing */
	| list adjust
		{
		    adjust_list_t **tail = &root_adjust;
		    while (*tail != NULL) tail = &(*tail)->next;
		    *tail = $2;
		}
	;

adjust:   INCLUDE resource
		{
		    $2->adj.Action = ADD_MANAGED_RESOURCE;
		    $$ = $2;
		}
	| EXCLUDE resource
		{
		    $2->adj.Action = REMOVE_MANAGED_RESOURCE;
		    $$ = $2;
		}
	| RESERVE resource
		{
		    $2->adj.Action = ADD_MANAGED_RESOURCE;
		    $2->adj.Attributes |= RES_RESERVED;
		    $$ = $2;
		}
	| adjust ',' resource
		{
		    $3->adj.Action = $1->adj.Action;
		    $3->adj.Attributes = $1->adj.Attributes;
		    $3->next = $1;
		    $$ = $3;
		}
	;

resource: IRQ_NO NUMBER
		{
		    $$ = calloc(sizeof(adjust_list_t), 1);
		    $$->adj.Resource = RES_IRQ;
		    $$->adj.resource.irq.IRQ = $2;
		}
	| PORT NUMBER '-' NUMBER
		{
		    if (($4 < $2) || ($4 > 0xffff)) {
			yyerror("invalid port range 0x%x-0x%x", $2, $4);
			YYERROR;
		    }
		    $$ = calloc(sizeof(adjust_list_t), 1);
		    $$->adj.Resource = RES_IO_RANGE;
		    $$->adj.resource.io.BasePort = $2;
		    $$->adj.resource.io.NumPorts = $4 - $2 + 1;
		}
	| MEMORY NUMBER '-' NUMBER
		{
		    if ($4 < $2) {
			yyerror("invalid address range 0x%x-0x%x", $2, $4);
			YYERROR;
		    }
		    $$ = calloc(sizeof(adjust_list_t), 1);
		    $$->adj.Resource = RES_MEMORY_RANGE;
		    $$->adj.resource.memory.Base = $2;
		    $$->adj.resource.memory.Size = $4 - $2 + 1;
		}
	;

%%
void yyerror(char *msg, ...)
{
     va_list ap;
     char str[256];

     va_start(ap, msg);
     sprintf(str, "error in file '%s' line %d: ",
	     current_file, current_lineno);
     vsprintf(str+strlen(str), msg, ap);
#if YYDEBUG
     fprintf(stderr, "%s\n", str);
#else
     syslog(LOG_ERR, "%s", str);
#endif
     va_end(ap);
}

-------------- next part --------------
Hi,

int sysfs_write_attribute(struct sysfs_attribute *sysattr,
                const char *new_value, size_t len)

has a problematic "feature": if the file is empty but readable, nothing gets
written into it. The attached patch (untested) should fix it.

	Dominik


The check which tests whether the value to be written into a sysfs attribute
is already there should only trigger if the length of both strings is the
same.

Signed-off-by: Dominik Brodowski <linux at dominikbrodowski.net>

--- sysfsutils-1.2.0/lib/sysfs_dir.c.original	2005-02-21 08:10:33.000000000 +0100
+++ sysfsutils-1.2.0/lib/sysfs_dir.c	2005-02-21 08:14:01.000000000 +0100
@@ -194,7 +194,8 @@
 			dprintf("Error reading attribute\n");
 			return -1;
 		}
-		if ((strncmp(sysattr->value, new_value, sysattr->len)) == 0) {
+		if (((strncmp(sysattr->value, new_value, sysattr->len)) == 0)&&
+		    (len == sysattr->len)) {
 			dprintf("Attr %s already has the requested value %s\n",
 					sysattr->name, new_value);
 			return 0;	


More information about the linux-pcmcia mailing list