/*
 * Copyright (c) 1999, 2000  Motoyuki Kasahara.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer,
 *    without modification, immediately at the beginning of the file.
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "pccard.h"

/*
 * Slot information table.
 */
typedef struct {
    char manufacturer[PCCARD_MAX_MANUFACTURER_LENGTH + 1];
    char version[PCCARD_MAX_VERSION_LENGTH + 1];
    char driver[PCCARD_MAX_DRIVER_LENGTH + 1];
    char alias[PCCARD_MAX_ALIAS_LENGTH + 1];
    int status;
} PCCard_Slot;

static PCCard_Slot *slot_table = NULL;

/*
 * The number of slots the system has.
 */
static int number_of_slots = 0;

/*
 * Use an alias file or not.
 */
static char *alias_file_name = NULL;

/*
 * Message handlers.
 */
extern void (*pccard_output_error_message)(const char *, ...);
extern void (*pccard_output_warning_message)(const char *, ...);
extern void (*pccard_output_debug_message)(const char *, ...);

/*
 * Debug flag.
 */
extern int pccard_debug_flag;


/*
 * Open a socket for talking to pccardd, and get current status of slots.
 * If an error occurs, -1 is returned.  Otherwise 0 is returned.
 */
int
pccard_initialize_client(const char *file_name)
{
    if (slot_table != NULL)
	pccard_finalize_client();

    /*
     * Set alias file name if `file_name' is not NULL.
     */
    if (file_name != NULL) {
	alias_file_name = (char *) malloc(strlen(file_name) + 1);
	if (alias_file_name == NULL) {
	    pccard_output_error_message("memory exhausted");
	    return -1;
	}
	strcpy(alias_file_name, file_name);
    }

    /*
     * Open a socket.
     */
    if (pccard_open_socket() == -1)
	return -1;

    /*
     * Get the number of slots the system has.
     */
    number_of_slots = pccard_query_number_of_slots();
    if (number_of_slots == -1)
	return -1;

    /*
     * Allocate memory for pccard table.
     */
    slot_table = (PCCard_Slot *) malloc(sizeof(PCCard_Slot) * number_of_slots);
    if (slot_table == NULL) {
	pccard_output_error_message("memory exhausted");
	return -1;
    }

    /*
     * Get all slots status.
     */
    return pccard_synchronize();
}


/*
 * Get status of all slots to syncrhonisze the slot table.
 * 0 is returned if it succeeds, -1 if doesn't.
 * 
 * NOTE: If another application sends a message to pccardd, pccardd
 * turns to send messages to the application.  To fix the situation,
 * we need to send a request to pccardd once.  We should also ask
 * pccardd about status of all slots, because my slot table may be
 * out of date.
 */
int
pccard_synchronize(void)
{
    PCCard_Slot *slot_entry;
    char manufacturer[PCCARD_MAX_RESPONSE_LENGTH + 1];
    char version[PCCARD_MAX_RESPONSE_LENGTH + 1];
    char driver[PCCARD_MAX_RESPONSE_LENGTH + 1];
    char alias[PCCARD_MAX_ALIAS_LENGTH + 1];
    int status;
    int slot;

    /*
     * Get information of each slot.
     */
    for (slot = 0; slot < number_of_slots; slot++) {
	slot_entry = slot_table + slot;
	if (pccard_query_slot_information(slot, manufacturer, version, driver,
	    &status) == -1)
	    return -1;

	if (status == PCCARD_STATUS_FILLED
	    && alias_file_name != NULL
	    && pccard_search_alias_file(alias_file_name, alias, manufacturer,
		version)) {
	    memcpy(slot_entry->alias, alias, PCCARD_MAX_ALIAS_LENGTH);
	    *(slot_entry->alias + PCCARD_MAX_ALIAS_LENGTH) = '\0';
	} else {
	    *(slot_entry->alias) = '\0';
	}

	memcpy(slot_entry->manufacturer, manufacturer,
	    PCCARD_MAX_MANUFACTURER_LENGTH);
	*(slot_entry->manufacturer + PCCARD_MAX_MANUFACTURER_LENGTH) = '\0';
	memcpy(slot_entry->version, version, PCCARD_MAX_VERSION_LENGTH);
	*(slot_entry->version + PCCARD_MAX_VERSION_LENGTH) = '\0';
	memcpy(slot_entry->driver, driver, PCCARD_MAX_DRIVER_LENGTH);
	*(slot_entry->driver + PCCARD_MAX_DRIVER_LENGTH) = '\0';
	slot_entry->status = status;
    }

    return 0;
}


/*
 * Close the connection to pccard, and dispose slot status data.
 */
