2008-10-16 09:04:33 +04:00
/*
2005-04-17 02:20:36 +04:00
* 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>
2006-01-06 11:19:48 +03:00
static const struct {
const char * token ;
const char * descr ;
2005-04-17 02:20:36 +04:00
} 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 " ) ) {
2005-11-07 12:01:32 +03:00
kfree ( info - > mfr ) ;
2005-06-23 11:09:02 +04:00
info - > mfr = kstrdup ( sep , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
} else if ( ! strcmp ( p , " MDL " ) | | ! strcmp ( p , " MODEL " ) ) {
2005-11-07 12:01:32 +03:00
kfree ( info - > model ) ;
2005-06-23 11:09:02 +04:00
info - > model = kstrdup ( sep , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
} else if ( ! strcmp ( p , " CLS " ) | | ! strcmp ( p , " CLASS " ) ) {
int i ;
2005-11-07 12:01:32 +03:00
kfree ( info - > class_name ) ;
2005-06-23 11:09:02 +04:00
info - > class_name = kstrdup ( sep , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04: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 " ) ) {
2005-11-07 12:01:32 +03:00
kfree ( info - > cmdset ) ;
2005-06-23 11:09:02 +04:00
info - > cmdset = kstrdup ( sep , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04: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 " ) ) {
2005-11-07 12:01:32 +03:00
kfree ( info - > description ) ;
2005-06-23 11:09:02 +04:00
info - > description = kstrdup ( sep , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
}
}
rock_on :
2005-11-07 12:01:32 +03:00
if ( q )
p = q + 1 ;
else
p = NULL ;
2005-04-17 02:20:36 +04:00
}
/* 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 ) ;
}
2006-01-06 11:19:43 +03:00
/* Read up to count-1 bytes of device id. Terminate buffer with
* ' \0 ' . Buffer begins with two Device ID length bytes as given by
* device . */
static ssize_t parport_read_device_id ( struct parport * port , char * buffer ,
size_t count )
{
unsigned char length [ 2 ] ;
unsigned lelen , belen ;
size_t idlens [ 4 ] ;
unsigned numidlens ;
unsigned current_idlen ;
ssize_t retval ;
size_t len ;
/* First two bytes are MSB,LSB of inclusive length. */
retval = parport_read ( port , length , 2 ) ;
if ( retval < 0 )
return retval ;
if ( retval ! = 2 )
return - EIO ;
if ( count < 2 )
return 0 ;
memcpy ( buffer , length , 2 ) ;
len = 2 ;
/* Some devices wrongly send LE length, and some send it two
* bytes short . Construct a sorted array of lengths to try . */
belen = ( length [ 0 ] < < 8 ) + length [ 1 ] ;
lelen = ( length [ 1 ] < < 8 ) + length [ 0 ] ;
idlens [ 0 ] = min ( belen , lelen ) ;
idlens [ 1 ] = idlens [ 0 ] + 2 ;
if ( belen ! = lelen ) {
int off = 2 ;
2008-02-03 16:42:53 +03:00
/* Don't try lengths of 0x100 and 0x200 as 1 and 2 */
2006-01-06 11:19:43 +03:00
if ( idlens [ 0 ] < = 2 )
off = 0 ;
idlens [ off ] = max ( belen , lelen ) ;
idlens [ off + 1 ] = idlens [ off ] + 2 ;
numidlens = off + 2 ;
}
else {
/* Some devices don't truly implement Device ID, but
* just return constant nibble forever . This catches
* also those cases . */
if ( idlens [ 0 ] = = 0 | | idlens [ 0 ] > 0xFFF ) {
printk ( KERN_DEBUG " %s: reported broken Device ID "
" length of %#zX bytes \n " ,
port - > name , idlens [ 0 ] ) ;
return - EIO ;
}
numidlens = 2 ;
}
/* Try to respect the given ID length despite all the bugs in
* the ID length . Read according to shortest possible ID
* first . */
for ( current_idlen = 0 ; current_idlen < numidlens ; + + current_idlen ) {
size_t idlen = idlens [ current_idlen ] ;
if ( idlen + 1 > = count )
break ;
retval = parport_read ( port , buffer + len , idlen - len ) ;
if ( retval < 0 )
return retval ;
len + = retval ;
if ( port - > physport - > ieee1284 . phase ! = IEEE1284_PH_HBUSY_DAVAIL ) {
if ( belen ! = len ) {
2006-02-03 14:03:59 +03:00
printk ( KERN_DEBUG " %s: Device ID was %zd bytes "
2006-01-06 11:19:43 +03:00
" while device told it would be %d "
" bytes \n " ,
port - > name , len , belen ) ;
}
goto done ;
}
/* This might end reading the Device ID too
* soon . Hopefully the needed fields were already in
* the first 256 bytes or so that we must have read so
* far . */
if ( buffer [ len - 1 ] = = ' ; ' ) {
printk ( KERN_DEBUG " %s: Device ID reading stopped "
" before device told data not available. "
2006-02-03 14:03:59 +03:00
" Current idlen %u of %u, len bytes %02X %02X \n " ,
2006-01-06 11:19:43 +03:00
port - > name , current_idlen , numidlens ,
length [ 0 ] , length [ 1 ] ) ;
goto done ;
}
}
if ( current_idlen < numidlens ) {
/* Buffer not large enough, read to end of buffer. */
size_t idlen , len2 ;
if ( len + 1 < count ) {
retval = parport_read ( port , buffer + len , count - len - 1 ) ;
if ( retval < 0 )
return retval ;
len + = retval ;
}
/* Read the whole ID since some devices would not
* otherwise give back the Device ID from beginning
* next time when asked . */
idlen = idlens [ current_idlen ] ;
len2 = len ;
while ( len2 < idlen & & retval > 0 ) {
char tmp [ 4 ] ;
retval = parport_read ( port , tmp ,
min ( sizeof tmp , idlen - len2 ) ) ;
if ( retval < 0 )
return retval ;
len2 + = retval ;
}
}
/* In addition, there are broken devices out there that don't
even finish off with a semi - colon . We do not need to care
about those at this time . */
done :
buffer [ len ] = ' \0 ' ;
return len ;
}
2005-04-17 02:20:36 +04:00
/* Get Std 1284 Device ID. */
2006-01-06 11:19:43 +03:00
ssize_t parport_device_id ( int devnum , char * buffer , size_t count )
2005-04-17 02:20:36 +04:00
{
ssize_t retval = - ENXIO ;
2007-10-19 10:54:26 +04:00
struct pardevice * dev = parport_open ( devnum , " Device ID probe " ) ;
2005-04-17 02:20:36 +04:00
if ( ! dev )
return - ENXIO ;
parport_claim_or_block ( dev ) ;
2006-01-06 11:19:43 +03:00
/* Negotiate to compatibility mode, and then to device ID
* mode . ( This so that we start form beginning of device ID if
* already in device ID mode . ) */
2005-04-17 02:20:36 +04:00
parport_negotiate ( dev - > port , IEEE1284_MODE_COMPAT ) ;
retval = parport_negotiate ( dev - > port ,
IEEE1284_MODE_NIBBLE | IEEE1284_DEVICEID ) ;
if ( ! retval ) {
2006-01-06 11:19:43 +03:00
retval = parport_read_device_id ( dev - > port , buffer , count ) ;
2005-04-17 02:20:36 +04:00
parport_negotiate ( dev - > port , IEEE1284_MODE_COMPAT ) ;
2006-01-06 11:19:43 +03:00
if ( retval > 2 )
parse_data ( dev - > port , dev - > daisy , buffer + 2 ) ;
2005-04-17 02:20:36 +04:00
}
parport_release ( dev ) ;
parport_close ( dev ) ;
return retval ;
}