blob: 02ad3eee93f5f4b2d73a1271efee4d3a5eecadc3 [file] [log] [blame]
/*
* (C) 2004-2005 Dominik Brodowski <linux@brodo.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@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>
#include <getopt.h>
#define MAX_SOCKET 8
static int pccardctl_power_one(unsigned long socket_no, unsigned int device,
unsigned int power)
{
int ret;
char file[SYSFS_PATH_MAX];
struct sysfs_attribute *attr;
snprintf(file, SYSFS_PATH_MAX,
"/sys/bus/pcmcia/devices/%lu.%u/power/state",
socket_no, device);
attr = sysfs_open_attribute(file);
if (!attr)
return -ENODEV;
ret = sysfs_write_attribute(attr, power ? "3" : "0", 1);
sysfs_close_attribute(attr);
return (ret);
}
static int pccardctl_power(unsigned long socket_no, unsigned int power)
{
unsigned int i;
for (i=0; i<2; i++) /* max 2 devices per socket */
pccardctl_power_one(socket_no, i, power);
return 0;
}
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("pcmciautils %s\n", VERSION);
printf("Copyright (C) 2004-2005 Dominik Brodowski, (C) 1999 David A. Hinds\n");
printf("Report errors and bugs to <linux-pcmcia@lists.infradead.org>, please.\n");
}
static char *cmdname[] = {
"insert",
"eject",
"suspend",
"resume",
"reset",
"info",
"status",
"config",
"ident",
};
static void print_help(void) {
unsigned int i;
printf("Usage: pccardctl COMMAND\n");
printf("Supported commands are:\n");
for (i = 0; i < sizeof(cmdname)/sizeof(cmdname[0]); i++) {
printf("\t%s\n", cmdname[i]);
}
}
static void print_unknown_arg(void) {
print_header();
printf("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
};
int main(int argc, char **argv) {
extern char *optarg;
extern int optind, opterr, optopt;
int ret = 0;
unsigned int 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;
case PCCARDCTL_SUSPEND:
ret = pccardctl_power(cont, 3);
break;
case PCCARDCTL_RESET:
ret = pccardctl_power(cont, 3);
if (ret && socket_is_set)
return (ret);
/* fall through */
case PCCARDCTL_RESUME:
ret = pccardctl_power(cont, 0);
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;
}