2005-04-16 15:20:36 -07:00
/* $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 ) ;
2005-06-23 00:09:02 -07:00
info - > mfr = kstrdup ( sep , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
} else if ( ! strcmp ( p , " MDL " ) | | ! strcmp ( p , " MODEL " ) ) {
if ( info - > model )
kfree ( info - > model ) ;
2005-06-23 00:09:02 -07:00
info - > model = kstrdup ( sep , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
} else if ( ! strcmp ( p , " CLS " ) | | ! strcmp ( p , " CLASS " ) ) {
int i ;
if ( info - > class_name )
kfree ( info - > class_name ) ;
2005-06-23 00:09:02 -07:00
info - > class_name = kstrdup ( sep , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
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 ) ;
2005-06-23 00:09:02 -07:00
info - > cmdset = kstrdup ( sep , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
/* 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 ) ;
2005-06-23 00:09:02 -07:00
info - > description = kstrdup ( sep , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
}
}
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 ;
}