void
pccard_finalize_client(void)
{
    number_of_slots = 0;
    free(slot_table);
    slot_table = NULL;
    free(alias_file_name);
    alias_file_name = NULL;
    pccard_close_socket();
}


/*
 * Return the alias file name.
 */
const char *
pccard_alias_file_name(void)
{
    return alias_file_name;
}


/*
 * Return the number of slots the system has.
 */
int
pccard_number_of_slots(void)
{
    return number_of_slots;
}


/*
 * Get manufacturer, version, driver name and status of the slot `slot'.
 * Upon return, `manufacturer', `version', `driver' and `status' are
 * set by this function.
 *
 * It returns -1 if `slot' is not valid, or this library is not
 * initialized yet.  It sets an empty string to manufacturer, 
 * version and driver name if `slot' is not active.
 */
int
pccard_slot_information(int slot, const char **manufacturer,
    const char **version, const char **driver, int *status)
{
    PCCard_Slot *slot_entry;

    if (slot_table == NULL) {
	pccard_output_error_message("pccard client is not initialized");
	return -1;
    }
    if (slot < 0 || slot >= number_of_slots) {
	pccard_output_error_message("invalid slot number %d", slot);
	return -1;
    }

    slot_entry = slot_table + slot;
    *manufacturer = slot_entry->manufacturer;
    *version = slot_entry->version;
    *driver = slot_entry->driver;
    *status = slot_entry->status;

    return 0;
}


/*
 * Return manufacturer information of the slot `slot'.
 * It returns NULL if `slot' is not valid, or this library is not
 * initialized yet.  It returns an empty string if `slot' is not
 * active.
 */
const char *
pccard_manufacturer(int slot)
{
    if (slot_table == NULL) {
	pccard_output_error_message("pccard client is not initialized");
	return NULL;
    }
    if (slot < 0 || slot >= number_of_slots) {
	pccard_output_error_message("invalid slot number %d", slot);
	return NULL;
    }

    return (slot_table + slot)->manufacturer;
}


/*
 * Return version information of the slot `slot'.
 * It returns NULL if `slot' is not valid, or this library is not
 * initialized yet.  It returns an empty string if `slot' is not
 * active.
 */
const char *
pccard_version(int slot)
{
    if (slot_table == NULL) {
	pccard_output_error_message("pccard client is not initialized");
	return NULL;
    }
    if (slot < 0 || slot >= number_of_slots) {
	pccard_output_error_message("invalid slot number %d", slot);
	return NULL;
    }

    return (slot_table + slot)->version;
}


/*
 * Return a driver name of the slot `slot'.
 * It returns NULL if `slot' is not valid, or this library is not
 * initialized yet.  It returns an empty string if `slot' is not
 * active.
 */
const char *
pccard_driver(int slot)
{
    if (slot_table == NULL) {
	pccard_output_error_message("pccard client is not initialized");
	return NULL;
    }
    if (slot < 0 || slot >= number_of_slots) {
	pccard_output_error_message("invalid slot number %d", slot);
	return NULL;
    }

    return (slot_table + slot)->driver;
}


/*
 * Return current status of the slot `slot'.
 * It returns -1 if `slot' is not valid, or this library is not
 * initialized yet.
 */
int
pccard_status(int slot)
{
    if (slot_table == NULL) {
	pccard_output_error_message("pccard client is not initialized");
	return -1;
    }
    if (slot < 0 || slot >= number_of_slots) {
	pccard_output_error_message("invalid slot number %d", slot);
	return -1;
    }

    return (slot_table + slot)->status;
}


/*
 * Return an alias name of the card inserted in `slot'.
 * It returns NULL if `slot' is not valid, or this library is not
 * initialized yet.  It returns an empty string if `slot' is not
 * active, or if the inserted card has no alias.
 */
const char *
pccard_alias(int slot)
{
    if (slot_table == NULL) {
	pccard_output_error_message("pccard client is not initialized");
	return NULL;
    }
    if (slot < 0 || slot >= number_of_slots) {
	pccard_output_error_message("invalid slot number %d", slot);
	return NULL;
    }

    return (slot_table + slot)->alias;
}


/*
 * Send an insert request to pccardd, and receive a response.
 * If it succeeds, 0 is returned.  Otherwise -1 is returned.
 */
int
pccard_insert(int slot)
{
    if (slot_table == NULL) {
	pccard_output_error_message("pccard client is not initialized");
	return -1;
    }
    if (slot < 0 || slot >= number_of_slots) {
	pccard_output_error_message("invalid slot number %d", slot);
	return -1;
    }

    if (pccard_query_insertion(slot) == -1)
	return -1;

    (slot_table + slot)->status = PCCARD_STATUS_INACTIVE2;

    if (pccard_debug_flag) {
	pccard_output_debug_message("status changed: slot=%d, status=%d",
	    slot, PCCARD_STATUS_INACTIVE2);
    }

    return 0;
}


