[RFC] pccardctl -- a sysfs-based insert/eject tool
Dominik Brodowski
linux at dominikbrodowski.de
Mon Jan 17 14:47:18 EST 2005
Hi!
This small(?) tool depending on libsysfs from sysfsutils-1.0.0 or later
allows for "eject" and "insert" commands to both 16-bit PCMCIA and 32-bit
CardBus cards using the new sysfs-based interface located in
/sys/class/pcmcia_socket/ . As it uses libsysfs which is distributed under
the LGPL, and that license seems to be incompatible with the MPL, I chose to
use the GPLv2ption of cardctl.c from pcmcia-cs.
To compile it, save this message to a file, remove everything up to and
including "---", and run "gcc -Wall -O2 -lsysfs pccardctl.c" or however you
named it :-)
Feedback is most welcome.
Thanks,
Dominik
---
/*
* (C) 2004 Dominik Brodowski <linux at dominikbrodowski.de>
*
* Partly based on cardctl.c from pcmcia-cs-3.2.7/cardmgr/, which states
* in its header:
*
* 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.
*
* Licensed under the terms of the GNU GPL License version 2.
*/
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <libintl.h>
#include <locale.h>
#include <sysfs/libsysfs.h>
#define _GNU_SOURCE
#include <getopt.h>
#define gettext(String) (String)
#define gettext_noop(String) (String)
#define MAX_SOCKET 8
static int pccardctl_echo_one(unsigned long socket_no, const char *in_file)
{
int ret;
char file[SYSFS_PATH_MAX];
struct sysfs_attribute *attr;
if (!file)
return -EINVAL;
snprintf(file, SYSFS_PATH_MAX, "/sys/class/pcmcia_socket/pcmcia_socket%lu/%s",
socket_no, in_file);
attr = sysfs_open_attribute(file);
if (!attr)
return -ENODEV;
ret = sysfs_write_attribute(attr, "42", 2);
sysfs_close_attribute(attr);
return (ret);
}
static int pccardctl_socket_exists(unsigned long socket_no)
{
char file[SYSFS_PATH_MAX];
snprintf(file, SYSFS_PATH_MAX,
"/sys/class/pcmcia_socket/pcmcia_socket%lu/card_insert",
socket_no);
return (!(sysfs_path_is_file(file)));
}
static char * pccardctl_get_string(unsigned long socket_no, const char *in_file)
{
int ret, len;
char value[SYSFS_PATH_MAX];
char file[SYSFS_PATH_MAX];
char *result;
snprintf(file, SYSFS_PATH_MAX, "/sys/bus/pcmcia/devices/%lu.0/%s",
socket_no, in_file);
ret = sysfs_read_attribute_value(file, value, SYSFS_PATH_MAX);
if (ret)
return NULL;
len = strlen(value);
if (len < 2)
return NULL;
result = malloc(sizeof(char) * len);
if (!result)
return NULL;
strncpy(result, value, len);
result[len - 1] = '\0';
return (result);
}
static int pccardctl_get_one(unsigned long socket_no, const char *in_file, unsigned int *result)
{
int ret;
char value[SYSFS_PATH_MAX];
char file[SYSFS_PATH_MAX];
snprintf(file, SYSFS_PATH_MAX, "/sys/bus/pcmcia/devices/%lu.0/%s",
socket_no, in_file);
ret = sysfs_read_attribute_value(file, value, SYSFS_PATH_MAX);
if (!ret)
sscanf(value, "0x%X", result);
return (ret);
}
static int pccardctl_ident(unsigned long socket_no)
{
static char *fn[] = {
"multifunction",
"memory",
"serial",
"parallel",
"fixed disk",
"video",
"network",
"AIMS",
"SCSI"
};
char *prod_id[4];
int valid_prod_id = 0;
int i;
unsigned int manf_id, card_id;
if (!pccardctl_socket_exists(socket_no))
return -ENODEV;
for (i=1; i<=4; i++) {
char file[SYSFS_PATH_MAX];
snprintf(file, SYSFS_PATH_MAX, "prod_id%u", i);
prod_id[i-1] = pccardctl_get_string(socket_no, file);
if (prod_id[i-1])
valid_prod_id++;
}
if (valid_prod_id) {
printf(" product info: ");
for (i=0;i<4;i++) {
printf("%s\"%s\"", (i>0) ? ", " : "",
prod_id[i] ? prod_id[i] : "" );
if (prod_id[i])
free(prod_id[i]);
}
printf("\n");
} else
printf(" no product info available\n");
if (!pccardctl_get_one(socket_no, "manf_id", &manf_id))
if (!pccardctl_get_one(socket_no, "card_id", &card_id))
printf(" manfid: 0x%04x, 0x%04x\n", manf_id, card_id);
if (!pccardctl_get_one(socket_no, "func_id", &manf_id))
printf(" function: %d (%s)\n", manf_id, fn[manf_id]);
return 0;
}
static int pccardctl_info(unsigned long socket_no)
{
int i;
unsigned int manf_id, card_id, func_id;
char *prod_id;
if (!pccardctl_socket_exists(socket_no))
return -ENODEV;
for (i=1; i<=4; i++) {
char file[SYSFS_PATH_MAX];
snprintf(file, SYSFS_PATH_MAX, "prod_id%u", i);
prod_id = pccardctl_get_string(socket_no, file);
printf("PRODID_%d=\"%s\"\n", i, (prod_id) ? prod_id : "");
free(prod_id);
prod_id = NULL;
}
printf("MANFID=%04x,%04x\n",
(!pccardctl_get_one(socket_no, "manf_id", &manf_id)) ? manf_id : 0,
(!pccardctl_get_one(socket_no, "card_id", &card_id)) ? card_id : 0);
printf("FUNCID=%d\n",
(!pccardctl_get_one(socket_no, "func_id", &func_id)) ? func_id : 255);
return 0;
}
static void print_header(void) {
printf("pccardctl (C) 2004 Dominik Brodowski, (C) 1999 David A. Hinds\n");
printf(gettext ("Report errors and bugs to <linux-pcmcia at lists.infradead.org>, please.\n"));
}
static void print_help(void) {
/* TBD */
}
static void print_unknown_arg(void) {
print_header();
printf(gettext ("invalid or unknown argument\n"));
print_help();
}
static struct option pccardctl_opts[] = {
{ .name="version", .has_arg=no_argument, .flag=NULL, .val='V'},
{ .name="help", .has_arg=no_argument, .flag=NULL, .val='h'},
// { .name="socket", .has_arg=required_argument, .flag=NULL, .val='s'},
// { .name="socketdir", .has_arg=required_argument, .flag=NULL, .val='d'},
};
enum {
PCCARDCTL_INSERT,
PCCARDCTL_EJECT,
PCCARDCTL_SUSPEND,
PCCARDCTL_RESUME,
PCCARDCTL_RESET,
PCCARDCTL_INFO,
PCCARDCTL_STATUS,
PCCARDCTL_CONFIG,
PCCARDCTL_IDENT,
NCMD
};
static char *cmdname[] = {
"insert",
"eject",
"suspend",
"resume",
"reset",
"info",
"status",
"config",
"ident",
};
int main(int argc, char **argv) {
extern char *optarg;
extern int optind, opterr, optopt;
int ret = 0, cont = 1;
unsigned long socket = 0;
unsigned int socket_is_set = 0;
char *s;
unsigned int cmd;
do {
ret = getopt_long(argc, argv, "Vhc:f:s:", pccardctl_opts, NULL);
switch (ret) {
case -1:
cont = 0;
break;
case 'V':
print_header();
return 0;
case 'h':
print_header();
print_help();
return 0;
case 'c':
case 's':
case 'f':
/* ignored */
fprintf(stderr, "ignoring parameter '%c'\n", ret);
break;
default:
print_unknown_arg();
return -EINVAL;
}
} while (cont);
if ((argc == optind) || (argc > (optind + 2))) {
print_unknown_arg();
return -EINVAL;
}
/* determine command */
for (cmd = 0; cmd < NCMD; cmd++)
if (strcmp(argv[optind], cmdname[cmd]) == 0)
break;
if (cmd == NCMD) {
print_unknown_arg();
return -EINVAL;
}
if (argc == optind+2) {
socket_is_set = 1;
socket = strtol(argv[optind+1], &s, 0);
if ((*argv[optind+1] == '\0') || (*s != '\0') || (socket >= MAX_SOCKET)) {
print_unknown_arg();
return -EINVAL;
}
}
for (cont = 0; cont < MAX_SOCKET; cont++) {
if (socket_is_set && (socket != cont))
continue;
if (!socket_is_set && !pccardctl_socket_exists(cont))
continue;
if (!socket_is_set && (cmd > PCCARDCTL_INFO))
printf("Socket %d:\n", cont);
ret = 0;
switch (cmd) {
case PCCARDCTL_INSERT:
ret = pccardctl_echo_one(cont, "card_insert");
break;
case PCCARDCTL_EJECT:
ret = pccardctl_echo_one(cont, "card_eject");
break;
case PCCARDCTL_INFO:
ret = pccardctl_info(cont);
break;
case PCCARDCTL_IDENT:
ret = pccardctl_ident(cont);
break;
default:
fprintf(stderr, "command '%s' not yet handled by pccardctl\n", cmdname[cmd]);
return -EAGAIN;
}
if (ret && socket_is_set)
return (ret);
}
return 0;
}
More information about the linux-pcmcia
mailing list