blob: d8e667935d426edcad3559d16e3d0e8f2d1efc51 [file] [log] [blame]
/*
* Startup tool for non statically mapped PCMCIA sockets
*
* (C) 2005 Dominik Brodowski <linux@brodo.de>
*
* The initial developer of the original code is David A. Hinds
* <dahinds@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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "startup.h"
/* uncomment for debug output */
#ifdef DEBUG
#define dprintf printf
#else
#define dprintf(...) do { } while (0);
#endif
/* Linked list of resource adjustments */
struct adjust_list_t *root_adjust;
/* 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/"
#define SYSFS_PATH_MAX 255
static int sysfs_read_file(const char *fname, char *buf, size_t buflen)
{
ssize_t numread;
int fd;
int ret = 0;
fd = open(fname, O_RDONLY);
if (fd <= 0)
return fd;
numread = read(fd, buf, buflen - 1);
if (numread < 1)
ret = -EIO;
else
buf[numread] = '\0';
close(fd);
return ret;
}
static int sysfs_write_file(const char *fname, const char *value, size_t len)
{
ssize_t numwrite;
int fd;
int ret = 0;
fd = open(fname, O_WRONLY);
if (fd <= 0)
return fd;
numwrite = write(fd, value, len);
if ((numwrite < 1) || ((size_t) numwrite != len))
ret = -EIO;
close(fd);
return ret;
}
static int add_available_resource(unsigned int socket_no, unsigned int type,
unsigned int action,
unsigned long start, unsigned long end)
{
char file[SYSFS_PATH_MAX];
char content[SYSFS_PATH_MAX];
int ret;
size_t len;
if (type >= MAX_RESOURCE_FILES)
return -EINVAL;
if (end <= start)
return -EINVAL;
dprintf("%d %d %d 0x%lx 0x%lx\n", socket_no, type, action, start, end);
snprintf(file, SYSFS_PATH_MAX, PATH_TO_SOCKET "pcmcia_socket%u/%s",
socket_no, resource_files[type]);
switch (action) {
case ADD_MANAGED_RESOURCE:
len = snprintf(content, SYSFS_PATH_MAX,
"0x%08lx - 0x%08lx", start, end);
break;
case REMOVE_MANAGED_RESOURCE:
len = snprintf(content, SYSFS_PATH_MAX,
"- 0x%08lx - 0x%08lx", start, end);
break;
default:
return -EINVAL;
}
dprintf("content is %s, file is %s\n", content, file);
ret = sysfs_write_file(file, content, len);
dprintf("ret is %d\n", ret);
return ret;
}
static int setup_done(unsigned int socket_no)
{
char file[SYSFS_PATH_MAX];
snprintf(file, SYSFS_PATH_MAX, PATH_TO_SOCKET
"pcmcia_socket%u/available_resources_setup_done",
socket_no);
return sysfs_write_file(file, "42", 2);
}
static int disallow_irq(unsigned int socket_no, unsigned int irq)
{
char file[SYSFS_PATH_MAX];
char content[SYSFS_PATH_MAX];
unsigned int mask = 0xfff;
unsigned int new_mask;
int ret;
size_t len;
if (irq >= 32)
return -EINVAL;
len = snprintf(file, SYSFS_PATH_MAX, PATH_TO_SOCKET
"pcmcia_socket%u/card_irq_mask",
socket_no);
dprintf("file is %s\n", file);
ret = sysfs_read_file(file, content, SYSFS_PATH_MAX);
if (ret)
return -ENODEV;
ret = sscanf(content, "0x%x\n", &mask);
if (ret != 1)
return -EIO;
new_mask = 1 << irq;
mask &= ~new_mask;
len = snprintf(content, SYSFS_PATH_MAX, "0x%04x", mask);
dprintf("content is %s\n", content);
return sysfs_write_file(file, content, len);
}
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;
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.Action,
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.Action,
al->adj.resource.io.BasePort,
al->adj.resource.io.BasePort +
al->adj.resource.io.NumPorts - 1);
break;
case RES_IRQ:
if (al->adj.Action == REMOVE_MANAGED_RESOURCE)
disallow_irq(socket_no,
al->adj.resource.irq.IRQ);
break;
}
}
}
int main(int argc, char *argv[])
{
char *socket_no;
unsigned long socket, i;
unsigned int all_sockets = 0;
socket_no = getenv("SOCKET_NO");
if (socket_no)
socket = strtoul(socket_no, NULL, 0);
else if (argc == 2)
socket = strtoul(argv[1], NULL, 0);
else if (argc == 1) {
socket = 0;
all_sockets = 1;
} else
return -EINVAL;
load_config();
for (i = 0; i < MAX_SOCKS; i++) {
if ((socket != i) && (!all_sockets))
continue;
adjust_resources(i);
setup_done(i);
}
return 0;
}