mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-16 00:34:20 +08:00
543537bd92
This patch creates a new kstrdup library function and changes the "local" implementations in several places to use this function. Most of the changes come from the sound and net subsystems. The sound part had already been acknowledged by Takashi Iwai and the net part by David S. Miller. I left UML alone for now because I would need more time to read the code carefully before making changes there. Signed-off-by: Paulo Marques <pmarques@grupopie.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
217 lines
5.8 KiB
C
217 lines
5.8 KiB
C
/* $Id: parport_probe.c,v 1.1 1999/07/03 08:56:17 davem Exp $
|
|
* Parallel port device probing code
|
|
*
|
|
* Authors: Carsten Gross, carsten@sol.wohnheim.uni-ulm.de
|
|
* Philip Blundell <philb@gnu.org>
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/parport.h>
|
|
#include <linux/ctype.h>
|
|
#include <linux/string.h>
|
|
#include <asm/uaccess.h>
|
|
|
|
static struct {
|
|
char *token;
|
|
char *descr;
|
|
} classes[] = {
|
|
{ "", "Legacy device" },
|
|
{ "PRINTER", "Printer" },
|
|
{ "MODEM", "Modem" },
|
|
{ "NET", "Network device" },
|
|
{ "HDC", "Hard disk" },
|
|
{ "PCMCIA", "PCMCIA" },
|
|
{ "MEDIA", "Multimedia device" },
|
|
{ "FDC", "Floppy disk" },
|
|
{ "PORTS", "Ports" },
|
|
{ "SCANNER", "Scanner" },
|
|
{ "DIGICAM", "Digital camera" },
|
|
{ "", "Unknown device" },
|
|
{ "", "Unspecified" },
|
|
{ "SCSIADAPTER", "SCSI adapter" },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
static void pretty_print(struct parport *port, int device)
|
|
{
|
|
struct parport_device_info *info = &port->probe_info[device + 1];
|
|
|
|
printk(KERN_INFO "%s", port->name);
|
|
|
|
if (device >= 0)
|
|
printk (" (addr %d)", device);
|
|
|
|
printk (": %s", classes[info->class].descr);
|
|
if (info->class)
|
|
printk(", %s %s", info->mfr, info->model);
|
|
|
|
printk("\n");
|
|
}
|
|
|
|
static void parse_data(struct parport *port, int device, char *str)
|
|
{
|
|
char *txt = kmalloc(strlen(str)+1, GFP_KERNEL);
|
|
char *p = txt, *q;
|
|
int guessed_class = PARPORT_CLASS_UNSPEC;
|
|
struct parport_device_info *info = &port->probe_info[device + 1];
|
|
|
|
if (!txt) {
|
|
printk(KERN_WARNING "%s probe: memory squeeze\n", port->name);
|
|
return;
|
|
}
|
|
strcpy(txt, str);
|
|
while (p) {
|
|
char *sep;
|
|
q = strchr(p, ';');
|
|
if (q) *q = 0;
|
|
sep = strchr(p, ':');
|
|
if (sep) {
|
|
char *u;
|
|
*(sep++) = 0;
|
|
/* Get rid of trailing blanks */
|
|
u = sep + strlen (sep) - 1;
|
|
while (u >= p && *u == ' ')
|
|
*u-- = '\0';
|
|
u = p;
|
|
while (*u) {
|
|
*u = toupper(*u);
|
|
u++;
|
|
}
|
|
if (!strcmp(p, "MFG") || !strcmp(p, "MANUFACTURER")) {
|
|
if (info->mfr)
|
|
kfree (info->mfr);
|
|
info->mfr = kstrdup(sep, GFP_KERNEL);
|
|
} else if (!strcmp(p, "MDL") || !strcmp(p, "MODEL")) {
|
|
if (info->model)
|
|
kfree (info->model);
|
|
info->model = kstrdup(sep, GFP_KERNEL);
|
|
} else if (!strcmp(p, "CLS") || !strcmp(p, "CLASS")) {
|
|
int i;
|
|
if (info->class_name)
|
|
kfree (info->class_name);
|
|
info->class_name = kstrdup(sep, GFP_KERNEL);
|
|
for (u = sep; *u; u++)
|
|
*u = toupper(*u);
|
|
for (i = 0; classes[i].token; i++) {
|
|
if (!strcmp(classes[i].token, sep)) {
|
|
info->class = i;
|
|
goto rock_on;
|
|
}
|
|
}
|
|
printk(KERN_WARNING "%s probe: warning, class '%s' not understood.\n", port->name, sep);
|
|
info->class = PARPORT_CLASS_OTHER;
|
|
} else if (!strcmp(p, "CMD") ||
|
|
!strcmp(p, "COMMAND SET")) {
|
|
if (info->cmdset)
|
|
kfree (info->cmdset);
|
|
info->cmdset = kstrdup(sep, GFP_KERNEL);
|
|
/* if it speaks printer language, it's
|
|
probably a printer */
|
|
if (strstr(sep, "PJL") || strstr(sep, "PCL"))
|
|
guessed_class = PARPORT_CLASS_PRINTER;
|
|
} else if (!strcmp(p, "DES") || !strcmp(p, "DESCRIPTION")) {
|
|
if (info->description)
|
|
kfree (info->description);
|
|
info->description = kstrdup(sep, GFP_KERNEL);
|
|
}
|
|
}
|
|
rock_on:
|
|
if (q) p = q+1; else p=NULL;
|
|
}
|
|
|
|
/* If the device didn't tell us its class, maybe we have managed to
|
|
guess one from the things it did say. */
|
|
if (info->class == PARPORT_CLASS_UNSPEC)
|
|
info->class = guessed_class;
|
|
|
|
pretty_print (port, device);
|
|
|
|
kfree(txt);
|
|
}
|
|
|
|
/* Get Std 1284 Device ID. */
|
|
ssize_t parport_device_id (int devnum, char *buffer, size_t len)
|
|
{
|
|
ssize_t retval = -ENXIO;
|
|
struct pardevice *dev = parport_open (devnum, "Device ID probe",
|
|
NULL, NULL, NULL, 0, NULL);
|
|
if (!dev)
|
|
return -ENXIO;
|
|
|
|
parport_claim_or_block (dev);
|
|
|
|
/* Negotiate to compatibility mode, and then to device ID mode.
|
|
* (This is in case we are already in device ID mode.) */
|
|
parport_negotiate (dev->port, IEEE1284_MODE_COMPAT);
|
|
retval = parport_negotiate (dev->port,
|
|
IEEE1284_MODE_NIBBLE | IEEE1284_DEVICEID);
|
|
|
|
if (!retval) {
|
|
int idlen;
|
|
unsigned char length[2];
|
|
|
|
/* First two bytes are MSB,LSB of inclusive length. */
|
|
retval = parport_read (dev->port, length, 2);
|
|
|
|
if (retval != 2) goto end_id;
|
|
|
|
idlen = (length[0] << 8) + length[1] - 2;
|
|
/*
|
|
* Check if the caller-allocated buffer is large enough
|
|
* otherwise bail out or there will be an at least off by one.
|
|
*/
|
|
if (idlen + 1 < len)
|
|
len = idlen;
|
|
else {
|
|
retval = -EINVAL;
|
|
goto out;
|
|
}
|
|
retval = parport_read (dev->port, buffer, len);
|
|
|
|
if (retval != len)
|
|
printk (KERN_DEBUG "%s: only read %Zd of %Zd ID bytes\n",
|
|
dev->port->name, retval,
|
|
len);
|
|
|
|
/* Some printer manufacturers mistakenly believe that
|
|
the length field is supposed to be _exclusive_.
|
|
In addition, there are broken devices out there
|
|
that don't even finish off with a semi-colon. */
|
|
if (buffer[len - 1] != ';') {
|
|
ssize_t diff;
|
|
diff = parport_read (dev->port, buffer + len, 2);
|
|
retval += diff;
|
|
|
|
if (diff)
|
|
printk (KERN_DEBUG
|
|
"%s: device reported incorrect "
|
|
"length field (%d, should be %Zd)\n",
|
|
dev->port->name, idlen, retval);
|
|
else {
|
|
/* One semi-colon short of a device ID. */
|
|
buffer[len++] = ';';
|
|
printk (KERN_DEBUG "%s: faking semi-colon\n",
|
|
dev->port->name);
|
|
|
|
/* If we get here, I don't think we
|
|
need to worry about the possible
|
|
standard violation of having read
|
|
more than we were told to. The
|
|
device is non-compliant anyhow. */
|
|
}
|
|
}
|
|
|
|
end_id:
|
|
buffer[len] = '\0';
|
|
parport_negotiate (dev->port, IEEE1284_MODE_COMPAT);
|
|
}
|
|
|
|
if (retval > 2)
|
|
parse_data (dev->port, dev->daisy, buffer);
|
|
|
|
out:
|
|
parport_release (dev);
|
|
parport_close (dev);
|
|
return retval;
|
|
}
|