[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