summaryrefslogtreecommitdiffstats
path: root/src/pcmcia-check-broken-cis.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pcmcia-check-broken-cis.c')
-rw-r--r--src/pcmcia-check-broken-cis.c155
1 files changed, 145 insertions, 10 deletions
diff --git a/src/pcmcia-check-broken-cis.c b/src/pcmcia-check-broken-cis.c
index 82d8f14..d5597fa 100644
--- a/src/pcmcia-check-broken-cis.c
+++ b/src/pcmcia-check-broken-cis.c
@@ -13,14 +13,25 @@
* (C) 2005 Dominik Brodowski
*/
+#include <fcntl.h>
+#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <unistd.h>
#include <errno.h>
#include <syslog.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
#include "cistpl.h"
+
+#define FIRMWARE_PATH "/lib/firmware"
+#define CIS_PATH "/etc/pcmcia/cis"
+#define SOCKET_PATH "/sys/class/pcmcia_socket/pcmcia_socket%d/cis"
+
struct needs_cis {
unsigned long code;
unsigned long ofs;
@@ -39,20 +50,140 @@ static struct needs_cis cis_table[] = {
{ },
};
+int device_has_driver() {
+ char *devpath, *path;
+ struct stat sbuf;
+
+ devpath = getenv("DEVPATH");
+ if (!devpath)
+ return ENODEV;
+ path = alloca(strlen(devpath)+15);
+ sprintf(path,"/sys/%s/driver", devpath);
+ if (!stat(path,&sbuf)) {
+ return 1;
+ }
+ return 0;
+}
+
+char *read_cis(char *cis_file, int *size) {
+ char *cis_path;
+ char *ret;
+ int rc, cis_fd;
+ struct stat sbuf;
+
+ cis_path = alloca(strlen(FIRMWARE_PATH) + strlen(cis_file) + 2);
+ sprintf(cis_path,"%s/%s", FIRMWARE_PATH, cis_file);
+ cis_fd = open(cis_path, O_RDONLY);
+ if (cis_fd == -1) {
+ cis_path = alloca(strlen(CIS_PATH) + strlen(cis_file) + 2);
+ sprintf(cis_path,"%s/%s", CIS_PATH, cis_file);
+ if (cis_fd == -1) {
+ rc = errno;
+ errno = rc;
+ return NULL;
+ }
+ }
+ fstat(cis_fd, &sbuf);
+ ret = malloc(sbuf.st_size);
+ if (!ret) {
+ rc = errno;
+ close(cis_fd);
+ errno = rc;
+ return NULL;
+ }
+ if (read(cis_fd, ret, sbuf.st_size) != sbuf.st_size) {
+ rc = errno;
+ free(ret);
+ close(cis_fd);
+ errno = rc;
+ return NULL;
+ }
+ close(cis_fd);
+ *size = sbuf.st_size;
+ return ret;
+}
+
+int write_cis(char *cis, int socket_no, int size) {
+ char *cis_path;
+ int cis_fd, count, rc;
+
+ cis_path = alloca(strlen(SOCKET_PATH) + 2);
+ sprintf(cis_path,SOCKET_PATH, socket_no);
+
+ cis_fd = open(cis_path, O_RDWR);
+ if (cis_fd == -1) {
+ return errno;
+ }
+
+ count = 0;
+ while (count < size) {
+ int c;
+
+ c = write(cis_fd, cis+count, size-count);
+ if (c <= 0) {
+ rc = errno;
+ close(cis_fd);
+ return rc;
+ }
+ count += c;
+ }
+ close(cis_fd);
+ return 0;
+}
+
+int repair_cis(char *cis_file, int socket_no) {
+ char *cis;
+ int rc, size;
+
+ if (device_has_driver()) {
+ return 0;
+ }
+
+ cis = read_cis(cis_file, &size);
+ if (!cis)
+ return errno;
+
+ rc = write_cis(cis, socket_no, size);
+ free(cis);
+ return rc;
+}
+
+static void usage(const char *progname) {
+ fprintf(stderr,
+ "Usage: %s [-r|--repair] <socketname>\n", progname);
+ exit(1);
+}
+
+static struct option options[] = { { "repair", 0, NULL, 'r' },
+ { NULL, 0, NULL, 0 } };
+
int main(int argc, char **argv) {
int ret;
+ char *socket;
unsigned int socket_no;
struct needs_cis * entry = NULL;
tuple_t tuple;
unsigned char buf[256];
-
- if (argc != 2)
- return -EINVAL;
-
- ret = sscanf(argv[1], "%u", &socket_no);
- if (ret != 1)
- return -ENODEV;
-
+ int opt;
+ int repair = 0;
+
+ while ((opt = getopt_long(argc, argv, "r", options, NULL)) != -1) {
+ switch (opt) {
+ case 'r':
+ repair = 1;
+ break;
+ default:
+ usage(argv[0]);
+ }
+ }
+ if ((socket = getenv("SOCKET_NO"))) {
+ socket_no = (unsigned int)strtoul(socket, NULL, 0);
+ } else {
+ if (argc < optind + 1)
+ usage(argv[0]);
+ socket_no = strtoul(argv[optind], NULL, 0);
+ }
+
ret = read_out_cis(socket_no, NULL);
if (ret)
return (ret);
@@ -78,8 +209,12 @@ int main(int argc, char **argv) {
entry++;
continue;
}
-
- printf("%s", entry->cisfile);
+
+ if (repair) {
+ return repair_cis(entry->cisfile, socket_no);
+ } else {
+ printf("%s", entry->cisfile);
+ }
};
return 0;