2005-04-17 02:20:36 +04:00
/*
* rsparser . c - parses and encodes pnpbios resource data streams
*/
# include <linux/ctype.h>
# include <linux/pnp.h>
# include <linux/pnpbios.h>
2005-10-31 02:03:48 +03:00
# include <linux/string.h>
# include <linux/slab.h>
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_PCI
# include <linux/pci.h>
# else
2007-07-26 21:41:20 +04:00
inline void pcibios_penalize_isa_irq ( int irq , int active )
{
}
2007-07-26 21:41:21 +04:00
# endif /* CONFIG_PCI */
2005-04-17 02:20:36 +04:00
2008-04-29 02:33:52 +04:00
# include "../base.h"
2005-04-17 02:20:36 +04:00
# include "pnpbios.h"
/* standard resource tags */
# define SMALL_TAG_PNPVERNO 0x01
# define SMALL_TAG_LOGDEVID 0x02
# define SMALL_TAG_COMPATDEVID 0x03
# define SMALL_TAG_IRQ 0x04
# define SMALL_TAG_DMA 0x05
# define SMALL_TAG_STARTDEP 0x06
# define SMALL_TAG_ENDDEP 0x07
# define SMALL_TAG_PORT 0x08
# define SMALL_TAG_FIXEDPORT 0x09
# define SMALL_TAG_VENDOR 0x0e
# define SMALL_TAG_END 0x0f
# define LARGE_TAG 0x80
# define LARGE_TAG_MEM 0x81
# define LARGE_TAG_ANSISTR 0x82
# define LARGE_TAG_UNICODESTR 0x83
# define LARGE_TAG_VENDOR 0x84
# define LARGE_TAG_MEM32 0x85
# define LARGE_TAG_FIXEDMEM32 0x86
/*
* Resource Data Stream Format :
*
* Allocated Resources ( required )
* end tag - >
* Resource Configuration Options ( optional )
* end tag - >
* Compitable Device IDs ( optional )
* final end tag - >
*/
/*
* Allocated Resources
*/
2007-07-26 21:41:21 +04:00
static void pnpbios_parse_allocated_irqresource ( struct pnp_resource_table * res ,
int irq )
2005-04-17 02:20:36 +04:00
{
int i = 0 ;
2007-07-26 21:41:21 +04:00
2007-07-26 21:41:20 +04:00
while ( ! ( res - > irq_resource [ i ] . flags & IORESOURCE_UNSET )
& & i < PNP_MAX_IRQ )
i + + ;
2005-04-17 02:20:36 +04:00
if ( i < PNP_MAX_IRQ ) {
2007-07-26 21:41:20 +04:00
res - > irq_resource [ i ] . flags = IORESOURCE_IRQ ; // Also clears _UNSET flag
2005-04-17 02:20:36 +04:00
if ( irq = = - 1 ) {
res - > irq_resource [ i ] . flags | = IORESOURCE_DISABLED ;
return ;
}
res - > irq_resource [ i ] . start =
2007-07-26 21:41:20 +04:00
res - > irq_resource [ i ] . end = ( unsigned long ) irq ;
2005-04-01 09:07:31 +04:00
pcibios_penalize_isa_irq ( irq , 1 ) ;
2005-04-17 02:20:36 +04:00
}
}
2007-07-26 21:41:21 +04:00
static void pnpbios_parse_allocated_dmaresource ( struct pnp_resource_table * res ,
int dma )
2005-04-17 02:20:36 +04:00
{
int i = 0 ;
2007-07-26 21:41:21 +04:00
2005-05-01 19:59:29 +04:00
while ( i < PNP_MAX_DMA & &
2007-07-26 21:41:20 +04:00
! ( res - > dma_resource [ i ] . flags & IORESOURCE_UNSET ) )
2005-05-01 19:59:29 +04:00
i + + ;
2005-04-17 02:20:36 +04:00
if ( i < PNP_MAX_DMA ) {
2007-07-26 21:41:20 +04:00
res - > dma_resource [ i ] . flags = IORESOURCE_DMA ; // Also clears _UNSET flag
2005-04-17 02:20:36 +04:00
if ( dma = = - 1 ) {
res - > dma_resource [ i ] . flags | = IORESOURCE_DISABLED ;
return ;
}
res - > dma_resource [ i ] . start =
2007-07-26 21:41:20 +04:00
res - > dma_resource [ i ] . end = ( unsigned long ) dma ;
2005-04-17 02:20:36 +04:00
}
}
2007-07-26 21:41:21 +04:00
static void pnpbios_parse_allocated_ioresource ( struct pnp_resource_table * res ,
int io , int len )
2005-04-17 02:20:36 +04:00
{
int i = 0 ;
2007-07-26 21:41:21 +04:00
2007-07-26 21:41:20 +04:00
while ( ! ( res - > port_resource [ i ] . flags & IORESOURCE_UNSET )
& & i < PNP_MAX_PORT )
i + + ;
2005-04-17 02:20:36 +04:00
if ( i < PNP_MAX_PORT ) {
2007-07-26 21:41:20 +04:00
res - > port_resource [ i ] . flags = IORESOURCE_IO ; // Also clears _UNSET flag
if ( len < = 0 | | ( io + len - 1 ) > = 0x10003 ) {
2005-04-17 02:20:36 +04:00
res - > port_resource [ i ] . flags | = IORESOURCE_DISABLED ;
return ;
}
2007-07-26 21:41:20 +04:00
res - > port_resource [ i ] . start = ( unsigned long ) io ;
2005-04-17 02:20:36 +04:00
res - > port_resource [ i ] . end = ( unsigned long ) ( io + len - 1 ) ;
}
}
2007-07-26 21:41:21 +04:00
static void pnpbios_parse_allocated_memresource ( struct pnp_resource_table * res ,
int mem , int len )
2005-04-17 02:20:36 +04:00
{
int i = 0 ;
2007-07-26 21:41:21 +04:00
2007-07-26 21:41:20 +04:00
while ( ! ( res - > mem_resource [ i ] . flags & IORESOURCE_UNSET )
& & i < PNP_MAX_MEM )
i + + ;
2005-04-17 02:20:36 +04:00
if ( i < PNP_MAX_MEM ) {
2007-07-26 21:41:20 +04:00
res - > mem_resource [ i ] . flags = IORESOURCE_MEM ; // Also clears _UNSET flag
2005-04-17 02:20:36 +04:00
if ( len < = 0 ) {
res - > mem_resource [ i ] . flags | = IORESOURCE_DISABLED ;
return ;
}
2007-07-26 21:41:20 +04:00
res - > mem_resource [ i ] . start = ( unsigned long ) mem ;
2005-04-17 02:20:36 +04:00
res - > mem_resource [ i ] . end = ( unsigned long ) ( mem + len - 1 ) ;
}
}
2007-07-26 21:41:20 +04:00
static unsigned char * pnpbios_parse_allocated_resource_data ( unsigned char * p ,
unsigned char * end ,
struct
pnp_resource_table
* res )
2005-04-17 02:20:36 +04:00
{
unsigned int len , tag ;
int io , size , mask , i ;
if ( ! p )
return NULL ;
/* Blank the resource table values */
pnp_init_resource_table ( res ) ;
while ( ( char * ) p < ( char * ) end ) {
/* determine the type of tag */
2007-07-26 21:41:20 +04:00
if ( p [ 0 ] & LARGE_TAG ) { /* large tag */
2005-04-17 02:20:36 +04:00
len = ( p [ 2 ] < < 8 ) | p [ 1 ] ;
tag = p [ 0 ] ;
2007-07-26 21:41:20 +04:00
} else { /* small tag */
2005-04-17 02:20:36 +04:00
len = p [ 0 ] & 0x07 ;
2007-07-26 21:41:20 +04:00
tag = ( ( p [ 0 ] > > 3 ) & 0x0f ) ;
2005-04-17 02:20:36 +04:00
}
switch ( tag ) {
case LARGE_TAG_MEM :
if ( len ! = 9 )
goto len_err ;
2007-07-26 21:41:20 +04:00
io = * ( short * ) & p [ 4 ] ;
size = * ( short * ) & p [ 10 ] ;
2005-04-17 02:20:36 +04:00
pnpbios_parse_allocated_memresource ( res , io , size ) ;
break ;
case LARGE_TAG_ANSISTR :
/* ignore this for now */
break ;
case LARGE_TAG_VENDOR :
/* do nothing */
break ;
case LARGE_TAG_MEM32 :
if ( len ! = 17 )
goto len_err ;
2007-07-26 21:41:20 +04:00
io = * ( int * ) & p [ 4 ] ;
size = * ( int * ) & p [ 16 ] ;
2005-04-17 02:20:36 +04:00
pnpbios_parse_allocated_memresource ( res , io , size ) ;
break ;
case LARGE_TAG_FIXEDMEM32 :
if ( len ! = 9 )
goto len_err ;
2007-07-26 21:41:20 +04:00
io = * ( int * ) & p [ 4 ] ;
size = * ( int * ) & p [ 8 ] ;
2005-04-17 02:20:36 +04:00
pnpbios_parse_allocated_memresource ( res , io , size ) ;
break ;
case SMALL_TAG_IRQ :
if ( len < 2 | | len > 3 )
goto len_err ;
io = - 1 ;
2007-07-26 21:41:20 +04:00
mask = p [ 1 ] + p [ 2 ] * 256 ;
for ( i = 0 ; i < 16 ; i + + , mask = mask > > 1 )
if ( mask & 0x01 )
io = i ;
2005-04-17 02:20:36 +04:00
pnpbios_parse_allocated_irqresource ( res , io ) ;
break ;
case SMALL_TAG_DMA :
if ( len ! = 2 )
goto len_err ;
io = - 1 ;
mask = p [ 1 ] ;
2007-07-26 21:41:20 +04:00
for ( i = 0 ; i < 8 ; i + + , mask = mask > > 1 )
if ( mask & 0x01 )
io = i ;
2005-04-17 02:20:36 +04:00
pnpbios_parse_allocated_dmaresource ( res , io ) ;
break ;
case SMALL_TAG_PORT :
if ( len ! = 7 )
goto len_err ;
2007-07-26 21:41:20 +04:00
io = p [ 2 ] + p [ 3 ] * 256 ;
2005-04-17 02:20:36 +04:00
size = p [ 7 ] ;
pnpbios_parse_allocated_ioresource ( res , io , size ) ;
break ;
case SMALL_TAG_VENDOR :
/* do nothing */
break ;
case SMALL_TAG_FIXEDPORT :
if ( len ! = 3 )
goto len_err ;
io = p [ 1 ] + p [ 2 ] * 256 ;
size = p [ 3 ] ;
pnpbios_parse_allocated_ioresource ( res , io , size ) ;
break ;
case SMALL_TAG_END :
p = p + 2 ;
2007-07-26 21:41:20 +04:00
return ( unsigned char * ) p ;
2005-04-17 02:20:36 +04:00
break ;
2007-07-26 21:41:20 +04:00
default : /* an unkown tag */
2007-08-15 20:32:08 +04:00
len_err :
2007-07-26 21:41:20 +04:00
printk ( KERN_ERR
" PnPBIOS: Unknown tag '0x%x', length '%d'. \n " ,
tag , len ) ;
2005-04-17 02:20:36 +04:00
break ;
}
/* continue to the next tag */
if ( p [ 0 ] & LARGE_TAG )
p + = len + 3 ;
else
p + = len + 1 ;
}
2007-07-26 21:41:20 +04:00
printk ( KERN_ERR
" PnPBIOS: Resource structure does not contain an end tag. \n " ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
/*
* Resource Configuration Options
*/
2008-02-06 12:40:03 +03:00
static __init void pnpbios_parse_mem_option ( unsigned char * p , int size ,
struct pnp_option * option )
2005-04-17 02:20:36 +04:00
{
2007-07-26 21:41:20 +04:00
struct pnp_mem * mem ;
2007-07-26 21:41:21 +04:00
2006-12-13 11:34:52 +03:00
mem = kzalloc ( sizeof ( struct pnp_mem ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! mem )
return ;
mem - > min = ( ( p [ 5 ] < < 8 ) | p [ 4 ] ) < < 8 ;
mem - > max = ( ( p [ 7 ] < < 8 ) | p [ 6 ] ) < < 8 ;
mem - > align = ( p [ 9 ] < < 8 ) | p [ 8 ] ;
mem - > size = ( ( p [ 11 ] < < 8 ) | p [ 10 ] ) < < 8 ;
mem - > flags = p [ 3 ] ;
2007-07-26 21:41:20 +04:00
pnp_register_mem_resource ( option , mem ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-06 12:40:03 +03:00
static __init void pnpbios_parse_mem32_option ( unsigned char * p , int size ,
struct pnp_option * option )
2005-04-17 02:20:36 +04:00
{
2007-07-26 21:41:20 +04:00
struct pnp_mem * mem ;
2007-07-26 21:41:21 +04:00
2006-12-13 11:34:52 +03:00
mem = kzalloc ( sizeof ( struct pnp_mem ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! mem )
return ;
mem - > min = ( p [ 7 ] < < 24 ) | ( p [ 6 ] < < 16 ) | ( p [ 5 ] < < 8 ) | p [ 4 ] ;
mem - > max = ( p [ 11 ] < < 24 ) | ( p [ 10 ] < < 16 ) | ( p [ 9 ] < < 8 ) | p [ 8 ] ;
mem - > align = ( p [ 15 ] < < 24 ) | ( p [ 14 ] < < 16 ) | ( p [ 13 ] < < 8 ) | p [ 12 ] ;
mem - > size = ( p [ 19 ] < < 24 ) | ( p [ 18 ] < < 16 ) | ( p [ 17 ] < < 8 ) | p [ 16 ] ;
mem - > flags = p [ 3 ] ;
2007-07-26 21:41:20 +04:00
pnp_register_mem_resource ( option , mem ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-06 12:40:03 +03:00
static __init void pnpbios_parse_fixed_mem32_option ( unsigned char * p , int size ,
struct pnp_option * option )
2005-04-17 02:20:36 +04:00
{
2007-07-26 21:41:20 +04:00
struct pnp_mem * mem ;
2007-08-15 20:32:08 +04:00
2006-12-13 11:34:52 +03:00
mem = kzalloc ( sizeof ( struct pnp_mem ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! mem )
return ;
mem - > min = mem - > max = ( p [ 7 ] < < 24 ) | ( p [ 6 ] < < 16 ) | ( p [ 5 ] < < 8 ) | p [ 4 ] ;
mem - > size = ( p [ 11 ] < < 24 ) | ( p [ 10 ] < < 16 ) | ( p [ 9 ] < < 8 ) | p [ 8 ] ;
mem - > align = 0 ;
mem - > flags = p [ 3 ] ;
2007-07-26 21:41:20 +04:00
pnp_register_mem_resource ( option , mem ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-06 12:40:03 +03:00
static __init void pnpbios_parse_irq_option ( unsigned char * p , int size ,
2007-07-26 21:41:21 +04:00
struct pnp_option * option )
2005-04-17 02:20:36 +04:00
{
2007-07-26 21:41:20 +04:00
struct pnp_irq * irq ;
2005-04-17 02:20:36 +04:00
unsigned long bits ;
2006-12-13 11:34:52 +03:00
irq = kzalloc ( sizeof ( struct pnp_irq ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! irq )
return ;
bits = ( p [ 2 ] < < 8 ) | p [ 1 ] ;
bitmap_copy ( irq - > map , & bits , 16 ) ;
if ( size > 2 )
irq - > flags = p [ 3 ] ;
else
irq - > flags = IORESOURCE_IRQ_HIGHEDGE ;
2007-07-26 21:41:20 +04:00
pnp_register_irq_resource ( option , irq ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-06 12:40:03 +03:00
static __init void pnpbios_parse_dma_option ( unsigned char * p , int size ,
2007-07-26 21:41:21 +04:00
struct pnp_option * option )
2005-04-17 02:20:36 +04:00
{
2007-07-26 21:41:20 +04:00
struct pnp_dma * dma ;
2007-07-26 21:41:21 +04:00
2006-12-13 11:34:52 +03:00
dma = kzalloc ( sizeof ( struct pnp_dma ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! dma )
return ;
dma - > map = p [ 1 ] ;
dma - > flags = p [ 2 ] ;
2007-07-26 21:41:20 +04:00
pnp_register_dma_resource ( option , dma ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-06 12:40:03 +03:00
static __init void pnpbios_parse_port_option ( unsigned char * p , int size ,
struct pnp_option * option )
2005-04-17 02:20:36 +04:00
{
2007-07-26 21:41:20 +04:00
struct pnp_port * port ;
2007-07-26 21:41:21 +04:00
2006-12-13 11:34:52 +03:00
port = kzalloc ( sizeof ( struct pnp_port ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! port )
return ;
port - > min = ( p [ 3 ] < < 8 ) | p [ 2 ] ;
port - > max = ( p [ 5 ] < < 8 ) | p [ 4 ] ;
port - > align = p [ 6 ] ;
port - > size = p [ 7 ] ;
port - > flags = p [ 1 ] ? PNP_PORT_FLAG_16BITADDR : 0 ;
2007-07-26 21:41:20 +04:00
pnp_register_port_resource ( option , port ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-06 12:40:03 +03:00
static __init void pnpbios_parse_fixed_port_option ( unsigned char * p , int size ,
struct pnp_option * option )
2005-04-17 02:20:36 +04:00
{
2007-07-26 21:41:20 +04:00
struct pnp_port * port ;
2007-07-26 21:41:21 +04:00
2006-12-13 11:34:52 +03:00
port = kzalloc ( sizeof ( struct pnp_port ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! port )
return ;
port - > min = port - > max = ( p [ 2 ] < < 8 ) | p [ 1 ] ;
port - > size = p [ 3 ] ;
port - > align = 0 ;
port - > flags = PNP_PORT_FLAG_FIXED ;
2007-07-26 21:41:20 +04:00
pnp_register_port_resource ( option , port ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-06 12:40:03 +03:00
static __init unsigned char *
pnpbios_parse_resource_option_data ( unsigned char * p , unsigned char * end ,
struct pnp_dev * dev )
2005-04-17 02:20:36 +04:00
{
unsigned int len , tag ;
int priority = 0 ;
struct pnp_option * option , * option_independent ;
if ( ! p )
return NULL ;
option_independent = option = pnp_register_independent_option ( dev ) ;
if ( ! option )
return NULL ;
while ( ( char * ) p < ( char * ) end ) {
/* determine the type of tag */
2007-07-26 21:41:20 +04:00
if ( p [ 0 ] & LARGE_TAG ) { /* large tag */
2005-04-17 02:20:36 +04:00
len = ( p [ 2 ] < < 8 ) | p [ 1 ] ;
tag = p [ 0 ] ;
2007-07-26 21:41:20 +04:00
} else { /* small tag */
2005-04-17 02:20:36 +04:00
len = p [ 0 ] & 0x07 ;
2007-07-26 21:41:20 +04:00
tag = ( ( p [ 0 ] > > 3 ) & 0x0f ) ;
2005-04-17 02:20:36 +04:00
}
switch ( tag ) {
case LARGE_TAG_MEM :
if ( len ! = 9 )
goto len_err ;
pnpbios_parse_mem_option ( p , len , option ) ;
break ;
case LARGE_TAG_MEM32 :
if ( len ! = 17 )
goto len_err ;
pnpbios_parse_mem32_option ( p , len , option ) ;
break ;
case LARGE_TAG_FIXEDMEM32 :
if ( len ! = 9 )
goto len_err ;
pnpbios_parse_fixed_mem32_option ( p , len , option ) ;
break ;
case SMALL_TAG_IRQ :
if ( len < 2 | | len > 3 )
goto len_err ;
pnpbios_parse_irq_option ( p , len , option ) ;
break ;
case SMALL_TAG_DMA :
if ( len ! = 2 )
goto len_err ;
pnpbios_parse_dma_option ( p , len , option ) ;
break ;
case SMALL_TAG_PORT :
if ( len ! = 7 )
goto len_err ;
pnpbios_parse_port_option ( p , len , option ) ;
break ;
case SMALL_TAG_VENDOR :
/* do nothing */
break ;
case SMALL_TAG_FIXEDPORT :
if ( len ! = 3 )
goto len_err ;
pnpbios_parse_fixed_port_option ( p , len , option ) ;
break ;
case SMALL_TAG_STARTDEP :
if ( len > 1 )
goto len_err ;
priority = 0x100 | PNP_RES_PRIORITY_ACCEPTABLE ;
if ( len > 0 )
priority = 0x100 | p [ 1 ] ;
option = pnp_register_dependent_option ( dev , priority ) ;
if ( ! option )
return NULL ;
break ;
case SMALL_TAG_ENDDEP :
if ( len ! = 0 )
goto len_err ;
if ( option_independent = = option )
2007-07-26 21:41:20 +04:00
printk ( KERN_WARNING
" PnPBIOS: Missing SMALL_TAG_STARTDEP tag \n " ) ;
2005-04-17 02:20:36 +04:00
option = option_independent ;
break ;
case SMALL_TAG_END :
2007-07-26 21:41:20 +04:00
return p + 2 ;
2005-04-17 02:20:36 +04:00
2007-07-26 21:41:20 +04:00
default : /* an unkown tag */
2007-08-15 20:32:08 +04:00
len_err :
2007-07-26 21:41:20 +04:00
printk ( KERN_ERR
" PnPBIOS: Unknown tag '0x%x', length '%d'. \n " ,
tag , len ) ;
2005-04-17 02:20:36 +04:00
break ;
}
/* continue to the next tag */
if ( p [ 0 ] & LARGE_TAG )
p + = len + 3 ;
else
p + = len + 1 ;
}
2007-07-26 21:41:20 +04:00
printk ( KERN_ERR
" PnPBIOS: Resource structure does not contain an end tag. \n " ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
/*
* Compatible Device IDs
*/
# define HEX(id,a) hex[((id)>>a) & 15]
# define CHAR(id,a) (0x40 + (((id)>>a) & 31))
void pnpid32_to_pnpid ( u32 id , char * str )
{
const char * hex = " 0123456789abcdef " ;
id = be32_to_cpu ( id ) ;
str [ 0 ] = CHAR ( id , 26 ) ;
str [ 1 ] = CHAR ( id , 21 ) ;
2007-07-26 21:41:20 +04:00
str [ 2 ] = CHAR ( id , 16 ) ;
2005-04-17 02:20:36 +04:00
str [ 3 ] = HEX ( id , 12 ) ;
str [ 4 ] = HEX ( id , 8 ) ;
str [ 5 ] = HEX ( id , 4 ) ;
str [ 6 ] = HEX ( id , 0 ) ;
str [ 7 ] = ' \0 ' ;
}
2007-07-26 21:41:20 +04:00
2005-04-17 02:20:36 +04:00
# undef CHAR
# undef HEX
2007-07-26 21:41:20 +04:00
static unsigned char * pnpbios_parse_compatible_ids ( unsigned char * p ,
unsigned char * end ,
struct pnp_dev * dev )
2005-04-17 02:20:36 +04:00
{
int len , tag ;
char id [ 8 ] ;
struct pnp_id * dev_id ;
if ( ! p )
return NULL ;
while ( ( char * ) p < ( char * ) end ) {
/* determine the type of tag */
2007-07-26 21:41:20 +04:00
if ( p [ 0 ] & LARGE_TAG ) { /* large tag */
2005-04-17 02:20:36 +04:00
len = ( p [ 2 ] < < 8 ) | p [ 1 ] ;
tag = p [ 0 ] ;
2007-07-26 21:41:20 +04:00
} else { /* small tag */
2005-04-17 02:20:36 +04:00
len = p [ 0 ] & 0x07 ;
2007-07-26 21:41:20 +04:00
tag = ( ( p [ 0 ] > > 3 ) & 0x0f ) ;
2005-04-17 02:20:36 +04:00
}
switch ( tag ) {
case LARGE_TAG_ANSISTR :
2007-07-26 21:41:20 +04:00
strncpy ( dev - > name , p + 3 ,
len > = PNP_NAME_LEN ? PNP_NAME_LEN - 2 : len ) ;
dev - > name [ len > =
PNP_NAME_LEN ? PNP_NAME_LEN - 1 : len ] = ' \0 ' ;
2005-04-17 02:20:36 +04:00
break ;
2007-07-26 21:41:20 +04:00
case SMALL_TAG_COMPATDEVID : /* compatible ID */
2005-04-17 02:20:36 +04:00
if ( len ! = 4 )
goto len_err ;
2007-07-26 21:41:20 +04:00
pnpid32_to_pnpid ( p [ 1 ] | p [ 2 ] < < 8 | p [ 3 ] < < 16 | p [ 4 ] < <
24 , id ) ;
2008-04-29 02:33:52 +04:00
dev_id = pnp_add_id ( dev , id ) ;
if ( ! dev_id )
return NULL ;
2005-04-17 02:20:36 +04:00
break ;
case SMALL_TAG_END :
p = p + 2 ;
2007-07-26 21:41:20 +04:00
return ( unsigned char * ) p ;
2005-04-17 02:20:36 +04:00
break ;
2007-07-26 21:41:20 +04:00
default : /* an unkown tag */
2007-08-15 20:32:08 +04:00
len_err :
2007-07-26 21:41:20 +04:00
printk ( KERN_ERR
" PnPBIOS: Unknown tag '0x%x', length '%d'. \n " ,
tag , len ) ;
2005-04-17 02:20:36 +04:00
break ;
}
/* continue to the next tag */
if ( p [ 0 ] & LARGE_TAG )
p + = len + 3 ;
else
p + = len + 1 ;
}
2007-07-26 21:41:20 +04:00
printk ( KERN_ERR
" PnPBIOS: Resource structure does not contain an end tag. \n " ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
/*
* Allocated Resource Encoding
*/
2007-07-26 21:41:20 +04:00
static void pnpbios_encode_mem ( unsigned char * p , struct resource * res )
2005-04-17 02:20:36 +04:00
{
unsigned long base = res - > start ;
unsigned long len = res - > end - res - > start + 1 ;
2007-07-26 21:41:21 +04:00
2005-04-17 02:20:36 +04:00
p [ 4 ] = ( base > > 8 ) & 0xff ;
p [ 5 ] = ( ( base > > 8 ) > > 8 ) & 0xff ;
p [ 6 ] = ( base > > 8 ) & 0xff ;
p [ 7 ] = ( ( base > > 8 ) > > 8 ) & 0xff ;
p [ 10 ] = ( len > > 8 ) & 0xff ;
p [ 11 ] = ( ( len > > 8 ) > > 8 ) & 0xff ;
}
2007-07-26 21:41:20 +04:00
static void pnpbios_encode_mem32 ( unsigned char * p , struct resource * res )
2005-04-17 02:20:36 +04:00
{
unsigned long base = res - > start ;
unsigned long len = res - > end - res - > start + 1 ;
2007-07-26 21:41:21 +04:00
2005-04-17 02:20:36 +04:00
p [ 4 ] = base & 0xff ;
p [ 5 ] = ( base > > 8 ) & 0xff ;
p [ 6 ] = ( base > > 16 ) & 0xff ;
p [ 7 ] = ( base > > 24 ) & 0xff ;
p [ 8 ] = base & 0xff ;
p [ 9 ] = ( base > > 8 ) & 0xff ;
p [ 10 ] = ( base > > 16 ) & 0xff ;
p [ 11 ] = ( base > > 24 ) & 0xff ;
p [ 16 ] = len & 0xff ;
p [ 17 ] = ( len > > 8 ) & 0xff ;
p [ 18 ] = ( len > > 16 ) & 0xff ;
p [ 19 ] = ( len > > 24 ) & 0xff ;
}
2007-07-26 21:41:20 +04:00
static void pnpbios_encode_fixed_mem32 ( unsigned char * p , struct resource * res )
{
unsigned long base = res - > start ;
2005-04-17 02:20:36 +04:00
unsigned long len = res - > end - res - > start + 1 ;
2007-07-26 21:41:21 +04:00
2005-04-17 02:20:36 +04:00
p [ 4 ] = base & 0xff ;
p [ 5 ] = ( base > > 8 ) & 0xff ;
p [ 6 ] = ( base > > 16 ) & 0xff ;
p [ 7 ] = ( base > > 24 ) & 0xff ;
p [ 8 ] = len & 0xff ;
p [ 9 ] = ( len > > 8 ) & 0xff ;
p [ 10 ] = ( len > > 16 ) & 0xff ;
p [ 11 ] = ( len > > 24 ) & 0xff ;
}
2007-07-26 21:41:20 +04:00
static void pnpbios_encode_irq ( unsigned char * p , struct resource * res )
2005-04-17 02:20:36 +04:00
{
unsigned long map = 0 ;
2007-07-26 21:41:21 +04:00
2005-04-17 02:20:36 +04:00
map = 1 < < res - > start ;
p [ 1 ] = map & 0xff ;
p [ 2 ] = ( map > > 8 ) & 0xff ;
}
2007-07-26 21:41:20 +04:00
static void pnpbios_encode_dma ( unsigned char * p , struct resource * res )
2005-04-17 02:20:36 +04:00
{
unsigned long map = 0 ;
2007-07-26 21:41:21 +04:00
2005-04-17 02:20:36 +04:00
map = 1 < < res - > start ;
p [ 1 ] = map & 0xff ;
}
2007-07-26 21:41:20 +04:00
static void pnpbios_encode_port ( unsigned char * p , struct resource * res )
2005-04-17 02:20:36 +04:00
{
unsigned long base = res - > start ;
unsigned long len = res - > end - res - > start + 1 ;
2007-07-26 21:41:21 +04:00
2005-04-17 02:20:36 +04:00
p [ 2 ] = base & 0xff ;
p [ 3 ] = ( base > > 8 ) & 0xff ;
p [ 4 ] = base & 0xff ;
p [ 5 ] = ( base > > 8 ) & 0xff ;
p [ 7 ] = len & 0xff ;
}
2007-07-26 21:41:20 +04:00
static void pnpbios_encode_fixed_port ( unsigned char * p , struct resource * res )
2005-04-17 02:20:36 +04:00
{
unsigned long base = res - > start ;
unsigned long len = res - > end - res - > start + 1 ;
2007-07-26 21:41:21 +04:00
2005-04-17 02:20:36 +04:00
p [ 1 ] = base & 0xff ;
p [ 2 ] = ( base > > 8 ) & 0xff ;
p [ 3 ] = len & 0xff ;
}
2007-07-26 21:41:20 +04:00
static unsigned char * pnpbios_encode_allocated_resource_data ( unsigned char * p ,
unsigned char * end ,
struct
pnp_resource_table
* res )
2005-04-17 02:20:36 +04:00
{
unsigned int len , tag ;
int port = 0 , irq = 0 , dma = 0 , mem = 0 ;
if ( ! p )
return NULL ;
while ( ( char * ) p < ( char * ) end ) {
/* determine the type of tag */
2007-07-26 21:41:20 +04:00
if ( p [ 0 ] & LARGE_TAG ) { /* large tag */
2005-04-17 02:20:36 +04:00
len = ( p [ 2 ] < < 8 ) | p [ 1 ] ;
tag = p [ 0 ] ;
2007-07-26 21:41:20 +04:00
} else { /* small tag */
2005-04-17 02:20:36 +04:00
len = p [ 0 ] & 0x07 ;
2007-07-26 21:41:20 +04:00
tag = ( ( p [ 0 ] > > 3 ) & 0x0f ) ;
2005-04-17 02:20:36 +04:00
}
switch ( tag ) {
case LARGE_TAG_MEM :
if ( len ! = 9 )
goto len_err ;
pnpbios_encode_mem ( p , & res - > mem_resource [ mem ] ) ;
mem + + ;
break ;
case LARGE_TAG_MEM32 :
if ( len ! = 17 )
goto len_err ;
pnpbios_encode_mem32 ( p , & res - > mem_resource [ mem ] ) ;
mem + + ;
break ;
case LARGE_TAG_FIXEDMEM32 :
if ( len ! = 9 )
goto len_err ;
pnpbios_encode_fixed_mem32 ( p , & res - > mem_resource [ mem ] ) ;
mem + + ;
break ;
case SMALL_TAG_IRQ :
if ( len < 2 | | len > 3 )
goto len_err ;
pnpbios_encode_irq ( p , & res - > irq_resource [ irq ] ) ;
irq + + ;
break ;
case SMALL_TAG_DMA :
if ( len ! = 2 )
goto len_err ;
pnpbios_encode_dma ( p , & res - > dma_resource [ dma ] ) ;
dma + + ;
break ;
case SMALL_TAG_PORT :
if ( len ! = 7 )
goto len_err ;
pnpbios_encode_port ( p , & res - > port_resource [ port ] ) ;
port + + ;
break ;
case SMALL_TAG_VENDOR :
/* do nothing */
break ;
case SMALL_TAG_FIXEDPORT :
if ( len ! = 3 )
goto len_err ;
pnpbios_encode_fixed_port ( p , & res - > port_resource [ port ] ) ;
port + + ;
break ;
case SMALL_TAG_END :
p = p + 2 ;
2007-07-26 21:41:20 +04:00
return ( unsigned char * ) p ;
2005-04-17 02:20:36 +04:00
break ;
2007-07-26 21:41:20 +04:00
default : /* an unkown tag */
2007-08-15 20:32:08 +04:00
len_err :
2007-07-26 21:41:20 +04:00
printk ( KERN_ERR
" PnPBIOS: Unknown tag '0x%x', length '%d'. \n " ,
tag , len ) ;
2005-04-17 02:20:36 +04:00
break ;
}
/* continue to the next tag */
if ( p [ 0 ] & LARGE_TAG )
p + = len + 3 ;
else
p + = len + 1 ;
}
2007-07-26 21:41:20 +04:00
printk ( KERN_ERR
" PnPBIOS: Resource structure does not contain an end tag. \n " ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
/*
* Core Parsing Functions
*/
2008-02-06 12:40:03 +03:00
int __init pnpbios_parse_data_stream ( struct pnp_dev * dev ,
struct pnp_bios_node * node )
2005-04-17 02:20:36 +04:00
{
2007-07-26 21:41:20 +04:00
unsigned char * p = ( char * ) node - > data ;
unsigned char * end = ( char * ) ( node - > data + node - > size ) ;
2007-07-26 21:41:21 +04:00
2007-07-26 21:41:20 +04:00
p = pnpbios_parse_allocated_resource_data ( p , end , & dev - > res ) ;
2005-04-17 02:20:36 +04:00
if ( ! p )
return - EIO ;
2007-07-26 21:41:20 +04:00
p = pnpbios_parse_resource_option_data ( p , end , dev ) ;
2005-04-17 02:20:36 +04:00
if ( ! p )
return - EIO ;
2007-07-26 21:41:20 +04:00
p = pnpbios_parse_compatible_ids ( p , end , dev ) ;
2005-04-17 02:20:36 +04:00
if ( ! p )
return - EIO ;
return 0 ;
}
2007-07-26 21:41:21 +04:00
int pnpbios_read_resources_from_node ( struct pnp_resource_table * res ,
struct pnp_bios_node * node )
2005-04-17 02:20:36 +04:00
{
2007-07-26 21:41:20 +04:00
unsigned char * p = ( char * ) node - > data ;
unsigned char * end = ( char * ) ( node - > data + node - > size ) ;
2007-07-26 21:41:21 +04:00
2007-07-26 21:41:20 +04:00
p = pnpbios_parse_allocated_resource_data ( p , end , res ) ;
2005-04-17 02:20:36 +04:00
if ( ! p )
return - EIO ;
return 0 ;
}
2007-07-26 21:41:21 +04:00
int pnpbios_write_resources_to_node ( struct pnp_resource_table * res ,
struct pnp_bios_node * node )
2005-04-17 02:20:36 +04:00
{
2007-07-26 21:41:20 +04:00
unsigned char * p = ( char * ) node - > data ;
unsigned char * end = ( char * ) ( node - > data + node - > size ) ;
2007-07-26 21:41:21 +04:00
2007-07-26 21:41:20 +04:00
p = pnpbios_encode_allocated_resource_data ( p , end , res ) ;
2005-04-17 02:20:36 +04:00
if ( ! p )
return - EIO ;
return 0 ;
}