/*
 * Send a removal request to pccardd, and receive a response.
 * If it succeeds, 0 is returned.  Otherwise -1 is returned.
 */
int
pccard_remove(int slot)
{
    if (slot_table == NULL) {
	pccard_output_error_message("pccard client is not initialized");
	return -1;
    }
    if (slot < 0 || slot >= number_of_slots) {
	pccard_output_error_message("invalid slot number %d", slot);
	return -1;
    }

    if (pccard_query_removal(slot) == -1)
	return -1;

    (slot_table + slot)->status = PCCARD_STATUS_FILLED2;

    if (pccard_debug_flag) {
	pccard_output_debug_message("status changed: slot=%d, status=%d",
	    slot, PCCARD_STATUS_FILLED2);
    }

    return 0;
}


/*
 * Wait and receive an update message from pccardd.
 * If it succeeds, 0 is returned.  Otherwise -1 is returned.
 */
int
pccard_update(void)
{
    PCCard_Slot *slot_entry;
    char manufacturer[PCCARD_MAX_RESPONSE_LENGTH + 1];
    char version[PCCARD_MAX_RESPONSE_LENGTH + 1];
    char driver[PCCARD_MAX_RESPONSE_LENGTH + 1];
    char alias[PCCARD_MAX_ALIAS_LENGTH + 1];
    int status;
    int slot;

    if (slot_table == NULL) {
	pccard_output_error_message("pccard client is not initialized");
	return -1;
    }

    /*
     * Get update information from pccardd.
     */
    if (pccard_wait_response() == -1)
	return -1;
    if (pccard_status_changed(&slot, manufacturer, version, driver, &status)
	== -1)
	return -1;

    if (slot < 0 || slot >= number_of_slots) {
	pccard_output_error_message("received invalid slot number %d", slot);
	return -1;
    }

    /*
     * Update the slot table.
     */
    slot_entry = slot_table + slot;
    if (status == PCCARD_STATUS_FILLED
	&& alias_file_name != NULL
	&& pccard_search_alias_file(alias_file_name, alias, manufacturer,
	    version)) {
	memcpy(slot_entry->alias, alias, PCCARD_MAX_ALIAS_LENGTH);
	*(slot_entry->alias + PCCARD_MAX_ALIAS_LENGTH) = '\0';
    } else {
	*(slot_entry->alias) = '\0';
    }
    memcpy(slot_entry->manufacturer, manufacturer,
	PCCARD_MAX_MANUFACTURER_LENGTH);
    *(slot_entry->manufacturer + PCCARD_MAX_MANUFACTURER_LENGTH) = '\0';
    memcpy(slot_entry->version, version, PCCARD_MAX_VERSION_LENGTH);
    *(slot_entry->version + PCCARD_MAX_VERSION_LENGTH) = '\0';
    memcpy(slot_entry->driver, driver, PCCARD_MAX_DRIVER_LENGTH);
    *(slot_entry->driver + PCCARD_MAX_DRIVER_LENGTH) = '\0';
    slot_entry->status = status;

    return slot;
}

/*
 * For test:
 */
#ifdef TEST
#include <signal.h>

void
signal_handler(int sig)
{
    pccard_finalize_client();
}

int
main()
{
    int nslots;
    const char *manufacturer;
    const char *version;
    const char *driver;
    const char *status_string;
    int status;
    int slot;

    signal(SIGHUP, signal_handler);
    signal(SIGINT, signal_handler);
    signal(SIGQUIT, signal_handler);
    signal(SIGTERM, signal_handler);

    pccard_initialize_client();
    nslots = pccard_number_of_slots();
    if (nslots == 0) {
	fprintf(stderr, "no slot\n");
	exit(1);
    }

    for (slot = 0; slot < nslots; slot++) {
	pccard_slot_information(slot, &manufacturer, &version, &driver,
	    &status);
	printf("slot %d:\n", slot);
	printf("  manufacturer = %s\n", manufacturer);
	printf("  version = %s\n", version);
	printf("  driver = %s\n", driver);
	switch (status) {
	case PCCARD_STATUS_EMPTY:
	    status_string = "empty";
	    break;
	case PCCARD_STATUS_FILLED:
	    status_string = "filled";
	    break;
	case PCCARD_STATUS_INACTIVE:
	    status_string = "inactive";
	    break;
	case PCCARD_STATUS_UNKNOWN:
	    status_string = "unknown";
	    break;
	}
	printf("  status = %s\n\n", status_string);
    }

    pccard_finalize_client();
}
#endif


syntax highlighted by Code2HTML, v. 0.9.1