summaryrefslogtreecommitdiff
path: root/src/pcmcia.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pcmcia.c')
-rw-r--r--src/pcmcia.c295
1 files changed, 295 insertions, 0 deletions
diff --git a/src/pcmcia.c b/src/pcmcia.c
new file mode 100644
index 0000000..a2c2ff5
--- /dev/null
+++ b/src/pcmcia.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2006 Dell, Inc.
+ * by Matt Domsch <Matt_Domsch@dell.com>
+ *
+ * Partly based on tools from pcmciautils-014, which states
+ * in its header:
+ * (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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <libgen.h>
+#include <linux/limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+
+#include "state.h"
+#include "pcmcia.h"
+
+#define MAX_SOCKET 8
+
+static unsigned char get_lan_tech(cistpl_funce_t *funce)
+{
+ cistpl_lan_tech_t *t;
+
+ if (funce->type == CISTPL_FUNCE_LAN_TECH) {
+ t = (cistpl_lan_tech_t *)(funce->data);
+ return t->tech;
+ }
+ return 0;
+}
+
+static unsigned char parse_tuple_for_network(tuple_t *tuple, cisparse_t *parse)
+{
+ static int func = 0;
+ unsigned char rc = 0;
+
+ switch (tuple->TupleCode) {
+ case CISTPL_FUNCID:
+ func = parse->funcid.func;
+ break;
+ case CISTPL_FUNCE:
+ if (func == CISTPL_FUNCID_NETWORK)
+ rc = get_lan_tech(&parse->funce);
+ break;
+ default:
+ break;
+ }
+ return rc;
+}
+
+int get_network_type(unsigned int socket_no, unsigned char *network_type)
+{
+ int ret = 0;
+ tuple_t tuple;
+ unsigned char buf[256];
+ cisparse_t parse;
+ unsigned char n;
+
+ memset(&tuple, 0, sizeof(tuple_t));
+
+ ret = read_out_cis(socket_no, NULL);
+ if (ret)
+ return (ret);
+
+ tuple.Attributes = TUPLE_RETURN_LINK | TUPLE_RETURN_COMMON;
+ tuple.DesiredTuple = RETURN_FIRST_TUPLE;
+
+ ret = pcmcia_get_first_tuple(BIND_FN_ALL, &tuple);
+ if (ret)
+ return (ret);
+
+ while(tuple.TupleCode != CISTPL_END) {
+ tuple.TupleData = buf;
+ tuple.TupleOffset = 0;
+ tuple.TupleDataMax = 255;
+
+ pcmcia_get_tuple_data(&tuple);
+
+ ret = pccard_parse_tuple(&tuple, &parse);
+ if (ret == 0) {
+ n = parse_tuple_for_network(&tuple, &parse);
+ if (n) {
+ *network_type = n;
+ break;
+ }
+ }
+
+ ret = pcmcia_get_next_tuple(BIND_FN_ALL, &tuple);
+ if (ret)
+ break;
+ }
+
+ return (ret);
+}
+
+/**
+ * sysfs_path_is_file: Check if the path supplied points to a file
+ * @path: path to validate
+ * Returns 0 if path points to file, 1 otherwise
+ * Copied from sysfsutils-2.1.0 (which is LGPL2.1 or later), relicensed GPLv2 for use here.
+ */
+static int sysfs_path_is_file(const char *path)
+{
+ struct stat astats;
+
+ if (!path) {
+ errno = EINVAL;
+ return 1;
+ }
+ if ((lstat(path, &astats)) != 0) {
+ return 1;
+ }
+ if (S_ISREG(astats.st_mode))
+ return 0;
+
+ return 1;
+}
+
+
+
+static int pccardctl_socket_exists(unsigned long socket_no)
+{
+ char file[PATH_MAX];
+
+ snprintf(file, sizeof(file),
+ "/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)
+{
+ int ret;
+ char *result = NULL;
+ int fd;
+ unsigned long resultsize = 0;
+ ssize_t length = 0;
+
+
+ *output = NULL;
+
+ resultsize = getpagesize() + 1;
+ result = malloc(resultsize);
+ if (!result)
+ return -ENOMEM;
+ memset(result, 0, resultsize);
+
+ fd = open(file, O_RDONLY);
+ if (fd < 0) {
+ ret = -1;
+ goto free_out;
+ }
+
+ length = read(fd, result, resultsize-1);
+ if (length < 0) {
+ close(fd);
+ ret = -1;
+ goto free_out;
+ }
+ result[length] = '\0';
+ if (result[length-1] == '\n')
+ result[length-1] = '\0';
+ *output = result;
+ ret = 0;
+ goto out;
+free_out:
+ free(result);
+out:
+ return ret;
+}
+
+static int pccardctl_get_one_f(unsigned long socket_no, unsigned int dev, const char *in_file, unsigned int *result)
+{
+ char *value;
+ char file[PATH_MAX];
+ int ret;
+
+ snprintf(file, sizeof(file), "/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 alloc_pcmcia(struct libbiosdevname_state *state,
+ unsigned long int socket_no)
+{
+ char file[PATH_MAX];
+ char dev_s[PATH_MAX];
+ char *dev;
+ int ret, i;
+ int rc;
+
+ if (!pccardctl_socket_exists(socket_no))
+ return -ENODEV;
+
+ snprintf(file, sizeof(file), "/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, sizeof(file), "/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));
+ }
+
+ for (i=0; i<4; i++) {
+ unsigned int function;
+ unsigned int function_id;
+ struct pcmcia_device *pdev;
+ unsigned char network_type = 0;
+
+ if (pccardctl_get_one_f(socket_no, i, "function", &function))
+ continue;
+
+ pdev = malloc(sizeof(*pdev));
+ if (!pdev)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&pdev->node);
+ pdev->socket = socket_no;
+ pdev->function = i;
+ if (!pccardctl_get_one(socket_no, "func_id", &function_id))
+ pdev->function_id = function_id;
+
+ rc = get_network_type(socket_no, &network_type);
+ if (!rc)
+ pdev->network_type = network_type;
+
+ list_add_tail(&pdev->node, &state->pcmcia_devices);
+ }
+ return 0;
+}
+
+void free_pcmcia_devices(struct libbiosdevname_state *state)
+{
+ struct pcmcia_device *pos, *next;
+ list_for_each_entry_safe(pos, next, &state->pcmcia_devices, node) {
+ list_del(&pos->node);
+ free(pos);
+ }
+}
+
+int get_pcmcia_devices(struct libbiosdevname_state *state)
+{
+ unsigned long int socket_no;
+ for (socket_no = 0; socket_no < MAX_SOCKET; socket_no++) {
+ alloc_pcmcia(state, socket_no);
+ }
+ return 0;
+}
+
+int unparse_pcmcia_name(char *buf, int size, const struct pcmcia_device *pdev)
+{
+ return snprintf(buf, size, "%u.%u", pdev->socket, pdev->function);
+}
+
+int unparse_pcmcia_device(char *buf, const int size, const struct pcmcia_device *p)
+{
+ char *s = buf;
+ s += snprintf(s, size-(s-buf), "PCMCIA location : ");
+ s += unparse_pcmcia_name(s, size-(s-buf), p);
+ s += snprintf(s, size-(s-buf), "\n");
+ return (s-buf);
+}
+