Logo Search packages:      
Sourcecode: pcmciautils version File versions  Download package

pccardctl.c

/*
 *  (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 <libgen.h>
#include <locale.h>
#include <ctype.h>

#include <sysfs/libsysfs.h>

#include <getopt.h>

#define MAX_SOCKET 8

static char *fn[] = {
      "multifunction",
      "memory",
      "serial",
      "parallel",
      "fixed disk",
      "video",
      "network",
      "AIMS",
      "SCSI"
};

/* crc32hash.c - derived from linux/lib/crc32.c, GNU GPL v2 */
static unsigned int crc32(unsigned char const *p, unsigned int len)
{
      int i;
      unsigned int crc = 0;
      while (len--) {
            crc ^= *p++;
            for (i = 0; i < 8; i++)
                  crc = (crc >> 1) ^ ((crc & 1) ? 0xedb88320 : 0);
      }
      return crc;
}


static int pccardctl_power_socket(unsigned long socket_no, unsigned int power)
{
      int ret;
      char file[SYSFS_PATH_MAX];
        struct sysfs_attribute *attr;

      snprintf(file, SYSFS_PATH_MAX,
             "/sys/class/pcmcia_socket/pcmcia_socket%lu/card_pm_state",
             socket_no);

        attr = sysfs_open_attribute(file);
        if (!attr)
                return -ENODEV;

        ret = sysfs_write_attribute(attr, power ? "off" : "on", power ? 3 : 2);

        sysfs_close_attribute(attr);

      return (ret);
}

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 int read_out_file(char * file, char **output)
{
      struct sysfs_attribute *attr = sysfs_open_attribute(file);
      int ret;
      char *result = NULL;

      *output = NULL;

      if (!attr)
            return -EIO;
      ret = sysfs_read_attribute(attr);

      if (ret || !attr->value || !attr->len || (attr->len > SYSFS_PATH_MAX))
            goto close_out;

      result = malloc(attr->len + 1);
      if (result) {
            memcpy(result, attr->value, attr->len);
            result[attr->len] = '\0';
            if (result[attr->len - 1] == '\n')
                  result[attr->len - 1] = '\0';
            *output = result;
      } else
            ret = -ENOMEM;

 close_out:
      sysfs_close_attribute(attr);
      return ret;
}

static int pccardctl_get_string_socket(unsigned long socket_no, const char *in_file, char **output)
{
      char file[SYSFS_PATH_MAX];

      snprintf(file, SYSFS_PATH_MAX, "/sys/class/pcmcia_socket/pcmcia_socket%lu/%s",
             socket_no, in_file);

      return read_out_file(file, output);
}

static int pccardctl_get_string(unsigned long socket_no, const char *in_file, char **output)
{
      char file[SYSFS_PATH_MAX];

      snprintf(file, SYSFS_PATH_MAX, "/sys/bus/pcmcia/devices/%lu.0/%s",
             socket_no, in_file);

      return read_out_file(file, output);
}

static int pccardctl_get_one_f(unsigned long socket_no, unsigned int dev, const char *in_file, unsigned int *result)
{
      char *value;
      char file[SYSFS_PATH_MAX];
      int ret;

      snprintf(file, SYSFS_PATH_MAX, "/sys/bus/pcmcia/devices/%lu.%u/%s",
             socket_no, dev, in_file);
      ret = read_out_file(file, &value);
      if (ret || !value)
            return -EINVAL;

      if (sscanf(value, "0x%X", result) != 1)
            return -EIO;
      return 0;
}

static int pccardctl_get_one(unsigned long socket_no, const char *in_file, unsigned int *result)
{
      return pccardctl_get_one_f(socket_no, 0, in_file, result);
}

static int pccardctl_get_power_device(unsigned long socket_no, unsigned int func)
{
      char * value;
      char file[SYSFS_PATH_MAX];

      snprintf(file, SYSFS_PATH_MAX, "/sys/bus/pcmcia/devices/%lu.%u/pm_state",
             socket_no, func);
      read_out_file(file, &value);
      if (value) {
            if (!strncmp(value, "off", 3))
                  return 1;
            return 0;
      }

      return -ENODEV;
}

