summaryrefslogtreecommitdiffstats
path: root/tools/serial_probe
diff options
context:
space:
mode:
authorFrancois Pons <fpons@mandriva.com>2000-04-07 14:43:04 +0000
committerFrancois Pons <fpons@mandriva.com>2000-04-07 14:43:04 +0000
commit1800d14ff9ac6dd218d274b6b4e13e3e71baecc4 (patch)
tree4a915815837bb688748bcefaaef31e1dce35fcac /tools/serial_probe
parentfe2f11d05f38a658db90c75ce73dbe87bfb84a64 (diff)
downloaddrakx-backup-do-not-use-1800d14ff9ac6dd218d274b6b4e13e3e71baecc4.tar
drakx-backup-do-not-use-1800d14ff9ac6dd218d274b6b4e13e3e71baecc4.tar.gz
drakx-backup-do-not-use-1800d14ff9ac6dd218d274b6b4e13e3e71baecc4.tar.bz2
drakx-backup-do-not-use-1800d14ff9ac6dd218d274b6b4e13e3e71baecc4.tar.xz
drakx-backup-do-not-use-1800d14ff9ac6dd218d274b6b4e13e3e71baecc4.zip
*** empty log message ***
Diffstat (limited to 'tools/serial_probe')
-rw-r--r--tools/serial_probe/Makefile9
-rw-r--r--tools/serial_probe/device.h80
-rw-r--r--tools/serial_probe/serial.c1200
-rw-r--r--tools/serial_probe/serial.h43
-rw-r--r--tools/serial_probe/serial_probe.c90
5 files changed, 1422 insertions, 0 deletions
diff --git a/tools/serial_probe/Makefile b/tools/serial_probe/Makefile
new file mode 100644
index 000000000..8e8590d62
--- /dev/null
+++ b/tools/serial_probe/Makefile
@@ -0,0 +1,9 @@
+CFLAGS = -Wall -Os
+CFILES = $(wildcard *.c)
+OFILES = $(CFILES:%.c=%.o)
+GOAL = serial_probe
+
+$(GOAL): $(OFILES)
+
+clean:
+ rm -f $(GOAL) $(OFILES) *~
diff --git a/tools/serial_probe/device.h b/tools/serial_probe/device.h
new file mode 100644
index 000000000..fa5f8183e
--- /dev/null
+++ b/tools/serial_probe/device.h
@@ -0,0 +1,80 @@
+
+/* Copyright 1999 Red Hat, Inc.
+ *
+ * This software may be freely redistributed under the terms of the GNU
+ * public license.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#ifndef _KUDZU_DEVICES_H_
+#define _KUDZU_DEVICES_H_
+
+#include <stdio.h>
+
+enum deviceClass {
+ /* device classes... this is somewhat ad-hoc */
+ CLASS_UNSPEC, CLASS_OTHER, CLASS_NETWORK, CLASS_SCSI, CLASS_VIDEO,
+ CLASS_AUDIO, CLASS_MOUSE, CLASS_MODEM, CLASS_CDROM, CLASS_TAPE,
+ CLASS_FLOPPY, CLASS_SCANNER, CLASS_HD, CLASS_RAID, CLASS_PRINTER,
+ CLASS_CAPTURE, CLASS_KEYBOARD, CLASS_PCMCIA
+};
+
+enum deviceBus {
+ /* 'bus' that a device is attached to... this is also ad-hoc */
+ /* BUS_SBUS is sort of a misnomer - it's more or less Sun */
+ /* OpenPROM probing of all various associated non-PCI buses */
+ BUS_UNSPEC = 0,
+ BUS_OTHER = (1 << 0),
+ BUS_PCI = (1 << 1),
+ BUS_SBUS = (1 << 2),
+ BUS_PSAUX = (1 << 3),
+ BUS_SERIAL = (1 << 4),
+ BUS_PARALLEL = (1 << 5),
+ BUS_SCSI = (1 << 6),
+ BUS_IDE = (1 << 7),
+ /* Again, misnomer */
+ BUS_KEYBOARD = (1 << 8),
+#ifdef _i_wanna_build_this_crap_
+ BUS_ISAPNP = (1 << 9),
+#endif
+};
+
+struct device {
+ /* This pointer is used to make lists by the library. */
+ /* Do not expect it to remain constant (or useful) across library calls. */
+ struct device *next;
+ /* Used for ordering, and for aliasing (modem0, modem1, etc.) */
+ int index;
+ enum deviceClass class; /* type */
+ enum deviceBus bus; /* bus it's attached to */
+ char * device; /* device file associated with it */
+ char * driver; /* driver to load, if any */
+ char * desc; /* a description */
+ int detached; /* should we care if it disappears? */
+ struct device *(*newDevice) (struct device *old, struct device *new);
+ void (*freeDevice) (struct device *dev);
+ void (*writeDevice) (FILE *file, struct device *dev);
+ int (*compareDevice) (struct device *dev1, struct device *dev2);
+};
+
+struct device *newDevice(struct device *old, struct device *new);
+void freeDevice(struct device *dev);
+void writeDevice(FILE *file, struct device *dev);
+int compareDevice(struct device *dev1, struct device *dev2);
+struct device *readDevice(FILE *file);
+
+/* Most of these aren't implemented yet...... */
+/* Return everything found, even non-useful stuff */
+#define PROBE_ALL 1
+/* Don't do 'dangerous' probes that could do weird things (isapnp, serial) */
+#define PROBE_SAFE (1<<1)
+/* Stop at first device found */
+#define PROBE_ONE (1<<2)
+
+
+#endif
diff --git a/tools/serial_probe/serial.c b/tools/serial_probe/serial.c
new file mode 100644
index 000000000..d0e7c19bd
--- /dev/null
+++ b/tools/serial_probe/serial.c
@@ -0,0 +1,1200 @@
+/* probe serial port for PnP/Legacy devices
+ *
+ * Copyright 1999 Red Hat, Inc.
+ *
+ * This software may be freely redistributed under the terms of the GNU
+ * public license.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <termios.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#include <linux/serial.h>
+#include "serial.h"
+
+/* character strings ARE null-terminated in the following structure */
+/* these elements are marked with a (string) in the comment */
+/* If PnP device sent 6 bit data stream, we've xlated by a 0x20 offset */
+/* When computing checksum, must remove this offset */
+struct pnp_com_id {
+ unsigned char xlate_6bit; /* does this contain xlated data */
+ unsigned char other_id[17]; /* backward compatibility with pre-PNP */
+ unsigned char other_len; /* length of the other_id */
+ unsigned char pnp_rev[2]; /* PnP revision bytes */
+ unsigned char pnp_rev_str[8]; /* PnP revision (string version) */
+ unsigned char eisa_id[4]; /* EISA Mfr identifier (string) */
+ unsigned char product_id[5]; /* Mfr determined product ID (string) */
+ unsigned char serial_number[9];/* Optional dev serial number (string) */
+ unsigned char class_name[33]; /* Optional PnP Class name (string) */
+ unsigned char driver_id[42]; /* Optional compat device IDs (string) */
+ unsigned char user_name[42]; /* Optional verbose product descr (string)*/
+ unsigned char checksum[2]; /* Optional checksum */
+};
+
+/* there are two possible bytes to signify the start of a PnP ID string */
+#define BeginPnP1 0x28
+#define BeginPnP2 0x08
+
+/* Likewise, two possible stop bytes */
+#define EndPnP1 0x29
+#define EndPnP2 0x09
+
+/* these chars indicate extensions to the base dev id exist */
+#define ExtendPnP1 0x5c
+#define ExtendPnP2 0x3c
+
+#define PNP_COM_MAXLEN 256
+
+/* results from initiating hardware probe of a hardware device */
+#define PNP_COM_FATAL 1 /* system error, check errno */
+#define PNP_COM_FAIL 2 /* probe ok, but found nothing */
+#define PNP_COM_OK 3 /* probe ok, we found it */
+
+/* types of devices we might find */
+/* if PNP_COM_PNPDEV is NOT set, its a legacy device */
+#define PNP_COM_MOUSE 1 /* its a mouse */
+#define PNP_COM_MODEM 2 /* its a modem */
+#define PNP_COM_OTHER 4 /* device is there, cant tell what kind */
+#define PNP_COM_NOEXIST 8 /* no device seen */
+#define PNP_COM_PNPDEV 512 /* its a PNP device */
+
+/* level of debugging output */
+/* current any value > 0 dumps all available debugging output */
+static int debug_level=0;
+
+static void serialFreeDevice(struct serialDevice *dev) {
+ if (dev->pnpmfr) free(dev->pnpmfr);
+ if (dev->pnpmodel) free(dev->pnpmodel);
+ if (dev->pnpcompat) free(dev->pnpcompat);
+ if (dev->pnpdesc) free(dev->pnpdesc);
+ freeDevice((struct device *)dev);
+}
+
+static void serialWriteDevice(FILE *file, struct serialDevice *dev)
+{
+ writeDevice(file, (struct device *) dev);
+ if (dev->pnpmfr)
+ fprintf(file,"pnpmfr: %s\n",dev->pnpmfr);
+ if (dev->pnpmodel)
+ fprintf(file,"pnpmodel: %s\n",dev->pnpmodel);
+ if (dev->pnpcompat)
+ fprintf(file,"pnpcompat: %s\n",dev->pnpcompat);
+ if (dev->pnpdesc)
+ fprintf(file,"pnpdesc: %s\n",dev->pnpdesc);
+}
+
+static int serialCompareDevice( struct serialDevice *dev1, struct serialDevice *dev2)
+{
+ int x;
+
+ x = compareDevice((struct device *)dev1, (struct device *)dev2);
+ if (x && x!=2) return x;
+ if (dev1->pnpmfr && dev2->pnpmfr && strcmp(dev1->pnpmfr,dev2->pnpmfr))
+ return 1;
+ if ((!dev1->pnpmfr || !dev2->pnpmfr) && (dev1->pnpmfr != dev2->pnpmfr))
+ return 1;
+ if (dev1->pnpmodel && dev2->pnpmodel && strcmp(dev1->pnpmodel,dev2->pnpmodel))
+ return 1;
+ if ((!dev1->pnpmodel || !dev2->pnpmodel) && (dev1->pnpmodel != dev2->pnpmodel))
+ return 1;
+ if (dev1->pnpcompat && dev2->pnpcompat && strcmp(dev1->pnpcompat,dev2->pnpcompat))
+ return 1;
+ if ((!dev1->pnpcompat || !dev2->pnpcompat) && (dev1->pnpcompat != dev2->pnpcompat))
+ return 1;
+ if (dev1->pnpdesc && dev2->pnpdesc && strcmp(dev1->pnpdesc,dev2->pnpdesc))
+ return 1;
+ if ((!dev1->pnpdesc || !dev2->pnpdesc) && (dev1->pnpdesc != dev2->pnpdesc))
+ return 1;
+ return x;
+}
+
+
+struct serialDevice * serialNewDevice(struct serialDevice *dev) {
+ struct serialDevice *ret;
+
+ ret = malloc(sizeof(struct serialDevice));
+ memset(ret,'\0',sizeof(struct serialDevice));
+ ret=(struct serialDevice *)newDevice((struct device *)dev,(struct device *)ret);
+ ret->bus = BUS_SERIAL;
+ ret->newDevice = serialNewDevice;
+ ret->freeDevice = serialFreeDevice;
+ ret->writeDevice = serialWriteDevice;
+ ret->compareDevice = serialCompareDevice;
+ if (dev && dev->bus == BUS_SERIAL) {
+ if (dev->pnpmfr)
+ ret->pnpmfr=strdup(dev->pnpmfr);
+ if (dev->pnpmodel)
+ ret->pnpmodel=strdup(dev->pnpmodel);
+ if (dev->pnpcompat)
+ ret->pnpcompat=strdup(dev->pnpcompat);
+ if (dev->pnpdesc)
+ ret->pnpdesc=strdup(dev->pnpdesc);
+ }
+ return ret;
+}
+
+/* UNUSED */
+void print_status_lines( int fd ) {
+ int modem_lines;
+
+ ioctl(fd, TIOCMGET, &modem_lines);
+
+ printf("DTR : %s\n",(modem_lines & TIOCM_DTR ? "On" : "Off"));
+ printf("RTS : %s\n",(modem_lines & TIOCM_RTS ? "On" : "Off"));
+ printf("CTS : %s\n",(modem_lines & TIOCM_CTS ? "On" : "Off"));
+ printf("DSR : %s\n",(modem_lines & TIOCM_DSR ? "On" : "Off"));
+ printf("CD : %s\n",(modem_lines & TIOCM_CD ? "On" : "Off"));
+ printf("RI : %s\n",(modem_lines & TIOCM_RI ? "On" : "Off"));
+
+}
+
+
+/* UNUSED except in debug */
+/* outputs data in a hex table, 8 values per row */
+void print_hex_data( unsigned char *data, int len ) {
+ int i, j, pos;
+
+ if (len == 0) {
+ printf("No data to print.\n");
+ return;
+ }
+
+ pos = 0;
+ for (i=0; i< len; i+=8) {
+ printf("0x%.4x ", i);
+ for (j=i; j < len && j < i+8; j++) {
+ printf("0x%.2x ",data[pos++]);
+ }
+ printf("\n");
+ }
+}
+
+
+/*
+ * wait_input - wait until there is data available on fd,
+ * for the length of time specified by *timo (indefinite
+ * if timo is NULL).
+ */
+
+int wait_for_input (int fd, struct timeval *timo) {
+ fd_set ready;
+ int n;
+
+ FD_ZERO(&ready);
+ FD_SET(fd, &ready);
+
+ n = select(fd+1, &ready, NULL, &ready, timo);
+ return n;
+}
+
+/* UNUSED */
+/* read characters into the buffer buf, until one of: */
+/* char_timeout expired before next character arrives */
+/* total_timeout expires */
+/* maxlen characters are retrieved */
+/* */
+/* returns < 0 if it fails */
+/* otherwise the # of characters received is returned */
+/* char_timeout is in microseconds (millionths of a sec) */
+/* total_timeout is in seconds */
+int timed_serial_read(int fd, int char_timeout, int total_timeout,
+ unsigned char *buf, int maxlen ) {
+
+ int done, pos, starttime, temp;
+ struct timeval timo;
+ unsigned char intbuf[2];
+
+ /* start reading */
+ done = 0;
+ pos = 0;
+ starttime=time(NULL);
+ memset(buf, 0, maxlen);
+ while (!done) {
+ timo.tv_sec=0;
+ timo.tv_usec=char_timeout;
+ if (wait_for_input(fd, &timo) > 0) {
+ temp = read( fd, intbuf, 1 );
+ if (temp < 0) {
+ if (errno != EAGAIN)
+ return -1;
+ } else {
+ buf[pos++] = intbuf[0];
+ buf[pos] = 0;
+ }
+ } else
+ done = 1;
+
+ /* shouldnt run more than 5 seconds */
+ if (time(NULL)-starttime > total_timeout )
+ done = 1;
+
+ if (pos > maxlen)
+ done = 1;
+ }
+ return pos;
+}
+
+
+int open_serial_port( char *port ) {
+ int fd;
+
+ fd = open( port, O_RDWR | O_NONBLOCK);
+ if (fd < 0)
+ return -1;
+
+ /* reset file so it is no longer in non-blocking mode */
+ if (fcntl(fd, F_SETFL, 0) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+/* <0 means ioctl error occurred */
+int get_serial_lines( int fd ) {
+ int modem_lines;
+
+ ioctl(fd, TIOCMGET, &modem_lines);
+ return modem_lines;
+}
+
+/* <0 means ioctl error occurred */
+int set_serial_lines( int fd, int modem_lines ) {
+ return ioctl(fd, TIOCMSET, &modem_lines);
+}
+
+/* set serial port to 1200 baud, 'nbits' bits, 1 stop, no parity */
+int setup_serial_port( int fd, int nbits, struct termios *attr ) {
+
+ attr->c_iflag = IGNBRK | IGNPAR;
+ attr->c_cflag = 0;
+ attr->c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD | PARENB);
+ attr->c_cflag |= CREAD | CLOCAL; /*| CRTSCTS ; */
+ if (nbits == 7)
+ attr->c_cflag |= CS7 | CSTOPB;
+ else
+ attr->c_cflag |= CS8;
+ attr->c_oflag = 0;
+ attr->c_lflag = 0;
+
+ attr->c_cc[VMIN] = 1;
+ attr->c_cc[VTIME] = 5;
+
+ cfsetospeed( attr, B1200);
+ cfsetispeed( attr, B1200);
+ return tcsetattr(fd, TCSANOW, attr);
+}
+
+/* Initialize the serial port to a known state *before* probing. This is
+ * apparently required for some Logitech mice, who will stubbornly refuse
+ * to respond to PnP probes after they've been opened by gpm or XFree.
+ */
+
+int init_port(int fd) {
+ struct termios attr;
+
+ if (tcgetattr(fd,&attr))
+ return 1;
+
+ cfsetospeed(&attr, B2400);
+ cfsetispeed(&attr, B2400);
+ attr.c_iflag = IXON | ICRNL;
+ attr.c_cflag = CLOCAL | HUPCL | CREAD | B9600 | CS8;
+ attr.c_oflag = 0;
+ attr.c_lflag = 0;
+ return tcsetattr(fd, TCSANOW, &attr);
+}
+
+
+/* Request for PnP info from serial device */
+/* See page 6 of the pnpcom doc from Microsoft */
+/* Return code tells us what happened */
+/* */
+/* PNP_COM_FATAL - error, errno has reason */
+/* PNP_COM_OK - probe initiated successfully */
+/* PNP_COM_FAIL - DSR never came on - try alterntives */
+/* means (ATI9?) to get PnP string */
+int init_pnp_com_seq1( int fd ) {
+ int modem_lines;
+ int temp;
+ int dsr_status;
+ int rc = PNP_COM_OK;
+ struct termios portattr;
+
+ if (init_port(fd))
+ return PNP_COM_FATAL;
+
+ modem_lines = get_serial_lines(fd);
+
+ /* turn off RTS */
+ modem_lines &= ~TIOCM_RTS;
+ set_serial_lines(fd, modem_lines);
+
+ /* wait 200ms for DSR=1 */
+ usleep(200000);
+
+ dsr_status = get_serial_lines(fd) & TIOCM_DSR;
+ /* see if we got DSR coming up */
+
+ if (!dsr_status) {
+ /* turn DTR and RTS back on and try alternative methods */
+ modem_lines |= TIOCM_DTR | TIOCM_RTS;
+ set_serial_lines(fd, modem_lines);
+ rc = PNP_COM_OK;
+ }
+
+ /* COM port Setup, 1st phase */
+ /* now we set port to be 1200 baud, 7 bits, no parity, 1 stop bit */
+ temp = tcgetattr(fd, &portattr);
+ if (temp < 0)
+ return PNP_COM_FATAL;
+ /* goto 1200 baud, etc etc as PnP requires */
+ temp = setup_serial_port( fd, 7, &portattr );
+ if (temp < 0)
+ return PNP_COM_FATAL;
+
+ /* we drop DTR and RTS */
+ modem_lines &= ~( TIOCM_RTS | TIOCM_DTR);
+ set_serial_lines(fd, modem_lines);
+ usleep(200000);
+
+ /* bring DTR back up */
+ modem_lines |= TIOCM_DTR;
+ set_serial_lines(fd, modem_lines);
+ usleep(200000);
+
+ /* now entering next phase */
+ modem_lines |= TIOCM_RTS;
+ set_serial_lines(fd, modem_lines);
+ usleep(200000);
+
+ return rc;
+}
+
+
+/* Request for PnP info from serial device */
+/* Uses ATI9 code, may not do anything but return 'ERROR' */
+/* Return code tells us what happened */
+/* */
+/* PNP_COM_FATAL - error, errno has reason */
+/* PNP_COM_OK - probe initiated successfully */
+/* PNP_COM_FAIL - DSR never came on - try alterntives */
+/* means (ATI9?) to get PnP string */
+int init_pnp_com_ati9( int fd ) {
+ int modem_lines;
+ int temp;
+ int done;
+ int respindex;
+ int starttime;
+ unsigned char resp[100], buf[2];
+ struct timeval timo;
+ struct termios portattr;
+
+ modem_lines = get_serial_lines(fd);
+
+ /* turn off RTS */
+ modem_lines &= ~TIOCM_RTS;
+ set_serial_lines(fd, modem_lines);
+
+ /* wait 200ms for DSR=1 */
+ usleep(200000);
+
+ /* now we set port to be 1200 baud, 8 bits, no parity, 1 stop bit */
+ temp = tcgetattr(fd, &portattr);
+ if (temp < 0) {
+ modem_lines |= TIOCM_DTR | TIOCM_RTS;
+ set_serial_lines(fd, modem_lines);
+ return PNP_COM_FATAL;
+ }
+
+ /* goto 1200 baud, 8 bits */
+ temp = setup_serial_port( fd, 8, &portattr );
+ if (temp < 0) {
+ modem_lines |= TIOCM_DTR | TIOCM_RTS;
+ set_serial_lines(fd, modem_lines);
+ return PNP_COM_FATAL;
+ }
+
+ /* turn on DTR and RTS */
+ modem_lines = get_serial_lines(fd);
+ modem_lines |= TIOCM_RTS | TIOCM_DTR;
+ set_serial_lines(fd, modem_lines);
+ usleep(200000);
+
+ /* send the 'AT' command */
+ if (debug_level > 0)
+ printf("Sending ATI9 command to modem\n");
+
+ write(fd, "ATI9\r", 5);
+
+ /* start reading - read the AT command back */
+ done = 0;
+ respindex= 0;
+ starttime=time(NULL);
+ memset(resp, 0, sizeof(resp));
+ while (!done) {
+ timo.tv_sec=0;
+ timo.tv_usec=250000;
+ if (wait_for_input(fd, &timo) > 0) {
+ temp = read( fd, buf, 1 );
+ if (temp < 0) {
+ if (errno != EAGAIN)
+ return PNP_COM_FATAL;
+ } else {
+ resp[respindex++] = buf[0];
+ resp[respindex] = 0;
+ }
+ } else
+ done = 1;
+
+ /* shouldnt run more than 5 seconds */
+ if (time(NULL)-starttime > 5 )
+ done = 1;
+
+ if (respindex > 6)
+ done = 1;
+
+ if (strstr(resp, "ATI9\r"))
+ done = 1;
+
+ if (debug_level > 0)
+ printf("ATI9 probe ->%d \"%s\"\n",respindex, resp);
+ }
+
+ /* see if we saw the 'OK' response */
+ if (strstr(resp, "("))
+ return PNP_COM_OK;
+ else
+ return PNP_COM_FAIL;
+
+ return PNP_COM_OK;
+}
+
+/* See if this is a legacy mouse device */
+/* Only called if the PnP probe above failed */
+/* We turn off the mouse via RS232 lines, then turn it on */
+/* If it spits out an 'M' character (at 1200 baud, 7N1) */
+/* it could be a mouse. */
+/* */
+/* Return code tells us what happened */
+/* */
+/* PNP_COM_FATAL - error, errno has reason */
+/* PNP_COM_OK - probe saw 'M' */
+/* PNP_COM_FAIL - Never saw the 'M' response */
+
+int find_legacy_mouse( int fd ) {
+ int modem_lines;
+ int temp;
+ int done;
+ int starttime;
+ unsigned char resp[2];
+ struct timeval timo;
+ struct termios portattr;
+
+ /* now we set port to be 1200 baud, 7 bits, no parity, 1 stop bit */
+ temp = tcgetattr(fd, &portattr);
+ if (temp < 0)
+ return PNP_COM_FATAL;
+
+ /* goto 1200 baud, etc etc*/
+ temp = setup_serial_port( fd, 7, &portattr );
+ if (temp < 0)
+ return PNP_COM_FATAL;
+
+ /* we drop DTR and RTS */
+ modem_lines = get_serial_lines(fd);
+ modem_lines &= ~( TIOCM_RTS | TIOCM_DTR);
+ set_serial_lines(fd, modem_lines);
+ usleep(200000);
+
+ /* bring them DTR back up */
+ modem_lines |= TIOCM_DTR | TIOCM_RTS;
+ set_serial_lines(fd, modem_lines);
+
+ /* start reading - after first character we quit */
+ done = 0;
+ starttime=time(NULL);
+ while (!done) {
+ timo.tv_sec=0;
+ timo.tv_usec=250000;
+ if (wait_for_input(fd, &timo) > 0) {
+ temp = read( fd, resp, 1 );
+ if (temp < 0) {
+ if (errno != EAGAIN)
+ return PNP_COM_FATAL;
+ } else {
+ done = 1;
+ }
+ } else
+ done = 1;
+
+
+ /* shouldnt run more than 2 seconds */
+ if (time(NULL)-starttime > 2 )
+ done = 1;
+ }
+ if (*resp == 'M')
+ return PNP_COM_OK;
+ else
+ return PNP_COM_FAIL;
+}
+
+/* See if this is a legacy modem device */
+/* Only called if the PnP probe above failed */
+/* We send a '!AT' and see if we get an 'OK' back */
+/* */
+/* Return code tells us what happened */
+/* */
+/* PNP_COM_FATAL - error, errno has reason */
+/* PNP_COM_OK - probe saw 'OK' */
+/* PNP_COM_FAIL - Never saw the 'OK' response */
+int find_legacy_modem( int fd ) {
+ int modem_lines;
+ int temp;
+ int done;
+ int respindex;
+ int starttime;
+ unsigned char resp[10], buf[2];
+ struct timeval timo;
+ struct termios portattr;
+
+ /* now we set port to be 1200 baud, 8 bits, no parity, 1 stop bit */
+ temp = tcgetattr(fd, &portattr);
+ if (temp < 0)
+ return PNP_COM_FATAL;
+
+ /* goto 1200 baud, 8 bits */
+ temp = setup_serial_port( fd, 8, &portattr );
+ if (temp < 0)
+ return PNP_COM_FATAL;
+
+ /* turn on DTR and RTS */
+ modem_lines = get_serial_lines(fd);
+ modem_lines |= TIOCM_RTS | TIOCM_DTR;
+ set_serial_lines(fd, modem_lines);
+ usleep(200000);
+
+ /* send the 'AT' command */
+ if (debug_level > 0)
+ printf("Sending AT command to modem\n");
+
+ write(fd, "AT\r", 3);
+
+ /* start reading - we'll get AT command back first, then modem response */
+ done = 0;
+ respindex= 0;
+ starttime=time(NULL);
+ memset(resp, 0, sizeof(resp));
+ while (!done) {
+ timo.tv_sec=0;
+ timo.tv_usec=250000;
+ if (wait_for_input(fd, &timo) > 0) {
+ temp = read( fd, buf, 1 );
+ if (temp < 0) {
+ if (errno != EAGAIN)
+ return PNP_COM_FATAL;
+ } else {
+ resp[respindex++] = buf[0];
+ }
+ } else
+ done = 1;
+
+ /* shouldnt run more than 5 seconds */
+ if (time(NULL)-starttime > 5 )
+ done = 1;
+
+ if (respindex > 9)
+ done = 1;
+ }
+
+ /* see if we saw the 'OK' response */
+ if (strstr(resp, "OK"))
+ return PNP_COM_OK;
+ else
+ return PNP_COM_FAIL;
+}
+
+/* retrieve the PnP ID string */
+/* timeout after 3 seconds */
+/* should probably set a 200 msec timeout per char, as spec says */
+/* if no char received, we're done */
+int read_pnp_string( int fd, unsigned char *pnp_string, int *pnp_len, int pnp_stringbuf_size ) {
+ int pnp_index;
+ int temp, done, counter;
+ int seen_start;
+ time_t starttime;
+ struct timeval timo;
+ unsigned char buf[80];
+ unsigned char end_char;
+
+ /* see if we have any input waiting */
+ pnp_index =0;
+ seen_start = 0;
+ done = 0;
+ end_char = 0;
+ starttime=time(NULL);
+ while (!done) {
+ timo.tv_sec=0;
+ timo.tv_usec=250000;
+ if (wait_for_input(fd, &timo) > 0) {
+ temp = read( fd, buf, 1 );
+ if (temp < 0) {
+ if (errno != EAGAIN)
+ return PNP_COM_FATAL;
+ } else {
+ for (counter=0; counter < temp; counter++) {
+ pnp_string[pnp_index++] = buf[counter];
+ if (seen_start) {
+ if (buf[counter] == end_char) {
+ done=1;
+ break;
+ }
+ } else {
+ if (buf[counter] == BeginPnP1) {
+ seen_start = 1;
+ end_char = EndPnP1;
+ } else if (buf[counter] == BeginPnP2) {
+ seen_start = 1;
+ end_char = EndPnP2;
+ }
+ }
+ }
+ }
+ } else
+ done = 1;
+
+ /* shouldnt run more than 4 seconds */
+ if (time(NULL)-starttime > 4 )
+ done = 1;
+
+ if (pnp_index >= pnp_stringbuf_size)
+ done = 1;
+ }
+ pnp_string[pnp_index] = 0;
+ *pnp_len=pnp_index;
+ return 0;
+}
+
+/* UNUSED */
+/* simple little helper function */
+void xlate_memcpy( void *dest, void *src, int len, int xlate_flag ) {
+ unsigned char *d, *s;
+ int i;
+
+ for (i=0,d=dest,s=src; i<len; i++, d++, s++)
+ *d = (*s) + ((xlate_flag) ? 0x20 : 0 );
+}
+
+/* parse the PnP ID string into components */
+int parse_pnp_string( unsigned char *pnp_id_string, int pnp_len,
+ struct pnp_com_id *pnp_id ) {
+ unsigned char *p1, *p2;
+ unsigned char *start;
+ unsigned char *end;
+ unsigned char *curpos;
+ unsigned char *endfield;
+ unsigned char *temppos;
+ unsigned char *pnp_string;
+ unsigned char end_char;
+
+ int no_more_extensions=0;
+ int stage;
+ int len;
+ unsigned short int checksum;
+ char hex_checksum[5];
+
+ char extension_delims[] = {EndPnP1, EndPnP2, ExtendPnP1, ExtendPnP2, 0};
+ char end_delims[] = {EndPnP1, EndPnP2, 0};
+
+ /* clear out pnp_id */
+ memset(pnp_id, 0, sizeof(*pnp_id));
+
+ /* copy pnp_string to temp space */
+ pnp_string = alloca(pnp_len+1);
+ memcpy(pnp_string, pnp_id_string, pnp_len+1);
+
+ /* first find the start of the PnP part of string */
+ p1 = memchr( pnp_string, BeginPnP1, pnp_len );
+ p2 = memchr( pnp_string, BeginPnP2, pnp_len );
+
+ /* use the one which points nearest to start of the string */
+ /* and is actually defined */
+ if ( p1 && p2 ) {
+ start = (p1 < p2) ? p1 : p2;
+ } else if (p1)
+ start = p1;
+ else if (p2)
+ start = p2;
+ else
+ start = NULL;
+
+ /* if no start then we're done */
+ if (!start)
+ return -1;
+
+ /* the length of the initial part cannot be more than 17 bytes */
+ if ((start - pnp_string) > 17)
+ return -1;
+
+ /* setup end character we are looking for based on the start character */
+ if (start == p2) {
+ pnp_id->xlate_6bit = 1;
+ end_char = EndPnP2;
+ /* we need to xlate data in PnP fields */
+ /* remember to skip the revision fields (bytes 1 and 2 after start) */
+ temppos=start;
+ while (1) {
+ if (*temppos == EndPnP2) {
+ *temppos += 0x20;
+ break;
+ } else if (temppos != start+1 && temppos != start+2 )
+ *temppos += 0x20;
+
+ temppos++;
+ }
+ } else {
+ pnp_id->xlate_6bit = 0;
+ end_char = EndPnP1;
+ }
+
+ /* move everything before the start of the PnP block */
+ memcpy(pnp_id->other_id, pnp_string, start-pnp_string);
+ pnp_id->other_len = start - pnp_string;
+
+ /* now we get the PnP fields - all were zero'd out above */
+ curpos = start+1;
+ memcpy(pnp_id->pnp_rev,curpos,2); curpos += 2;
+ memcpy(pnp_id->eisa_id,curpos,3); curpos += 3;
+ memcpy(pnp_id->product_id,curpos,4); curpos += 4;
+ /* now we see if have extension fields */
+ no_more_extensions = 0;
+ stage = 0;
+ while (!no_more_extensions) {
+ if (*curpos == ExtendPnP1 || *curpos == ExtendPnP2) {
+ curpos++;
+ endfield = strpbrk(curpos, extension_delims);
+ if (!endfield)
+ return -1;
+ /* if we reached the end of all PnP data, back off */
+ /* cause there is a checksum at the end of extension data */
+ if (*endfield == EndPnP1 || *endfield == EndPnP2)
+ endfield -= 2;
+ } else
+ break;
+
+ len = endfield - curpos;
+ switch (stage) {
+ case 0:
+ if (len != 8 && len != 0 )
+ return -1;
+
+ memcpy(pnp_id->serial_number,curpos,len);
+ curpos += len;
+ break;
+
+ case 1:
+ if (len > 33)
+ return -1;
+ memcpy(pnp_id->class_name, curpos, len);
+ curpos = endfield;
+ break;
+
+ case 2:
+ if (len > 41)
+ return -1;
+ memcpy(pnp_id->driver_id, curpos, len);
+ curpos = endfield;
+ break;
+
+ case 3:
+ if (len > 41)
+ return -1;
+ memcpy(pnp_id->user_name, curpos, len);
+ curpos = endfield;
+ break;
+ }
+ stage++;
+ }
+
+ /* now find the end of all PnP data */
+ end = strpbrk(curpos, end_delims);
+ if (!end)
+ return -1;
+
+ /* if we had any extensions, we expect an checksum */
+ if (stage != 0) {
+ /* copy checksum into struct */
+ memcpy(pnp_id->checksum, curpos, 2);
+
+ /* compute the checksum as the sum of all PnP bytes, excluding */
+ /* the two byte checksum. */
+ checksum = 0;
+ for (temppos=start; temppos <= end; temppos++) {
+ /* skip checksum in calculation */
+ if (temppos == (end-2) || temppos == (end-1))
+ continue;
+ /* dont xlate the revision at start */
+ if (temppos != (start+1) && temppos != (start+2))
+ checksum += *temppos - ((pnp_id->xlate_6bit) ? 0x20 : 0);
+ else
+ checksum += *temppos;
+ }
+ sprintf(hex_checksum, "%.2X", checksum & 0xff);
+ if (strncmp(hex_checksum, pnp_id->checksum, 2))
+ return -1;
+ }
+
+ /* checksum was ok, so we're done */
+ return 0;
+}
+
+/* UNUSED except for debugging */
+void print_pnp_id( struct pnp_com_id id ) {
+ int i;
+ int extensions_exist;
+ int revision_temp;
+
+ if (id.other_len != 0) {
+ printf("Detected non-PnP data stream at start.\n");
+ printf(" Length = 0x%x\n",id.other_len);
+ printf(" Contents =");
+ for (i=0; i<id.other_len; i++)
+ printf(" 0x%x",id.other_id[i]);
+ printf("\n");
+ } else
+ printf("Non-PnP data stream not detected at start.\n");
+
+
+ /* parse PnP revision bytes into a string values (eg. "1.00") */
+ revision_temp = ((id.pnp_rev[0]&0x3f) << 6)+(id.pnp_rev[1]&0x3f);
+ sprintf(id.pnp_rev_str, "%d.%d",revision_temp/100,revision_temp % 100);
+
+ printf("\nPnP Required fields:\n");
+ printf(" Revision = %s\n",id.pnp_rev_str);
+ printf(" Manufacturer = %s\n",id.eisa_id);
+ printf(" Product ID = %s\n",id.product_id);
+
+ extensions_exist = id.serial_number[0] || id.class_name[0] ||
+ id.driver_id[0] || id.user_name[0];
+
+ if (extensions_exist) {
+ printf("\nPnP extension field(s) exist:\n");
+ if (id.serial_number[0])
+ printf(" Serial Number = %s\n",id.serial_number);
+ if (id.class_name[0])
+ printf(" PnP class name = %s\n",id.class_name);
+ if (id.driver_id[0])
+ printf(" PnP Compatible = %s\n",id.driver_id);
+ if (id.user_name[0])
+ printf(" PnP Description = %s\n",id.user_name);
+ }
+}
+
+int attempt_pnp_retrieve(int fd, char *pnp_string, int *pnp_strlen, int pnp_stringbuf_size) {
+ int pnp_probe_status;
+ int tried_at_prodding;
+ int give_up;
+ struct pnp_com_id pnp_id;
+
+ tried_at_prodding=0;
+ give_up=0;
+
+ while (!give_up) {
+ pnp_probe_status = init_pnp_com_seq1(fd);
+ if (pnp_probe_status == PNP_COM_FATAL) {
+ return(PNP_COM_FATAL);
+ } else if (pnp_probe_status == PNP_COM_OK) {
+ read_pnp_string(fd, pnp_string, pnp_strlen, pnp_stringbuf_size );
+
+ if (debug_level > 0) {
+ printf("\nPNP string = |%s|\n\n",pnp_string);
+ print_hex_data(pnp_string, *pnp_strlen);
+ }
+
+ if (*pnp_strlen == 1 && pnp_string[0] == 'M') /* legacy mouse */
+ return PNP_COM_OK;
+ /* see if we got anything useful, if not try at command */
+ /* to prod device into correct serial params */
+ if (parse_pnp_string( pnp_string, *pnp_strlen, &pnp_id )<0)
+ if (!tried_at_prodding) {
+ write(fd, "AT\r", 3);
+ tried_at_prodding=1;
+ } else
+ give_up = 1;
+ else
+ return PNP_COM_OK;
+ } else
+ give_up = 1;
+ }
+
+ /* try sending a ATI9 code to the modem to see if we get PnP id back */
+ init_pnp_com_ati9(fd);
+ read_pnp_string(fd, pnp_string, pnp_strlen, pnp_stringbuf_size );
+ if (parse_pnp_string( pnp_string, *pnp_strlen, &pnp_id )<0) {
+ *pnp_strlen = 0;
+ pnp_string[0] = 0;
+ return PNP_COM_FAIL;
+ } else
+ return PNP_COM_OK;
+}
+
+struct device *serialProbe(enum deviceClass probeClass, int probeFlags,
+ struct device *devlist) {
+ int fd;
+ int temp;
+ int pnp_strlen;
+ int devicetype=-1;
+ unsigned char pnp_string[100];
+ char port[20];
+ struct termios origattr;
+ struct pnp_com_id pnp_id;
+ struct serialDevice *serdev;
+ struct stat sb;
+ int maj, twelve=12;
+ int console=-1;
+ int stdin_line=-1;
+ struct serial_struct si;
+
+ if (probeFlags & PROBE_SAFE) return devlist;
+
+ /* Are we on a serial console? */
+ fstat(0,&sb);
+ maj = major(sb.st_rdev);
+ if (maj != 4 && (maj < 136 || maj > 143)) {
+ if (ioctl (0, TIOCLINUX, &twelve) < 0) {
+ if (ioctl (0, TIOCGSERIAL, &si) >= 0) {
+ if (si.line > 0) {
+ stdin_line = 1 << si.line;
+ } else {
+ stdin_line = 0;
+ }
+ } else stdin_line = 0;
+ }
+ }
+
+ fd=open("/dev/console",O_RDWR);
+ if (fd != -1) {
+ fstat(fd,&sb);
+ maj = major(sb.st_rdev);
+ if (maj != 4 && (maj < 136 || maj > 143)) {
+ if (ioctl (fd, TIOCLINUX, &twelve) < 0) {
+ if (ioctl (fd, TIOCGSERIAL, &si) >= 0) {
+ if (si.line > 0) {
+ console = 1 << si.line;
+ } else {
+ console = 0;
+ }
+ } else console = 0;
+ }
+ }
+ close(fd);
+ }
+
+
+ if (
+ (probeClass == CLASS_UNSPEC) ||
+ (probeClass == CLASS_OTHER) ||
+ (probeClass == CLASS_MOUSE) ||
+ (probeClass == CLASS_MODEM) ||
+ (probeClass == CLASS_PRINTER)
+ ) {
+ int x;
+
+ for (x=0; x<=3 ; x++) {
+ struct stat sbuf;
+ char lockfile[32];
+ if (x==console || x==stdin_line) continue;
+ snprintf(port,20,"/dev/ttyS%d",x);
+
+ /* Make sure it's not in use */
+ snprintf(lockfile,32,"/var/lock/LCK..ttyS%d",x);
+ if (!stat(lockfile,&sbuf))
+ continue;
+ memset(lockfile,'\0',32);
+ if (readlink("/dev/modem",lockfile,32)>0) {
+ if (!strcmp(basename(port),basename(lockfile))) {
+ snprintf(lockfile,32,"/var/lock/LCK..modem");
+ if (!stat(lockfile,&sbuf))
+ continue;
+ }
+ }
+
+ if ((fd=open_serial_port(port)) < 0) {
+ continue;
+ }
+ /* save the current state of the port */
+ temp = tcgetattr(fd, &origattr);
+ if (temp < 0) {
+ close(fd);
+ continue;
+ }
+
+ /* try twiddling RS232 control lines and see if it talks to us */
+ devicetype=-1;
+ pnp_strlen = 0;
+ attempt_pnp_retrieve( fd, pnp_string, &pnp_strlen, sizeof(pnp_string) - 1 );
+
+ /* see if we found any PnP signature */
+ if (pnp_strlen != 0) {
+ if (*pnp_string == 'M') { /* Legacy mouse */
+ if (probeClass == CLASS_MOUSE || probeClass == CLASS_UNSPEC) {
+ serdev = serialNewDevice(NULL);
+ serdev->class=CLASS_MOUSE;
+ serdev->device=strdup(port+5);
+ serdev->desc=strdup("Generic Serial Mouse");
+ serdev->driver=strdup("generic");
+ if (devlist)
+ serdev->next = devlist;
+ devlist = (struct device *)serdev;
+ if (probeFlags & PROBE_ONE) {
+ tcsetattr(fd, TCSANOW, &origattr);
+ tcflush(fd, TCIOFLUSH);
+ close(fd);
+ return devlist;
+ }
+ }
+ tcsetattr(fd, TCSANOW, &origattr);
+ close(fd);
+ continue;
+ }
+ /* fill in the PnP com structure */
+ if (parse_pnp_string( pnp_string, pnp_strlen, &pnp_id )<0) {
+ goto endprobe;
+ } else {
+ char *foo;
+ int len;
+
+ if (debug_level > 0) {
+ printf("PnP ID string for serial device on port %s\n",port);
+ print_pnp_id( pnp_id );
+ }
+ serdev = serialNewDevice(NULL);
+ if (pnp_id.user_name[0]) {
+ serdev->pnpdesc = strdup(pnp_id.user_name);
+ len = strlen(pnp_id.eisa_id)+strlen(pnp_id.product_id)+strlen(pnp_id.user_name)+3;
+ foo = malloc(len);
+ snprintf(foo,len,"%s|%s %s",pnp_id.eisa_id,pnp_id.product_id,pnp_id.user_name);
+ } else {
+ len = strlen(pnp_id.eisa_id)+strlen(pnp_id.product_id)+3;
+ foo = malloc(len);
+ snprintf(foo,len,"%s|%s",pnp_id.eisa_id,pnp_id.product_id);
+ }
+ serdev->desc=strdup(foo);
+ serdev->device=strdup(port+5);
+ serdev->driver=strdup("ignore");
+ serdev->pnpmfr = strdup(pnp_id.eisa_id);
+ serdev->pnpmodel = strdup(pnp_id.product_id);
+
+ free(foo);
+ foo=pnp_id.product_id;
+ if (pnp_id.driver_id) {
+ if (strstr(pnp_id.driver_id,"PNP"))
+ foo = strstr(pnp_id.driver_id,"PNP")+3;
+ serdev->pnpcompat = strdup(pnp_id.driver_id);
+ }
+ if (!strncmp(foo, "0F", 2))
+ serdev->class = CLASS_MOUSE;
+ else if (!strncmp(foo, "C", 1))
+ serdev->class = CLASS_MODEM;
+ else if (!strncmp(pnp_id.class_name, "Modem", 5))
+ serdev->class = CLASS_MODEM;
+ else
+ serdev->class = CLASS_OTHER;
+ if (serdev->class == probeClass || probeClass == CLASS_UNSPEC) {
+ if (devlist)
+ serdev->next = devlist;
+ devlist = (struct device *)serdev;
+ if (probeFlags & PROBE_ONE) {
+ tcsetattr(fd, TCSANOW, &origattr);
+ tcflush(fd, TCIOFLUSH);
+ close(fd);
+ return devlist;
+ }
+ } else {
+ serdev->freeDevice(serdev);
+ }
+ goto endprobe;
+ }
+ } else {
+ /* try to find a legacy device */
+
+ temp = find_legacy_mouse(fd);
+ if (temp == PNP_COM_FATAL) {
+ goto endprobe;
+ } else if (temp == PNP_COM_OK) {
+ if (probeClass == CLASS_UNSPEC || probeClass == CLASS_MOUSE) {
+ serdev=serialNewDevice(NULL);
+ serdev->class = CLASS_MOUSE;
+ serdev->device = strdup(port+5);
+ serdev->driver= strdup("generic");
+ serdev->desc = strdup("Generic Serial Mouse");
+ if (devlist)
+ serdev->next = devlist;
+ devlist = (struct device *)serdev;
+ if (probeFlags & PROBE_ONE) {
+ tcsetattr(fd, TCSANOW, &origattr);
+ tcflush(fd, TCIOFLUSH);
+ close(fd);
+ return devlist;
+ }
+ }
+ goto endprobe;
+ } else {
+ if (debug_level > 0)
+ printf("Didnt see a legacy mouse, need to ATI it now.\n");
+
+ temp = find_legacy_modem(fd);
+ if (temp == PNP_COM_FATAL) {
+ goto endprobe;
+ } else if (temp == PNP_COM_OK) {
+ if (debug_level > 0)
+ printf("\nLegacy modem signature seen.\n\n");
+ if (probeClass == CLASS_UNSPEC || probeClass == CLASS_MODEM) {
+ serdev=serialNewDevice(NULL);
+ serdev->class = CLASS_MODEM;
+ serdev->device = strdup(port+5);
+ serdev->driver= strdup("ignore");
+ serdev->desc = strdup("Generic Serial Modem");
+ if (devlist)
+ serdev->next = devlist;
+ devlist = (struct device *)serdev;
+ if (probeFlags & PROBE_ONE) {
+ tcsetattr(fd, TCSANOW, &origattr);
+ tcflush(fd, TCIOFLUSH);
+ close(fd);
+ return devlist;
+ }
+ }
+ goto endprobe;
+ } else {
+ if (debug_level > 0)
+ printf("Didnt see a legacy modem, game over.\n");
+ }
+ }
+ }
+endprobe:
+ tcsetattr(fd, TCSANOW, &origattr);
+ tcflush(fd, TCIOFLUSH);
+ close(fd);
+ }
+ }
+ return devlist;
+}
diff --git a/tools/serial_probe/serial.h b/tools/serial_probe/serial.h
new file mode 100644
index 000000000..75d178ce9
--- /dev/null
+++ b/tools/serial_probe/serial.h
@@ -0,0 +1,43 @@
+/* Copyright 1999 Red Hat, Inc.
+ *
+ * This software may be freely redistributed under the terms of the GNU
+ * public license.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef _KUDZU_SERIAL_H_
+#define _KUDZU_SERIAL_H_
+
+#include "device.h"
+
+struct serialDevice {
+ /* common fields */
+ struct device *next; /* next device in list */
+ int index;
+ enum deviceClass class; /* type */
+ enum deviceBus bus; /* bus it's attached to */
+ char * device; /* device file associated with it */
+ char * driver; /* driver to load, if any */
+ char * desc; /* a description */
+ int detached;
+ /* serial-specific fields */
+ struct serialDevice *(*newDevice) (struct serialDevice *dev);
+ void (*freeDevice) (struct serialDevice *dev);
+ void (*writeDevice) (FILE *file, struct serialDevice *dev);
+ int (*compareDevice) (struct serialDevice *dev1, struct serialDevice *dev2);
+ char * pnpmfr;
+ char * pnpmodel;
+ char * pnpcompat;
+ char * pnpdesc;
+
+};
+
+struct serialDevice *serialNewDevice(struct serialDevice *dev);
+struct device *serialProbe(enum deviceClass probeClass, int probeFlags,
+ struct device *devlist);
+
+#endif
diff --git a/tools/serial_probe/serial_probe.c b/tools/serial_probe/serial_probe.c
new file mode 100644
index 000000000..0f4f57af4
--- /dev/null
+++ b/tools/serial_probe/serial_probe.c
@@ -0,0 +1,90 @@
+/* Copyright 1999 MandrakeSoft <fpons@mandrakesoft.com>
+ *
+ * The following file used by this one are copyrighted by RedHat and
+ * are taken from kudzu :
+ * device.h
+ * serial.h
+ * serial.c
+ * This file is taken from kudzu.c copyrighted by RedHat, 1999.
+ *
+ * This software may be freely redistributed under the terms of the GNU
+ * public license.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdio.h>
+
+#include "serial.h"
+#include "device.h"
+
+typedef struct device *(newFunc)(struct device *);
+typedef int (initFunc)();
+typedef struct device *(probeFunc)(enum deviceClass, int, struct device *);
+
+char *classStrings[] = {
+ "UNSPEC", "OTHER", "NETWORK", "SCSI", "VIDEO", "AUDIO",
+ "MOUSE", "MODEM", "CDROM", "TAPE", "FLOPPY", "SCANNER",
+ "HD", "RAID", "PRINTER", "CAPTURE", "KEYBOARD", NULL
+};
+
+struct device *newDevice(struct device *old, struct device *new) {
+ if (!old) {
+ if (!new) {
+ new = malloc(sizeof(struct device));
+ memset(new,'\0',sizeof(struct device));
+ }
+ new->class = CLASS_UNSPEC;
+ } else {
+ new->class = old->class;
+ if (old->device) new->device = strdup(old->device);
+ if (old->driver) new->driver = strdup(old->driver);
+ if (old->desc) new->desc = strdup(old->desc);
+ }
+ new->newDevice = newDevice;
+ new->freeDevice = freeDevice;
+ new->compareDevice = compareDevice;
+ return new;
+}
+
+void freeDevice(struct device *dev) {
+ if (!dev) {
+ printf("freeDevice(null)\n");
+ abort(); /* return; */
+ }
+ if (dev->device) free (dev->device);
+ if (dev->driver) free (dev->driver);
+ if (dev->desc) free (dev->desc);
+ free (dev);
+}
+
+void writeDevice(FILE *file, struct device *dev) {}
+int compareDevice(struct device *dev1, struct device *dev2) { return 0; }
+
+int main () {
+ struct device* devices = NULL;
+ struct serialDevice* serialDevice = NULL;
+
+ devices = serialProbe(CLASS_UNSPEC, 0, devices);
+ while (devices) {
+ serialDevice = (struct serialDevice*)devices;
+
+ printf("CLASS=%s\n", classStrings[serialDevice->class]);
+ printf("BUS=SERIAL\n");
+ printf("DEVICE=/dev/%s\n", serialDevice->device);
+ printf("DRIVER=%s\n", serialDevice->driver);
+ if (!serialDevice->pnpdesc) printf("DESCRIPTION=%s\n", serialDevice->desc);
+ if (serialDevice->pnpmfr) printf("MANUFACTURER=%s\n", serialDevice->pnpmfr);
+ if (serialDevice->pnpmodel) printf("MODEL=%s\n", serialDevice->pnpmodel);
+ if (serialDevice->pnpcompat) printf("COMPAT=%s\n", serialDevice->pnpcompat);
+ if (serialDevice->pnpdesc) printf("DESCRIPTION=%s\n", serialDevice->pnpdesc);
+ printf("\n");
+
+ devices=devices->next;
+ }
+
+ return 0;
+}