1472 lines
36 KiB
C
1472 lines
36 KiB
C
/*======================================================================
|
|
|
|
PCMCIA Card Manager daemon
|
|
|
|
cardmgr.c 1.183 2003/02/27 07:11:45
|
|
|
|
The contents of this file are subject to the Mozilla Public
|
|
License Version 1.1 (the "License"); you may not use this file
|
|
except in compliance with the License. You may obtain a copy of
|
|
the License at http://www.mozilla.org/MPL/
|
|
|
|
Software distributed under the License is distributed on an "AS
|
|
IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
implied. See the License for the specific language governing
|
|
rights and limitations under the License.
|
|
|
|
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.
|
|
|
|
Alternatively, the contents of this file may be used under the
|
|
terms of the GNU General Public License version 2 (the "GPL"), in
|
|
which case the provisions of the GPL are applicable instead of the
|
|
above. If you wish to allow the use of your version of this file
|
|
only under the terms of the GPL and not to allow others to use
|
|
your version of this file under the MPL, indicate your decision
|
|
by deleting the provisions above and replace them with the notice
|
|
and other provisions required by the GPL. If you do not delete
|
|
the provisions above, a recipient may use your version of this
|
|
file under either the MPL or the GPL.
|
|
|
|
======================================================================*/
|
|
|
|
#include <sys/types.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <syslog.h>
|
|
#include <getopt.h>
|
|
#include <signal.h>
|
|
#include <stdarg.h>
|
|
#include <sys/time.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/utsname.h>
|
|
#include <sys/file.h>
|
|
|
|
#include <pcmcia/version.h>
|
|
#include <pcmcia/config.h>
|
|
#include <pcmcia/cs_types.h>
|
|
#include <pcmcia/cs.h>
|
|
#include <pcmcia/cistpl.h>
|
|
#include <pcmcia/ds.h>
|
|
|
|
#include "cardmgr.h"
|
|
|
|
/*====================================================================*/
|
|
|
|
typedef struct socket_info_t {
|
|
int fd;
|
|
int state;
|
|
card_info_t *card;
|
|
bind_info_t *bind[MAX_BINDINGS];
|
|
mtd_ident_t *mtd[2*CISTPL_MAX_DEVICES];
|
|
cistpl_funcid_t funcid;
|
|
cistpl_manfid_t manfid;
|
|
cistpl_vers_1_t prodid;
|
|
} socket_info_t;
|
|
|
|
#define SOCKET_PRESENT 0x01
|
|
#define SOCKET_READY 0x02
|
|
#define SOCKET_HOTPLUG 0x04
|
|
|
|
/* Linked list of resource adjustments */
|
|
struct adjust_list_t *root_adjust = NULL;
|
|
|
|
/* Linked list of device definitions */
|
|
struct device_info_t *root_device = NULL;
|
|
|
|
/* Special pointer to "anonymous" card definition */
|
|
struct card_info_t *blank_card = NULL;
|
|
|
|
/* Linked list of card definitions */
|
|
struct card_info_t *root_card = NULL;
|
|
|
|
/* Linked list of function definitions */
|
|
struct card_info_t *root_func = NULL;
|
|
|
|
/* Linked list of MTD definitions */
|
|
struct mtd_ident_t *root_mtd = NULL;
|
|
|
|
/* Default MTD */
|
|
struct mtd_ident_t *default_mtd = NULL;
|
|
|
|
static int sockets;
|
|
static struct socket_info_t socket[MAX_SOCKS];
|
|
|
|
/* Default path for config file, device scripts */
|
|
#ifdef ETC
|
|
static char *configpath = ETC;
|
|
#else
|
|
static char *configpath = "/etc/pcmcia";
|
|
#endif
|
|
|
|
/* Default path for pid file */
|
|
static char *pidfile = "/var/run/cardmgr.pid";
|
|
|
|
/* Default path for finding modules */
|
|
static char *modpath = NULL;
|
|
|
|
/* Default path for socket info table */
|
|
static char *stabfile;
|
|
|
|
/* If set, don't generate beeps when cards are inserted */
|
|
static int be_quiet = 0;
|
|
|
|
/* If set, use modprobe instead of insmod */
|
|
static int do_modprobe = 0;
|
|
|
|
/* If set, configure already inserted cards, then exit */
|
|
static int one_pass = 0;
|
|
|
|
/* Extra message logging? */
|
|
static int verbose = 0;
|
|
|
|
#define EITHER_OR(a, b) ((a) ? (a) : (b))
|
|
|
|
/*====================================================================*/
|
|
|
|
static int major = 0;
|
|
|
|
static int lookup_dev(char *name)
|
|
{
|
|
FILE *f;
|
|
int n;
|
|
char s[32], t[32];
|
|
|
|
f = fopen("/proc/devices", "r");
|
|
if (f == NULL)
|
|
return -errno;
|
|
while (fgets(s, 32, f) != NULL) {
|
|
if (sscanf(s, "%d %s", &n, t) == 2)
|
|
if (strcmp(name, t) == 0)
|
|
break;
|
|
}
|
|
fclose(f);
|
|
if (strcmp(name, t) == 0)
|
|
return n;
|
|
else
|
|
return -ENODEV;
|
|
}
|
|
|
|
int open_dev(dev_t dev, int mode)
|
|
{
|
|
static char *paths[] = {
|
|
"/var/lib/pcmcia", "/var/run", "/dev", "/tmp", NULL
|
|
};
|
|
static int nd = 0;
|
|
char **p, fn[64];
|
|
int fd;
|
|
|
|
int o_mode = (mode & S_IWRITE) ? O_RDWR : O_RDONLY;
|
|
for (p = paths; *p; p++) {
|
|
sprintf(fn, "%s/cm-%d-%d", *p, getpid(), nd++);
|
|
if (mknod(fn, mode, dev) == 0) {
|
|
fd = open(fn, o_mode);
|
|
if (fd < 0)
|
|
fd = open(fn, O_NONBLOCK|o_mode);
|
|
unlink(fn);
|
|
if (fd >= 0)
|
|
return fd;
|
|
if (errno == ENODEV)
|
|
break;
|
|
}
|
|
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int open_sock(int sock, int mode)
|
|
{
|
|
dev_t dev = (major<<8)+sock;
|
|
return open_dev(dev, mode);
|
|
}
|
|
|
|
/*======================================================================
|
|
|
|
xlate_scsi_name() is a sort-of-hack used to deduce the minor
|
|
device numbers of SCSI devices, from the information available to
|
|
the low-level driver.
|
|
|
|
======================================================================*/
|
|
|
|
/* getting these from headers is too much grief */
|
|
#define SCSI_DISK0_MAJOR 8
|
|
#define SCSI_TAPE_MAJOR 9
|
|
#define SCSI_CDROM_MAJOR 11
|
|
#define SCSI_GENERIC_MAJOR 21
|
|
#define SCSI_IOCTL_GET_IDLUN 0x5382
|
|
|
|
static int xlate_scsi_name(bind_info_t *bind)
|
|
{
|
|
int i, fd, mode, minor;
|
|
u_long arg[2], id1, id2;
|
|
|
|
id1 = strtol(bind->name+3, NULL, 16);
|
|
if ((bind->major == SCSI_DISK0_MAJOR) ||
|
|
(bind->major == SCSI_CDROM_MAJOR))
|
|
mode = S_IREAD|S_IFBLK;
|
|
else
|
|
mode = S_IREAD|S_IFCHR;
|
|
|
|
for (i = 0; i < 16; i++) {
|
|
minor = (bind->major == SCSI_DISK0_MAJOR) ? (i<<4) : i;
|
|
fd = open_dev((bind->major<<8)+minor, mode);
|
|
if (fd < 0)
|
|
continue;
|
|
if (ioctl(fd, SCSI_IOCTL_GET_IDLUN, arg) == 0) {
|
|
id2 = (arg[0]&0x0f) + ((arg[0]>>4)&0xf0) +
|
|
((arg[0]>>8)&0xf00) + ((arg[0]>>12)&0xf000);
|
|
if (id1 == id2) {
|
|
close(fd);
|
|
switch (bind->major) {
|
|
case SCSI_DISK0_MAJOR:
|
|
case SCSI_GENERIC_MAJOR:
|
|
sprintf(bind->name+2, "%c", 'a'+i); break;
|
|
case SCSI_CDROM_MAJOR:
|
|
sprintf(bind->name, "scd%d", i); break;
|
|
case SCSI_TAPE_MAJOR:
|
|
sprintf(bind->name+2, "%d", i); break;
|
|
}
|
|
bind->minor = minor;
|
|
return 0;
|
|
}
|
|
}
|
|
close(fd);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*====================================================================*/
|
|
|
|
#define BEEP_TIME 150
|
|
#define BEEP_OK 1000
|
|
#define BEEP_WARN 2000
|
|
#define BEEP_ERR 4000
|
|
|
|
#include <sys/kd.h>
|
|
|
|
static void beep(unsigned int ms, unsigned int freq)
|
|
{
|
|
int fd, arg;
|
|
|
|
if (be_quiet)
|
|
return;
|
|
fd = open("/dev/tty0", O_RDWR);
|
|
if (fd < 0)
|
|
return;
|
|
arg = (ms << 16) | freq;
|
|
ioctl(fd, KDMKTONE, arg);
|
|
close(fd);
|
|
usleep(ms*1000);
|
|
}
|
|
|
|
/*====================================================================*/
|
|
|
|
static void write_pid(void)
|
|
{
|
|
FILE *f;
|
|
f = fopen(pidfile, "w");
|
|
if (f == NULL)
|
|
syslog(LOG_WARNING, "could not open %s: %m", pidfile);
|
|
else {
|
|
fprintf(f, "%d\n", getpid());
|
|
fclose(f);
|
|
}
|
|
}
|
|
|
|
static void write_stab(void)
|
|
{
|
|
int i, j, k;
|
|
FILE *f;
|
|
socket_info_t *s;
|
|
bind_info_t *bind;
|
|
|
|
f = fopen(stabfile, "w");
|
|
if (f == NULL) {
|
|
syslog(LOG_WARNING, "fopen(stabfile) failed: %m");
|
|
return;
|
|
}
|
|
if (flock(fileno(f), LOCK_EX) != 0) {
|
|
syslog(LOG_ERR, "flock(stabfile) failed: %m");
|
|
return;
|
|
}
|
|
for (i = 0; i < sockets; i++) {
|
|
s = &socket[i];
|
|
fprintf(f, "Socket %d: ", i);
|
|
if (!(s->state & SOCKET_PRESENT)) {
|
|
fprintf(f, "empty\n");
|
|
} else if (s->state & SOCKET_HOTPLUG) {
|
|
fprintf(f, "CardBus hotplug device\n");
|
|
} else if (!s->card) {
|
|
fprintf(f, "unsupported card\n");
|
|
} else {
|
|
fprintf(f, "%s\n", s->card->name);
|
|
for (j = 0; j < s->card->bindings; j++)
|
|
for (k = 0, bind = s->bind[j];
|
|
bind != NULL;
|
|
k++, bind = bind->next) {
|
|
char *class = s->card->class[j];
|
|
if (!class) class = s->card->device[j]->class;
|
|
fprintf(f, "%d\t%s\t%s\t%d\t%s",
|
|
i, (class ? class : "none"),
|
|
bind->dev_info, k, bind->name);
|
|
if (bind->major)
|
|
fprintf(f, "\t%d\t%d\n",
|
|
bind->major, bind->minor);
|
|
else
|
|
fputc('\n', f);
|
|
}
|
|
}
|
|
}
|
|
fflush(f);
|
|
flock(fileno(f), LOCK_UN);
|
|
fclose(f);
|
|
}
|
|
|
|
/*====================================================================*/
|
|
|
|
static int get_tuple(int ns, cisdata_t code, ds_ioctl_arg_t *arg)
|
|
{
|
|
socket_info_t *s = &socket[ns];
|
|
|
|
arg->tuple.DesiredTuple = code;
|
|
arg->tuple.Attributes = 0;
|
|
if (ioctl(s->fd, DS_GET_FIRST_TUPLE, arg) != 0)
|
|
return -1;
|
|
arg->tuple.TupleOffset = 0;
|
|
if (ioctl(s->fd, DS_GET_TUPLE_DATA, arg) != 0) {
|
|
syslog(LOG_INFO, "error reading CIS data on socket %d: %m", ns);
|
|
return -1;
|
|
}
|
|
if (ioctl(s->fd, DS_PARSE_TUPLE, arg) != 0) {
|
|
syslog(LOG_INFO, "error parsing CIS on socket %d: %m", ns);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*======================================================================
|
|
|
|
For configurations with the standalone PCMCIA modules, this is
|
|
used to get PCI device ID's for CardBus cards.
|
|
|
|
======================================================================*/
|
|
|
|
typedef struct pci_id {
|
|
u_short vendor, device;
|
|
struct pci_id *next;
|
|
} pci_id_t;
|
|
|
|
static int get_pci_id(int ns, pci_id_t *id)
|
|
{
|
|
socket_info_t *s = &socket[ns];
|
|
config_info_t config;
|
|
|
|
config.Function = config.ConfigBase = 0;
|
|
if ((ioctl(s->fd, DS_GET_CONFIGURATION_INFO, &config) != 0) ||
|
|
(config.IntType != INT_CARDBUS) || !config.ConfigBase)
|
|
return 0;
|
|
id->vendor = config.ConfigBase & 0xffff;
|
|
id->device = config.ConfigBase >> 16;
|
|
return 1;
|
|
}
|
|
|
|
/*====================================================================*/
|
|
|
|
static void log_card_info(socket_info_t *s, pci_id_t *pci_id)
|
|
{
|
|
char v[256] = "";
|
|
int i;
|
|
static char *fn[] = {
|
|
"multi", "memory", "serial", "parallel", "fixed disk",
|
|
"video", "network", "AIMS", "SCSI"
|
|
};
|
|
|
|
if (s->prodid.ns) {
|
|
for (i = 0; i < s->prodid.ns; i++)
|
|
sprintf(v+strlen(v), "%s\"%s\"",
|
|
(i>0) ? ", " : "", s->prodid.str+s->prodid.ofs[i]);
|
|
syslog(LOG_INFO, " product info: %s", v);
|
|
} else {
|
|
syslog(LOG_INFO, " no product info available");
|
|
}
|
|
*v = '\0';
|
|
if (s->manfid.manf != 0)
|
|
sprintf(v, " manfid: 0x%04x, 0x%04x",
|
|
s->manfid.manf, s->manfid.card);
|
|
if (s->funcid.func != 0xff)
|
|
sprintf(v+strlen(v), " function: %d (%s)", s->funcid.func,
|
|
fn[s->funcid.func]);
|
|
if (strlen(v) > 0) syslog(LOG_INFO, "%s", v);
|
|
if (pci_id->vendor != 0)
|
|
syslog(LOG_INFO, " PCI id: 0x%04x, 0x%04x",
|
|
pci_id->vendor, pci_id->device);
|
|
}
|
|
|
|
static card_info_t *lookup_card(int ns)
|
|
{
|
|
socket_info_t *s = &socket[ns];
|
|
card_info_t *card = NULL;
|
|
ds_ioctl_arg_t arg;
|
|
pci_id_t pci_id = { 0, 0 };
|
|
cs_status_t status;
|
|
int i, ret, has_cis = 0;
|
|
|
|
s->manfid = (cistpl_manfid_t) { 0, 0 };
|
|
s->funcid = (cistpl_funcid_t) { 0xff, 0xff };
|
|
s->prodid = (cistpl_vers_1_t) { 0, 0, 0 };
|
|
|
|
/* Do we have a CIS structure? */
|
|
ret = ioctl(s->fd, DS_VALIDATE_CIS, &arg);
|
|
has_cis = ((ret == 0) && (arg.cisinfo.Chains > 0));
|
|
|
|
/* Try to read VERS_1, MANFID tuples */
|
|
if (has_cis) {
|
|
/* rule of thumb: cards with no FUNCID, but with common memory
|
|
device geometry information, are probably memory cards */
|
|
if (get_tuple(ns, CISTPL_FUNCID, &arg) == 0)
|
|
s->funcid = arg.tuple_parse.parse.funcid;
|
|
else if (get_tuple(ns, CISTPL_DEVICE_GEO, &arg) == 0)
|
|
s->funcid.func = CISTPL_FUNCID_MEMORY;
|
|
if (get_tuple(ns, CISTPL_MANFID, &arg) == 0)
|
|
s->manfid = arg.tuple_parse.parse.manfid;
|
|
if (get_tuple(ns, CISTPL_VERS_1, &arg) == 0)
|
|
s->prodid = arg.tuple_parse.parse.version_1;
|
|
|
|
for (card = root_card; card; card = card->next) {
|
|
|
|
if (card->ident_type &
|
|
~(VERS_1_IDENT|MANFID_IDENT|TUPLE_IDENT))
|
|
continue;
|
|
|
|
if (card->ident_type & VERS_1_IDENT) {
|
|
if (!s->prodid.ns)
|
|
continue;
|
|
for (i = 0; i < card->id.vers.ns; i++) {
|
|
if (strcmp(card->id.vers.pi[i], "*") == 0)
|
|
continue;
|
|
if (i >= s->prodid.ns)
|
|
break;
|
|
if (strcmp(card->id.vers.pi[i],
|
|
s->prodid.str+s->prodid.ofs[i]) != 0)
|
|
break;
|
|
}
|
|
if (i < card->id.vers.ns)
|
|
continue;
|
|
}
|
|
|
|
if (card->ident_type & MANFID_IDENT) {
|
|
if ((s->manfid.manf != card->manfid.manf) ||
|
|
(s->manfid.card != card->manfid.card))
|
|
continue;
|
|
}
|
|
|
|
if (card->ident_type & TUPLE_IDENT) {
|
|
arg.tuple.DesiredTuple = card->id.tuple.code;
|
|
arg.tuple.Attributes = 0;
|
|
ret = ioctl(s->fd, DS_GET_FIRST_TUPLE, &arg);
|
|
if (ret != 0) continue;
|
|
arg.tuple.TupleOffset = card->id.tuple.ofs;
|
|
ret = ioctl(s->fd, DS_GET_TUPLE_DATA, &arg);
|
|
if (ret != 0) continue;
|
|
if (strncmp((char *)arg.tuple_parse.data,
|
|
card->id.tuple.info,
|
|
strlen(card->id.tuple.info)) != 0)
|
|
continue;
|
|
}
|
|
|
|
break; /* we have a match */
|
|
}
|
|
}
|
|
|
|
/* Check PCI vendor/device info */
|
|
status.Function = 0;
|
|
ioctl(s->fd, DS_GET_STATUS, &status);
|
|
if (status.CardState & CS_EVENT_CB_DETECT) {
|
|
if (get_pci_id(ns, &pci_id)) {
|
|
if (!card) {
|
|
for (card = root_card; card; card = card->next)
|
|
if ((card->ident_type == PCI_IDENT) &&
|
|
(pci_id.vendor == card->manfid.manf) &&
|
|
(pci_id.device == card->manfid.card))
|
|
break;
|
|
}
|
|
} else {
|
|
/* this is a 2.4 kernel; hotplug handles these cards */
|
|
s->state |= SOCKET_HOTPLUG;
|
|
syslog(LOG_INFO, "socket %d: CardBus hotplug device", ns);
|
|
//beep(BEEP_TIME, BEEP_OK);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Try for a FUNCID match */
|
|
if (!card && (s->funcid.func != 0xff)) {
|
|
for (card = root_func; card; card = card->next)
|
|
if (card->id.func.funcid == s->funcid.func)
|
|
break;
|
|
}
|
|
|
|
if (card) {
|
|
syslog(LOG_INFO, "socket %d: %s", ns, card->name);
|
|
beep(BEEP_TIME, BEEP_OK);
|
|
if (verbose) log_card_info(s, &pci_id);
|
|
return card;
|
|
}
|
|
|
|
if (!blank_card || (status.CardState & CS_EVENT_CB_DETECT) ||
|
|
s->manfid.manf || s->manfid.card || s->prodid.ns || pci_id.vendor) {
|
|
syslog(LOG_INFO, "unsupported card in socket %d", ns);
|
|
if (one_pass) return NULL;
|
|
beep(BEEP_TIME, BEEP_ERR);
|
|
log_card_info(s, &pci_id);
|
|
write_stab();
|
|
return NULL;
|
|
} else {
|
|
card = blank_card;
|
|
syslog(LOG_INFO, "socket %d: %s", ns, card->name);
|
|
beep(BEEP_TIME, BEEP_WARN);
|
|
return card;
|
|
}
|
|
}
|
|
|
|
/*====================================================================*/
|
|
|
|
static void load_config(void)
|
|
{
|
|
if (chdir(configpath) != 0) {
|
|
syslog(LOG_ERR, "chdir to %s failed: %m", configpath);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (parse_configfile("config") != 0)
|
|
exit(EXIT_FAILURE);
|
|
if (root_device == NULL)
|
|
syslog(LOG_WARNING, "no device drivers defined");
|
|
if ((root_card == NULL) && (root_func == NULL))
|
|
syslog(LOG_WARNING, "no cards defined");
|
|
}
|
|
|
|
/*====================================================================*/
|
|
|
|
static void free_card(card_info_t *card)
|
|
{
|
|
if (card && (--card->refs == 0)) {
|
|
int i;
|
|
free(card->name);
|
|
switch(card->ident_type) {
|
|
case VERS_1_IDENT:
|
|
for (i = 0; i < card->id.vers.ns; i++)
|
|
free(card->id.vers.pi[i]);
|
|
break;
|
|
case TUPLE_IDENT:
|
|
free(card->id.tuple.info);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
for (i = 0; i < card->bindings; i++)
|
|
if (card->class[i])
|
|
free(card->class[i]);
|
|
free(card);
|
|
}
|
|
}
|
|
|
|
static void free_device(device_info_t *dev)
|
|
{
|
|
if (dev && (--dev->refs == 0)) {
|
|
int i;
|
|
for (i = 0; i < dev->modules; i++) {
|
|
free(dev->module[i]);
|
|
if (dev->opts[i]) free(dev->opts[i]);
|
|
}
|
|
if (dev->class) free(dev->class);
|
|
free(dev);
|
|
}
|
|
}
|
|
|
|
static void free_mtd(mtd_ident_t *mtd)
|
|
{
|
|
if (mtd && (--mtd->refs == 0)) {
|
|
free(mtd->name);
|
|
free(mtd->module);
|
|
free(mtd);
|
|
}
|
|
}
|
|
|
|
static void free_config(void)
|
|
{
|
|
while (root_adjust != NULL) {
|
|
adjust_list_t *adj = root_adjust;
|
|
root_adjust = root_adjust->next;
|
|
free(adj);
|
|
}
|
|
|
|
while (root_device != NULL) {
|
|
device_info_t *dev = root_device;
|
|
root_device = root_device->next;
|
|
free_device(dev);
|
|
}
|
|
|
|
while (root_card != NULL) {
|
|
card_info_t *card = root_card;
|
|
root_card = root_card->next;
|
|
free_card(card);
|
|
}
|
|
|
|
while (root_func != NULL) {
|
|
card_info_t *card = root_func;
|
|
root_func = root_func->next;
|
|
free_card(card);
|
|
}
|
|
blank_card = NULL;
|
|
|
|
while (root_mtd != NULL) {
|
|
mtd_ident_t *mtd = root_mtd;
|
|
root_mtd = root_mtd->next;
|
|
free_mtd(mtd);
|
|
}
|
|
default_mtd = NULL;
|
|
}
|
|
|
|
/*====================================================================*/
|
|
|
|
static int execute(char *msg, char *cmd)
|
|
{
|
|
int ret;
|
|
FILE *f;
|
|
char line[256];
|
|
|
|
syslog(LOG_INFO, "executing: '%s'", cmd);
|
|
strcat(cmd, " 2>&1");
|
|
f = popen(cmd, "r");
|
|
while (fgets(line, 255, f)) {
|
|
line[strlen(line)-1] = '\0';
|
|
syslog(LOG_INFO, "+ %s", line);
|
|
}
|
|
ret = pclose(f);
|
|
if (WIFEXITED(ret)) {
|
|
if (WEXITSTATUS(ret))
|
|
syslog(LOG_INFO, "%s exited with status %d",
|
|
msg, WEXITSTATUS(ret));
|
|
return WEXITSTATUS(ret);
|
|
} else
|
|
syslog(LOG_INFO, "%s exited on signal %d",
|
|
msg, WTERMSIG(ret));
|
|
return -1;
|
|
}
|
|
|
|
/*====================================================================*/
|
|
|
|
static int execute_on_dev(char *action, char *class, char *dev)
|
|
{
|
|
/* Fixed length strings are ok here */
|
|
char msg[128], cmd[128];
|
|
|
|
sprintf(msg, "%s cmd", action);
|
|
sprintf(cmd, "./%s %s %s", class, action, dev);
|
|
return execute(msg, cmd);
|
|
}
|
|
|
|
static void eprintf(char *name, char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
char s[32];
|
|
va_start(args, fmt);
|
|
vsprintf(s, fmt, args);
|
|
setenv(name, s, 1);
|
|
va_end(args);
|
|
}
|
|
|
|
static int execute_on_all(char *cmd, char *class, int sn, int fn)
|
|
{
|
|
socket_info_t *s = &socket[sn];
|
|
bind_info_t *bind;
|
|
int i, k, ret = 0;
|
|
char m[10];
|
|
|
|
eprintf("MANFID", "%04x,%04x", s->manfid.manf, s->manfid.card);
|
|
eprintf("FUNCID", "%d", s->funcid.func);
|
|
for (i = 0; i < 4; i++) {
|
|
sprintf(m, "PRODID_%d", i+1);
|
|
if (i < s->prodid.ns)
|
|
setenv(m, s->prodid.str+s->prodid.ofs[i], 1);
|
|
else
|
|
unsetenv(m);
|
|
}
|
|
eprintf("SOCKET", "%d", sn);
|
|
|
|
for (k = 0, bind = s->bind[fn]; bind != NULL; k++, bind = bind->next)
|
|
if (bind->name[0] && (bind->name[2] != '#')) {
|
|
setenv("CLASS", class, 1);
|
|
setenv("DRIVER", bind->dev_info, 1);
|
|
eprintf("INSTANCE", "%d", k);
|
|
if (bind->major) {
|
|
eprintf("MAJOR", "%d", bind->major);
|
|
eprintf("MINOR", "%d", bind->minor);
|
|
} else {
|
|
unsetenv("MAJOR");
|
|
unsetenv("MINOR");
|
|
}
|
|
ret |= execute_on_dev(cmd, class, bind->name);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*====================================================================*/
|
|
|
|
typedef struct module_list_t {
|
|
char *mod;
|
|
int usage;
|
|
struct module_list_t *next;
|
|
} module_list_t;
|
|
|
|
static module_list_t *module_list = NULL;
|
|
|
|
static int try_insmod(char *mod, char *opts)
|
|
{
|
|
char *cmd = malloc(strlen(mod) + strlen(modpath) +
|
|
(opts ? strlen(opts) : 0) + 30);
|
|
int ret;
|
|
|
|
strcpy(cmd, "insmod ");
|
|
if (strchr(mod, '/') != NULL)
|
|
sprintf(cmd+7, "%s/%s.o", modpath, mod);
|
|
else
|
|
sprintf(cmd+7, "%s/pcmcia/%s.o", modpath, mod);
|
|
if (access(cmd+7, R_OK) != 0) {
|
|
syslog(LOG_INFO, "module %s not available", cmd+7);
|
|
free(cmd);
|
|
return -1;
|
|
}
|
|
if (opts) {
|
|
strcat(cmd, " ");
|
|
strcat(cmd, opts);
|
|
}
|
|
ret = execute("insmod", cmd);
|
|
free(cmd);
|
|
return ret;
|
|
}
|
|
|
|
static int try_modprobe(char *mod, char *opts)
|
|
{
|
|
char *cmd = malloc(strlen(mod) + (opts ? strlen(opts) : 0) + 20);
|
|
char *s = strrchr(mod, '/');
|
|
int ret;
|
|
|
|
sprintf(cmd, "modprobe %s", (s) ? s+1 : mod);
|
|
if (opts) {
|
|
strcat(cmd, " ");
|
|
strcat(cmd, opts);
|
|
}
|
|
ret = execute("modprobe", cmd);
|
|
free(cmd);
|
|
return ret;
|
|
}
|
|
|
|
static void install_module(char *mod, char *opts)
|
|
{
|
|
module_list_t *ml;
|
|
|
|
for (ml = module_list; ml != NULL; ml = ml->next)
|
|
if (strcmp(mod, ml->mod) == 0) break;
|
|
if (ml == NULL) {
|
|
ml = (module_list_t *)malloc(sizeof(struct module_list_t));
|
|
ml->mod = mod;
|
|
ml->usage = 0;
|
|
ml->next = module_list;
|
|
module_list = ml;
|
|
}
|
|
ml->usage++;
|
|
if (ml->usage != 1)
|
|
return;
|
|
|
|
if (access("/proc/bus/pccard/drivers", R_OK) == 0) {
|
|
FILE *f = fopen("/proc/bus/pccard/drivers", "r");
|
|
if (f) {
|
|
char a[61], s[33];
|
|
while (fgets(a, 60, f)) {
|
|
int is_kernel;
|
|
sscanf(a, "%s %d", s, &is_kernel);
|
|
if (strcmp(s, mod) != 0) continue;
|
|
/* If it isn't a module, we won't try to rmmod */
|
|
ml->usage += is_kernel;
|
|
fclose(f);
|
|
return;
|
|
}
|
|
fclose(f);
|
|
}
|
|
}
|
|
|
|
if (do_modprobe) {
|
|
if (try_modprobe(mod, opts) != 0)
|
|
try_insmod(mod, opts);
|
|
} else {
|
|
if (try_insmod(mod, opts) != 0)
|
|
try_modprobe(mod, opts);
|
|
}
|
|
}
|
|
|
|
static void remove_module(char *mod)
|
|
{
|
|
char *s, cmd[128];
|
|
module_list_t *ml;
|
|
|
|
for (ml = module_list; ml != NULL; ml = ml->next)
|
|
if (strcmp(mod, ml->mod) == 0) break;
|
|
if (ml != NULL) {
|
|
ml->usage--;
|
|
if (ml->usage == 0) {
|
|
/* Strip off leading path names */
|
|
s = strrchr(mod, '/');
|
|
s = (s) ? s+1 : mod;
|
|
sprintf(cmd, do_modprobe ? "modprobe -r %s" : "rmmod %s", s);
|
|
execute(do_modprobe ? "modprobe" : "rmmod", cmd);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*====================================================================*/
|
|
|
|
static mtd_ident_t *lookup_mtd(region_info_t *region)
|
|
{
|
|
mtd_ident_t *mtd;
|
|
int match = 0;
|
|
|
|
for (mtd = root_mtd; mtd; mtd = mtd->next) {
|
|
switch (mtd->mtd_type) {
|
|
case JEDEC_MTD:
|
|
if ((mtd->jedec_mfr == region->JedecMfr) &&
|
|
(mtd->jedec_info == region->JedecInfo)) {
|
|
match = 1;
|
|
break;
|
|
}
|
|
case DTYPE_MTD:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (match) break;
|
|
}
|
|
if (mtd)
|
|
return mtd;
|
|
else
|
|
return default_mtd;
|
|
}
|
|
|
|
/*====================================================================*/
|
|
|
|
static void bind_mtd(int sn)
|
|
{
|
|
socket_info_t *s = &socket[sn];
|
|
region_info_t region;
|
|
bind_info_t bind;
|
|
mtd_info_t mtd_info;
|
|
mtd_ident_t *mtd;
|
|
int i, attr, ret, nr;
|
|
|
|
nr = 0;
|
|
for (attr = 0; attr < 2; attr++) {
|
|
region.Attributes = attr;
|
|
ret = ioctl(s->fd, DS_GET_FIRST_REGION, ®ion);
|
|
while (ret == 0) {
|
|
mtd = lookup_mtd(®ion);
|
|
if (mtd) {
|
|
/* Have we seen this MTD before? */
|
|
for (i = 0; i < nr; i++)
|
|
if (s->mtd[i] == mtd) break;
|
|
if (i == nr) {
|
|
install_module(mtd->module, mtd->opts);
|
|
s->mtd[nr] = mtd;
|
|
mtd->refs++;
|
|
nr++;
|
|
}
|
|
syslog(LOG_INFO, " %s memory region at 0x%x: %s",
|
|
attr ? "Attribute" : "Common", region.CardOffset,
|
|
mtd->name);
|
|
/* Bind MTD to this region */
|
|
strcpy(mtd_info.dev_info, s->mtd[i]->module);
|
|
mtd_info.Attributes = region.Attributes;
|
|
mtd_info.CardOffset = region.CardOffset;
|
|
if (ioctl(s->fd, DS_BIND_MTD, &mtd_info) != 0) {
|
|
syslog(LOG_INFO,
|
|
"bind MTD '%s' to region at 0x%x failed: %m",
|
|
(char *)mtd_info.dev_info, region.CardOffset);
|
|
}
|
|
}
|
|
ret = ioctl(s->fd, DS_GET_NEXT_REGION, ®ion);
|
|
}
|
|
}
|
|
s->mtd[nr] = NULL;
|
|
|
|
/* Now bind each unique MTD as a normal client of this socket */
|
|
for (i = 0; i < nr; i++) {
|
|
strcpy(bind.dev_info, s->mtd[i]->module);
|
|
bind.function = 0;
|
|
if (ioctl(s->fd, DS_BIND_REQUEST, &bind) != 0)
|
|
syslog(LOG_INFO, "bind MTD '%s' to socket %d failed: %m",
|
|
(char *)bind.dev_info, sn);
|
|
}
|
|
}
|
|
|
|
/*====================================================================*/
|
|
|
|
static void update_cis(socket_info_t *s)
|
|
{
|
|
cisdump_t cis;
|
|
FILE *f = fopen(s->card->cis_file, "r");
|
|
if (f == NULL)
|
|
syslog(LOG_ERR, "could not open '%s': %m", s->card->cis_file);
|
|
else {
|
|
cis.Length = fread(cis.Data, 1, CISTPL_MAX_CIS_SIZE, f);
|
|
fclose(f);
|
|
if (ioctl(s->fd, DS_REPLACE_CIS, &cis) != 0)
|
|
syslog(LOG_ERR, "could not replace CIS: %m");
|
|
}
|
|
}
|
|
|
|
/*====================================================================*/
|
|
|
|
static void do_insert(int sn)
|
|
{
|
|
socket_info_t *s = &socket[sn];
|
|
card_info_t *card;
|
|
device_info_t **dev;
|
|
bind_info_t *bind, **tail;
|
|
int i, j, ret;
|
|
|
|
/* Already identified? */
|
|
if ((s->card != NULL) && (s->card != blank_card))
|
|
return;
|
|
|
|
if (verbose) syslog(LOG_INFO, "initializing socket %d", sn);
|
|
card = lookup_card(sn);
|
|
if (s->state & SOCKET_HOTPLUG) {
|
|
write_stab();
|
|
return;
|
|
}
|
|
/* Make sure we've learned something new before continuing */
|
|
if (card == s->card)
|
|
return;
|
|
s->card = card;
|
|
card->refs++;
|
|
if (card->cis_file) update_cis(s);
|
|
|
|
dev = card->device;
|
|
|
|
/* Set up MTD's */
|
|
for (i = 0; i < card->bindings; i++)
|
|
if (dev[i]->needs_mtd)
|
|
break;
|
|
if (i < card->bindings)
|
|
bind_mtd(sn);
|
|
|
|
/* Install kernel modules */
|
|
for (i = 0; i < card->bindings; i++) {
|
|
dev[i]->refs++;
|
|
for (j = 0; j < dev[i]->modules; j++)
|
|
install_module(dev[i]->module[j], dev[i]->opts[j]);
|
|
}
|
|
|
|
/* Bind drivers by their dev_info identifiers */
|
|
for (i = 0; i < card->bindings; i++) {
|
|
bind = calloc(1, sizeof(bind_info_t));
|
|
strcpy((char *)bind->dev_info, (char *)dev[i]->dev_info);
|
|
if (strcmp(bind->dev_info, "cb_enabler") == 0)
|
|
bind->function = BIND_FN_ALL;
|
|
else
|
|
bind->function = card->dev_fn[i];
|
|
if (ioctl(s->fd, DS_BIND_REQUEST, bind) != 0) {
|
|
if (errno == EBUSY) {
|
|
syslog(LOG_INFO, "'%s' already bound to socket %d",
|
|
(char *)bind->dev_info, sn);
|
|
} else {
|
|
syslog(LOG_INFO, "bind '%s' to socket %d failed: %m",
|
|
(char *)bind->dev_info, sn);
|
|
beep(BEEP_TIME, BEEP_ERR);
|
|
write_stab();
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (ret = j = 0; j < 10; j++) {
|
|
ret = ioctl(s->fd, DS_GET_DEVICE_INFO, bind);
|
|
if ((ret == 0) || (errno != EAGAIN))
|
|
break;
|
|
usleep(100000);
|
|
}
|
|
if (ret != 0) {
|
|
syslog(LOG_INFO, "get dev info on socket %d failed: %m",
|
|
sn);
|
|
if ((errno == EAGAIN) &&
|
|
(strcmp(dev[i]->module[dev[i]->modules-1],
|
|
(char *)bind->dev_info) != 0))
|
|
syslog(LOG_INFO, "wrong module '%s' for device '%s'?",
|
|
dev[i]->module[dev[i]->modules-1],
|
|
(char *)bind->dev_info);
|
|
ioctl(s->fd, DS_UNBIND_REQUEST, bind);
|
|
beep(BEEP_TIME, BEEP_ERR);
|
|
write_stab();
|
|
return;
|
|
}
|
|
tail = &s->bind[i];
|
|
while (ret == 0) {
|
|
bind_info_t *old;
|
|
if ((strlen(bind->name) > 3) && (bind->name[2] == '#'))
|
|
xlate_scsi_name(bind);
|
|
old = *tail = bind; tail = (bind_info_t **)&bind->next;
|
|
bind = (bind_info_t *)malloc(sizeof(bind_info_t));
|
|
memcpy(bind, old, sizeof(bind_info_t));
|
|
ret = ioctl(s->fd, DS_GET_NEXT_DEVICE, bind);
|
|
}
|
|
*tail = NULL; free(bind);
|
|
write_stab();
|
|
}
|
|
|
|
/* Run "start" commands */
|
|
for (i = ret = 0; i < card->bindings; i++) {
|
|
char *class = EITHER_OR(card->class[i], dev[i]->class);
|
|
if (class)
|
|
ret |= execute_on_all("start", class, sn, i);
|
|
}
|
|
beep(BEEP_TIME, (ret) ? BEEP_ERR : BEEP_OK);
|
|
|
|
}
|
|
|
|
/*====================================================================*/
|
|
|
|
static int do_check(int sn)
|
|
{
|
|
socket_info_t *s = &socket[sn];
|
|
card_info_t *card;
|
|
device_info_t **dev;
|
|
int i;
|
|
|
|
card = s->card;
|
|
if (card == NULL)
|
|
return 0;
|
|
|
|
/* Run "check" commands */
|
|
dev = card->device;
|
|
for (i = 0; i < card->bindings; i++) {
|
|
char *class = EITHER_OR(card->class[i], dev[i]->class);
|
|
if (class && execute_on_all("check", class, sn, i))
|
|
return CS_IN_USE;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*====================================================================*/
|
|
|
|
static void do_remove(int sn)
|
|
{
|
|
socket_info_t *s = &socket[sn];
|
|
card_info_t *card;
|
|
device_info_t **dev;
|
|
bind_info_t *bind;
|
|
int i, j;
|
|
|
|
if (verbose) syslog(LOG_INFO, "shutting down socket %d", sn);
|
|
|
|
card = s->card;
|
|
if (card == NULL)
|
|
goto done;
|
|
|
|
/* Run "stop" commands */
|
|
dev = card->device;
|
|
for (i = 0; i < card->bindings; i++) {
|
|
char *class = EITHER_OR(card->class[i], dev[i]->class);
|
|
if (class)
|
|
execute_on_all("stop", class, sn, i);
|
|
}
|
|
|
|
/* unbind driver instances */
|
|
for (i = 0; i < card->bindings; i++) {
|
|
if (s->bind[i]) {
|
|
if (ioctl(s->fd, DS_UNBIND_REQUEST, s->bind[i]) != 0)
|
|
syslog(LOG_INFO, "unbind '%s' from socket %d failed: %m",
|
|
(char *)s->bind[i]->dev_info, sn);
|
|
while (s->bind[i]) {
|
|
bind = s->bind[i];
|
|
s->bind[i] = bind->next;
|
|
free(bind);
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; (s->mtd[i] != NULL); i++) {
|
|
bind_info_t b;
|
|
strcpy(b.dev_info, s->mtd[i]->module);
|
|
b.function = 0;
|
|
if (ioctl(s->fd, DS_UNBIND_REQUEST, &b) != 0)
|
|
syslog(LOG_INFO, "unbind MTD '%s' from socket %d failed: %m",
|
|
s->mtd[i]->module, sn);
|
|
}
|
|
|
|
/* remove kernel modules in inverse order */
|
|
for (i = 0; i < card->bindings; i++) {
|
|
for (j = dev[i]->modules-1; j >= 0; j--)
|
|
remove_module(dev[i]->module[j]);
|
|
free_device(dev[i]);
|
|
}
|
|
/* Remove any MTD's bound to this socket */
|
|
for (i = 0; (s->mtd[i] != NULL); i++) {
|
|
remove_module(s->mtd[i]->module);
|
|
free_mtd(s->mtd[i]);
|
|
s->mtd[i] = NULL;
|
|
}
|
|
|
|
done:
|
|
beep(BEEP_TIME, BEEP_OK);
|
|
free_card(card);
|
|
s->card = NULL;
|
|
write_stab();
|
|
}
|
|
|
|
/*====================================================================*/
|
|
|
|
static void do_suspend(int sn)
|
|
{
|
|
socket_info_t *s = &socket[sn];
|
|
card_info_t *card;
|
|
device_info_t **dev;
|
|
int i;
|
|
|
|
card = s->card;
|
|
if (card == NULL)
|
|
return;
|
|
dev = card->device;
|
|
for (i = 0; i < card->bindings; i++) {
|
|
char *class = EITHER_OR(card->class[i], dev[i]->class);
|
|
if (class && execute_on_all("suspend", class, sn, i))
|
|
beep(BEEP_TIME, BEEP_ERR);
|
|
}
|
|
}
|
|
|
|
/*====================================================================*/
|
|
|
|
static void do_resume(int sn)
|
|
{
|
|
socket_info_t *s = &socket[sn];
|
|
card_info_t *card;
|
|
device_info_t **dev;
|
|
int i;
|
|
|
|
card = s->card;
|
|
if (card == NULL)
|
|
return;
|
|
dev = card->device;
|
|
for (i = 0; i < card->bindings; i++) {
|
|
char *class = EITHER_OR(card->class[i], dev[i]->class);
|
|
if (class && execute_on_all("resume", class, sn, i))
|
|
beep(BEEP_TIME, BEEP_ERR);
|
|
}
|
|
}
|
|
|
|
/*====================================================================*/
|
|
|
|
static void wait_for_pending(void)
|
|
{
|
|
cs_status_t status;
|
|
int i;
|
|
status.Function = 0;
|
|
for (;;) {
|
|
usleep(100000);
|
|
for (i = 0; i < sockets; i++)
|
|
if ((ioctl(socket[i].fd, DS_GET_STATUS, &status) == 0) &&
|
|
(status.CardState & CS_EVENT_CARD_INSERTION))
|
|
break;
|
|
if (i == sockets) break;
|
|
}
|
|
}
|
|
|
|
/*====================================================================*/
|
|
|
|
static void free_resources(void)
|
|
{
|
|
adjust_list_t *al;
|
|
int fd = socket[0].fd;
|
|
|
|
for (al = root_adjust; al; al = al->next) {
|
|
if (al->adj.Action == ADD_MANAGED_RESOURCE) {
|
|
al->adj.Action = REMOVE_MANAGED_RESOURCE;
|
|
ioctl(fd, DS_ADJUST_RESOURCE_INFO, &al->adj);
|
|
} else if ((al->adj.Action == REMOVE_MANAGED_RESOURCE) &&
|
|
(al->adj.Resource == RES_IRQ)) {
|
|
al->adj.Action = ADD_MANAGED_RESOURCE;
|
|
ioctl(fd, DS_ADJUST_RESOURCE_INFO, &al->adj);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/*====================================================================*/
|
|
|
|
static void adjust_resources(void)
|
|
{
|
|
adjust_list_t *al;
|
|
int ret;
|
|
char tmp[64];
|
|
int fd = socket[0].fd;
|
|
|
|
for (al = root_adjust; al; al = al->next) {
|
|
ret = ioctl(fd, DS_ADJUST_RESOURCE_INFO, &al->adj);
|
|
if (ret != 0) {
|
|
switch (al->adj.Resource) {
|
|
case RES_MEMORY_RANGE:
|
|
sprintf(tmp, "memory %#lx-%#lx",
|
|
al->adj.resource.memory.Base,
|
|
al->adj.resource.memory.Base +
|
|
al->adj.resource.memory.Size - 1);
|
|
break;
|
|
case RES_IO_RANGE:
|
|
sprintf(tmp, "IO ports %#x-%#x",
|
|
al->adj.resource.io.BasePort,
|
|
al->adj.resource.io.BasePort +
|
|
al->adj.resource.io.NumPorts - 1);
|
|
break;
|
|
case RES_IRQ:
|
|
sprintf(tmp, "irq %u", al->adj.resource.irq.IRQ);
|
|
break;
|
|
}
|
|
syslog(LOG_INFO, "could not adjust resource: %s: %m", tmp);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*====================================================================*/
|
|
|
|
static void fork_now(void)
|
|
{
|
|
int ret;
|
|
if ((ret = fork()) > 0)
|
|
_exit(0);
|
|
if (ret == -1)
|
|
syslog(LOG_ERR, "forking: %m");
|
|
if (setsid() < 0)
|
|
syslog(LOG_ERR, "detaching from tty: %m");
|
|
}
|
|
|
|
static void done(void)
|
|
{
|
|
syslog(LOG_INFO, "exiting");
|
|
unlink(pidfile);
|
|
unlink(stabfile);
|
|
}
|
|
|
|
/*====================================================================*/
|
|
|
|
/* most recent signal */
|
|
static int caught_signal = 0;
|
|
|
|
static void catch_signal(int sig)
|
|
{
|
|
caught_signal = sig;
|
|
if (signal(sig, catch_signal) == SIG_ERR)
|
|
syslog(LOG_INFO, "signal(%d): %m", sig);
|
|
}
|
|
|
|
static void handle_signal(void)
|
|
{
|
|
int i;
|
|
switch (caught_signal) {
|
|
case SIGTERM:
|
|
case SIGINT:
|
|
for (i = 0; i < sockets; i++)
|
|
if ((socket[i].state & SOCKET_PRESENT) &&
|
|
(do_check(i) == 0)) do_remove(i);
|
|
free_resources();
|
|
exit(0);
|
|
break;
|
|
case SIGHUP:
|
|
free_resources();
|
|
free_config();
|
|
syslog(LOG_INFO, "re-loading config file");
|
|
load_config();
|
|
adjust_resources();
|
|
break;
|
|
#ifdef SIGPWR
|
|
case SIGPWR:
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*====================================================================*/
|
|
|
|
static int init_sockets(void)
|
|
{
|
|
int fd, i;
|
|
|
|
major = lookup_dev("pcmcia");
|
|
if (major < 0) {
|
|
if (major == -ENODEV)
|
|
syslog(LOG_ERR, "no pcmcia driver in /proc/devices");
|
|
else
|
|
syslog(LOG_ERR, "could not open /proc/devices: %m");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
for (fd = -1, i = 0; i < MAX_SOCKS; i++) {
|
|
fd = open_sock(i, S_IFCHR|S_IREAD|S_IWRITE);
|
|
if (fd < 0) break;
|
|
socket[i].fd = fd;
|
|
socket[i].state = 0;
|
|
}
|
|
if ((fd < 0) && (errno != ENODEV) && (errno != ENOENT))
|
|
syslog(LOG_ERR, "open_sock(socket %d) failed: %m", i);
|
|
sockets = i;
|
|
if (sockets == 0) {
|
|
if (errno == ENODEV)
|
|
syslog(LOG_ERR, "no sockets found!");
|
|
else if (errno == EBUSY)
|
|
syslog(LOG_ERR, "another cardmgr is already running?");
|
|
return -1;
|
|
} else
|
|
syslog(LOG_INFO, "watching %d sockets", sockets);
|
|
|
|
adjust_resources();
|
|
return 0;
|
|
}
|
|
|
|
/*====================================================================*/
|
|
|
|
int cardmgr_call(void)
|
|
{
|
|
int i, max_fd, ret, event, pass;
|
|
int delay_fork = 0;
|
|
struct timeval tv;
|
|
fd_set fds;
|
|
|
|
if (access("/var/lib/pcmcia", R_OK) == 0) {
|
|
stabfile = "/var/lib/pcmcia/stab";
|
|
} else {
|
|
stabfile = "/var/run/stab";
|
|
}
|
|
do_modprobe = (access("/sbin/modprobe", X_OK) == 0);
|
|
|
|
//openlog("cardmgr", LOG_PID|LOG_PERROR, LOG_DAEMON);
|
|
|
|
/*
|
|
putenv("PATH=/bin:/sbin:/usr/bin:/usr/sbin");
|
|
if (verbose)
|
|
putenv("VERBOSE=1");
|
|
if (one_pass)
|
|
putenv("ONEPASS=1");
|
|
*/
|
|
if (modpath == NULL) {
|
|
if (access("/lib/modules/preferred", X_OK) == 0)
|
|
modpath = "/lib/modules/preferred";
|
|
else {
|
|
struct utsname utsname;
|
|
if (uname(&utsname) != 0) {
|
|
log_message("CM: uname(): %m");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
modpath = (char *)malloc(strlen(utsname.release)+14);
|
|
sprintf(modpath, "/lib/modules/%s", utsname.release);
|
|
}
|
|
}
|
|
if (access(modpath, X_OK) != 0)
|
|
syslog(LOG_INFO, "cannot access %s: %m", modpath);
|
|
/* We default to using modprobe if it is available */
|
|
do_modprobe |= (access("/sbin/modprobe", X_OK) == 0);
|
|
|
|
load_config();
|
|
|
|
if (init_sockets() != 0)
|
|
exit(EXIT_FAILURE);
|
|
|
|
closelog();
|
|
close(0); close(1); close(2);
|
|
if (!delay_fork && !one_pass)
|
|
fork_now();
|
|
log_message("CM: cardmgr/hacked starting, version is " CS_RELEASE);
|
|
|
|
/* If we've gotten this far, then clean up pid and stab at exit */
|
|
atexit(&done);
|
|
write_pid();
|
|
write_stab();
|
|
|
|
for (i = max_fd = 0; i < sockets; i++)
|
|
max_fd = (socket[i].fd > max_fd) ? socket[i].fd : max_fd;
|
|
|
|
/* First select() call: poll, don't wait */
|
|
tv.tv_sec = tv.tv_usec = 0;
|
|
|
|
/* Wait for sockets in setup-pending state to settle */
|
|
if (one_pass || delay_fork)
|
|
wait_for_pending();
|
|
|
|
for (pass = 0; ; pass++) {
|
|
FD_ZERO(&fds);
|
|
for (i = 0; i < sockets; i++)
|
|
FD_SET(socket[i].fd, &fds);
|
|
|
|
while ((ret = select(max_fd+1, &fds, NULL, NULL,
|
|
((pass == 0) ? &tv : NULL))) < 0) {
|
|
if (errno == EINTR) {
|
|
handle_signal();
|
|
} else {
|
|
log_message("CM: select(): %m");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < sockets; i++) {
|
|
if (!FD_ISSET(socket[i].fd, &fds))
|
|
continue;
|
|
ret = read(socket[i].fd, &event, 4);
|
|
if ((ret == -1) && (errno != EAGAIN))
|
|
log_message("CM: read(%d): %m\n", i);
|
|
if (ret != 4)
|
|
continue;
|
|
|
|
switch (event) {
|
|
case CS_EVENT_CARD_REMOVAL:
|
|
socket[i].state = 0;
|
|
do_remove(i);
|
|
break;
|
|
case CS_EVENT_EJECTION_REQUEST:
|
|
ret = do_check(i);
|
|
if (ret == 0) {
|
|
socket[i].state = 0;
|
|
do_remove(i);
|
|
}
|
|
write(socket[i].fd, &ret, 4);
|
|
break;
|
|
case CS_EVENT_CARD_INSERTION:
|
|
case CS_EVENT_INSERTION_REQUEST:
|
|
socket[i].state |= SOCKET_PRESENT;
|
|
case CS_EVENT_CARD_RESET:
|
|
socket[i].state |= SOCKET_READY;
|
|
do_insert(i);
|
|
break;
|
|
case CS_EVENT_RESET_PHYSICAL:
|
|
socket[i].state &= ~SOCKET_READY;
|
|
break;
|
|
case CS_EVENT_PM_SUSPEND:
|
|
do_suspend(i);
|
|
break;
|
|
case CS_EVENT_PM_RESUME:
|
|
do_resume(i);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (one_pass)
|
|
exit(EXIT_SUCCESS);
|
|
if (delay_fork) {
|
|
fork_now();
|
|
write_pid();
|
|
delay_fork = 0;
|
|
}
|
|
|
|
} /* repeat */
|
|
return 0;
|
|
}
|