static int pccardctl_get_power_socket(unsigned long socket_no)
{
      char * value;
      char file[SYSFS_PATH_MAX];

      snprintf(file, SYSFS_PATH_MAX, "/sys/class/pcmcia_socket/pcmcia_socket%lu/card_pm_state",
             socket_no);
      read_out_file(file, &value);
      if (value) {
            if (!strncmp(value, "off", 3))
                  return 1;
            return 0;
      }

      return -ENODEV;
}


static int pccardctl_ident(unsigned long socket_no)
{
      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);

            pccardctl_get_string(socket_no, file, &prod_id[i-1]);
            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);

            pccardctl_get_string(socket_no, file, &prod_id);

            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 int pccardctl_status(unsigned long socket_no)
{
      char *card_type;
      char *card_voltage;
      int susp;
      int is_cardbus = 0;
      int i, ret;

      if (!pccardctl_socket_exists(socket_no))
            return -ENODEV;

      pccardctl_get_string_socket(socket_no, "card_type", &card_type);
      if (!card_type) {
            printf("  no card\n");
            return 0;
      }

      pccardctl_get_string_socket(socket_no, "card_voltage", &card_voltage);

      strncmp(card_type, "16", 2) ? is_cardbus = 0 : 1;

      printf("  %s %s %s", card_voltage, card_type, is_cardbus ? "CardBus card" : "PC Card");

      susp = pccardctl_get_power_socket(socket_no);
      if (susp > 0)
            printf(" [suspended]");
      printf("\n");

      if (is_cardbus) {
            /* FIXME: handle functions */
            return 0;
      }

      for (i=0; i<4; i++) {
            int function;
            char drv[SYSFS_PATH_MAX];
            char file[SYSFS_PATH_MAX];
            if (pccardctl_get_one_f(socket_no, i, "function", &function))
                  continue;

            printf("  Subdevice %u (function %u)", i, function);

            snprintf(file, SYSFS_PATH_MAX, "/sys/bus/pcmcia/devices/%lu.%u/driver",
                   socket_no, i);
            ret = readlink(file, drv, sizeof(drv) - 1);
            if (ret <= 0)
                  printf(" [unbound]");
            else if (ret > 0) {
                  drv[ret]='\0';
                  printf(" bound to driver \"%s\"", basename(drv));
            }

            susp = pccardctl_get_power_device(socket_no, i);
            if (susp > 0)
                  printf(" [suspended]");

            printf("\n");
      }

      return 0;
}

static void print_header(void) {
      printf("pcmciautils %s\n", PCMCIAUTILS_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[] = {
      "ls", /* needs to be first */
      "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="verbose",      .has_arg=no_argument,         .flag=NULL, .val='v'},
//    { .name="socket", .has_arg=required_argument,   .flag=NULL, .val='s'},
//    { .name="socketdir",    .has_arg=required_argument,   .flag=NULL, .val='d'},
};

static void lspcmcia_socket_available_resources(unsigned long socket_no, char *which) {
      char file[SYSFS_PATH_MAX];
      struct sysfs_attribute *attr;
      int ret, length, first = 0;
      char *sep;
      char *result = NULL;

      snprintf(file, SYSFS_PATH_MAX, "/sys/class/pcmcia_socket/pcmcia_socket%lu/available_resources_%s",
                   socket_no, which);

      attr = sysfs_open_attribute(file);
      if (!attr)
            return;


      ret = sysfs_read_attribute(attr);
      if (ret)
            goto close_out;

      printf("\t\t\tAvailable %s:\t", which[0] == 'i' ? "ioports" : "iomem");

      if (!attr->value || !attr->len || (attr->len < 5))
            goto close_out;

      result = malloc(attr->len + 1);
      if (result) {
            memcpy(result, attr->value, attr->len);
            result[attr->len] = '\0';
            if (result[attr->len - 1] == '\n')
                  result[attr->len - 1] = '\0';
      } else
            goto close_out;

      ret = 0;
      do {
            sep = strchr(&result[ret], '\n');
            if (sep) {
                  length = sep - &result[ret];
                  if (length > SYSFS_PATH_MAX)
                        break;
                  memcpy(file, &result[ret], length);
                  file[length] = '\0';
                  printf("%s\n\t\t\t\t\t\t",file);
                  first++;
                  ret += length + 1;
            }
      } while (sep);
      if (result) {
            printf("%s\n", &result[ret]);
            first++;
      }

 close_out:
      if (!first)
            printf("--none--\n");
      sysfs_close_attribute(attr);
      return;
}

static void lspcmcia_socket(unsigned long socket_no, int verbose, char *driver) {
      char *card_voltage, *card_vpp, *card_vcc, *ready;
      int pm_state;

      pm_state = pccardctl_get_power_socket(socket_no);
      pccardctl_get_string_socket(socket_no, "available_resources_setup_done", &ready);


      printf("\tConfiguration:\tstate: %s\tready: %s\n", pm_state ? "suspended" : "on", ready ? ready : "unknown");

      pccardctl_get_string_socket(socket_no, "card_voltage", &card_voltage);
      pccardctl_get_string_socket(socket_no, "card_vpp", &card_vpp);
      pccardctl_get_string_socket(socket_no, "card_vcc", &card_vcc);
      if (card_voltage && card_vpp && card_vcc)
            printf("\t\t\tVoltage: %s Vcc: %s Vpp: %s\n", card_voltage, card_vcc, card_vpp);

      if (verbose > 1) {
            char *irq_mask_s;
            int i, irqs = 0;
            unsigned int irq_mask;

            pccardctl_get_string_socket(socket_no, "card_irq_mask", &irq_mask_s);
            if (irq_mask_s && sscanf(irq_mask_s, "0x%X", &irq_mask) == 1) {
                  printf("\t\t\tAvailable IRQs: ");
                  for (i=0;i<32;i++) {
                        if (!(irq_mask & (1 <<i)))
                              continue;
                        if (irqs)
                              printf(", ");
                        printf("%d", i);
                        irqs++;
                  }
                  if (!irqs)
                        printf("none");
                  printf("\n");
            }

            lspcmcia_socket_available_resources(socket_no, "io");
            lspcmcia_socket_available_resources(socket_no, "mem");
      }
      return;
}

static int lspcmcia(unsigned long socket_no, int verbose)
{
      char file[SYSFS_PATH_MAX];
      char drv_s[SYSFS_PATH_MAX];
      char dev_s[SYSFS_PATH_MAX];
      char *drv;
      char *dev;
      char *res;
      int ret, i;

      if (!pccardctl_socket_exists(socket_no))
            return -ENODEV;

      snprintf(file, SYSFS_PATH_MAX, "/sys/class/pcmcia_socket/pcmcia_socket%lu/device", socket_no);
      ret = readlink(file, dev_s, sizeof(dev_s) - 1);
      if (ret > 0) {
            dev_s[ret]='\0';
            dev = basename(dev_s);
      } else {
            snprintf(file, SYSFS_PATH_MAX, "/sys/class/pcmcia_socket/pcmcia_socket%lu", socket_no);
            ret = readlink(file, dev_s, sizeof(dev_s) - 1);
            if (ret <= 0)
                  return -ENODEV;
            dev_s[ret]='\0';
            dev = basename(dirname(dev_s));
      }

      snprintf(file, SYSFS_PATH_MAX, "/sys/class/pcmcia_socket/pcmcia_socket%lu/device/driver", socket_no);
      ret = readlink(file, drv_s, sizeof(drv_s) - 1);
      if (ret <= 0) {
            snprintf(file, SYSFS_PATH_MAX, "/sys/class/pcmcia_socket/pcmcia_socket%lu/../driver", socket_no);
            ret = readlink(file, drv_s, sizeof(drv_s) - 1);
            if (ret <= 0)
                  return -ENODEV;
      }
      drv_s[ret]='\0';
      drv = basename(drv_s);

      printf("Socket %lu Bridge:   \t[%s] \t(bus ID: %s)\n", socket_no, drv, dev);
      if (verbose)
            lspcmcia_socket(socket_no, verbose, drv);


      pccardctl_get_string_socket(socket_no, "card_type", &res);
      if (!res)
            return 0;

      if (!strncmp(res, "32", 2)) {
            printf("  CardBus card -- see \"lspci\" for more information\n");
            return 0;
      }

      for (i=0; i<4; i++) {
            int function;

            if (pccardctl_get_one_f(socket_no, i, "function", &function))
                  continue;

            printf("Socket %lu Device %d:\t", socket_no, i);

            snprintf(file, SYSFS_PATH_MAX, "/sys/bus/pcmcia/devices/%lu.%u/driver",
                   socket_no, i);
            ret = readlink(file, drv_s, sizeof(drv_s) - 1);
            if (ret <= 0)
                  printf("[-- no driver --]\t");
            else if (ret > 0) {
                  drv_s[ret]='\0';
                  printf("[%s]\t\t", basename(drv_s));
            }
            printf("(bus ID: %lu.%d)\n", socket_no, i);

            if (verbose) {
                  int j;
                  unsigned int manf_id, card_id;
                  int pm_state = pccardctl_get_power_device(socket_no, i);

                  printf("\tConfiguration:\tstate: %s\n", pm_state ? "suspended" : "on");

                  printf("\tProduct Name:   ");
                  for (j=1;j<=4;j++) {
                        snprintf(file, SYSFS_PATH_MAX, "prod_id%d", j);
                        pccardctl_get_string(socket_no, file, &res);
                        if (res)
                              printf("%s ", res);
                  }
                  printf("\n");

                  printf("\tIdentification:\t");
                  if (!pccardctl_get_one(socket_no, "manf_id", &manf_id))
                        if (!pccardctl_get_one(socket_no, "card_id", &card_id))
                              printf("manf_id: 0x%04x\tcard_id: 0x%04x\n\t\t\t", manf_id, card_id);
                  if (!pccardctl_get_one(socket_no, "func_id", &manf_id))
                        printf("function: %d (%s)\n\t\t\t", manf_id, fn[manf_id]);
                  for (j=1;j<=4;j++) {
                        snprintf(file, SYSFS_PATH_MAX, "prod_id%d", j);
                        pccardctl_get_string(socket_no, file, &res);
                        if (res)
                              printf("prod_id(%u): \"%s\" (0x%08x)\n", j, res, crc32(res, strlen(res)));
                        else
                              printf("prod_id(%u): --- (---)\n", j);
                        if (j<4)
                              printf("\t\t\t");
                  }
            }
      }

      return 0;
}

enum {
      PCCARDCTL_LSPCMCIA, /* needs to be first */
      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;
      int verbose = 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, "Vhvc:f:s:", pccardctl_opts, NULL);
            switch (ret) {
            case -1:
                  cont = 0;
                  break;
            case 'V':
                  print_header();
                  return 0;
            case 'v':
                  verbose++;
                  break;
            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 (strcmp(basename(argv[0]), "lspcmcia") == 0) {
            cmd = 0;
            goto check_socket;
      }

      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;
      }

 check_socket:
      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_LSPCMCIA:
                  ret = lspcmcia(cont, verbose);
                  break;
            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_socket(cont, 1);
                  break;
            case PCCARDCTL_RESET:
                  ret = pccardctl_power_socket(cont, 1);
                  if (ret && socket_is_set)
                        return (ret);
                  /* fall through */
            case PCCARDCTL_RESUME:
                  ret = pccardctl_power_socket(cont, 0);
                  break;
            case PCCARDCTL_STATUS:
                  ret = pccardctl_status(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;
}

Generated by  Doxygen 1.6.0   Back to index