2005-04-17 02:20:36 +04:00
/*
* cistpl . c - - 16 - bit PCMCIA Card Information Structure parser
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* The initial developer of the original code is David A . Hinds
* < dahinds @ users . sourceforge . net > . Portions created by David A . Hinds
* are Copyright ( C ) 1999 David A . Hinds . All Rights Reserved .
*
* ( C ) 1999 David A . Hinds
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/major.h>
# include <linux/errno.h>
# include <linux/timer.h>
# include <linux/slab.h>
# include <linux/mm.h>
# include <linux/pci.h>
# include <linux/ioport.h>
2009-12-08 00:11:45 +03:00
# include <linux/io.h>
2005-04-17 02:20:36 +04:00
# include <asm/byteorder.h>
2007-10-16 12:23:52 +04:00
# include <asm/unaligned.h>
2005-04-17 02:20:36 +04:00
# include <pcmcia/ss.h>
# include <pcmcia/cisreg.h>
# include <pcmcia/cistpl.h>
# include "cs_internal.h"
static const u_char mantissa [ ] = {
10 , 12 , 13 , 15 , 20 , 25 , 30 , 35 ,
40 , 45 , 50 , 55 , 60 , 70 , 80 , 90
} ;
static const u_int exponent [ ] = {
1 , 10 , 100 , 1000 , 10000 , 100000 , 1000000 , 10000000
} ;
/* Convert an extended speed byte to a time in nanoseconds */
# define SPEED_CVT(v) \
( mantissa [ ( ( ( v ) > > 3 ) & 15 ) - 1 ] * exponent [ ( v ) & 7 ] / 10 )
/* Convert a power byte to a current in 0.1 microamps */
# define POWER_CVT(v) \
( mantissa [ ( ( v ) > > 3 ) & 15 ] * exponent [ ( v ) & 7 ] / 10 )
# define POWER_SCALE(v) (exponent[(v)&7])
/* Upper limit on reasonable # of tuples */
# define MAX_TUPLES 200
2010-07-24 19:43:10 +04:00
/* Bits in IRQInfo1 field */
# define IRQ_INFO2_VALID 0x10
2005-09-08 03:00:26 +04:00
/* 16-bit CIS? */
static int cis_width ;
module_param ( cis_width , int , 0444 ) ;
2005-04-17 02:20:36 +04:00
void release_cis_mem ( struct pcmcia_socket * s )
{
2010-03-02 10:57:33 +03:00
mutex_lock ( & s - > ops_mutex ) ;
if ( s - > cis_mem . flags & MAP_ACTIVE ) {
s - > cis_mem . flags & = ~ MAP_ACTIVE ;
s - > ops - > set_mem_map ( s , & s - > cis_mem ) ;
if ( s - > cis_mem . res ) {
release_resource ( s - > cis_mem . res ) ;
kfree ( s - > cis_mem . res ) ;
s - > cis_mem . res = NULL ;
}
iounmap ( s - > cis_virt ) ;
s - > cis_virt = NULL ;
2005-04-17 02:20:36 +04:00
}
2010-03-02 10:57:33 +03:00
mutex_unlock ( & s - > ops_mutex ) ;
2005-04-17 02:20:36 +04:00
}
2010-03-02 10:57:33 +03:00
/**
* set_cis_map ( ) - map the card memory at " card_offset " into virtual space .
*
2005-04-17 02:20:36 +04:00
* If flags & MAP_ATTRIB , map the attribute space , otherwise
* map the memory space .
2010-02-17 20:00:07 +03:00
*
* Must be called with ops_mutex held .
2005-04-17 02:20:36 +04:00
*/
2010-03-02 10:57:33 +03:00
static void __iomem * set_cis_map ( struct pcmcia_socket * s ,
unsigned int card_offset , unsigned int flags )
2005-04-17 02:20:36 +04:00
{
2005-07-28 12:07:23 +04:00
pccard_mem_map * mem = & s - > cis_mem ;
int ret ;
if ( ! ( s - > features & SS_CAP_STATIC_MAP ) & & ( mem - > res = = NULL ) ) {
2010-03-02 10:57:33 +03:00
mem - > res = pcmcia_find_mem_region ( 0 , s - > map_size ,
s - > map_size , 0 , s ) ;
2005-07-28 12:07:23 +04:00
if ( mem - > res = = NULL ) {
2014-10-10 20:12:47 +04:00
dev_notice ( & s - > dev , " cs: unable to map card memory! \n " ) ;
2005-07-28 12:07:23 +04:00
return NULL ;
}
s - > cis_virt = NULL ;
2005-04-17 02:20:36 +04:00
}
2005-06-28 03:28:58 +04:00
2005-07-28 12:07:23 +04:00
if ( ! ( s - > features & SS_CAP_STATIC_MAP ) & & ( ! s - > cis_virt ) )
s - > cis_virt = ioremap ( mem - > res - > start , s - > map_size ) ;
mem - > card_start = card_offset ;
mem - > flags = flags ;
ret = s - > ops - > set_mem_map ( s , mem ) ;
if ( ret ) {
iounmap ( s - > cis_virt ) ;
s - > cis_virt = NULL ;
return NULL ;
}
if ( s - > features & SS_CAP_STATIC_MAP ) {
if ( s - > cis_virt )
iounmap ( s - > cis_virt ) ;
s - > cis_virt = ioremap ( mem - > static_start , s - > map_size ) ;
}
return s - > cis_virt ;
2005-04-17 02:20:36 +04:00
}
/* Bits in attr field */
# define IS_ATTR 1
# define IS_INDIRECT 8
2010-03-02 10:57:33 +03:00
/**
* pcmcia_read_cis_mem ( ) - low - level function to read CIS memory
2010-03-30 20:07:50 +04:00
*
* must be called with ops_mutex held
2010-03-02 10:57:33 +03:00
*/
2005-06-28 03:28:52 +04:00
int pcmcia_read_cis_mem ( struct pcmcia_socket * s , int attr , u_int addr ,
2005-04-17 02:20:36 +04:00
u_int len , void * ptr )
{
2010-03-02 10:57:33 +03:00
void __iomem * sys , * end ;
unsigned char * buf = ptr ;
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
dev_dbg ( & s - > dev , " pcmcia_read_cis_mem(%d, %#x, %u) \n " , attr , addr , len ) ;
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
if ( attr & IS_INDIRECT ) {
/* Indirect accesses use a bunch of special registers at fixed
locations in common memory */
u_char flags = ICTRL0_COMMON | ICTRL0_AUTOINC | ICTRL0_BYTEGRAN ;
if ( attr & IS_ATTR ) {
addr * = 2 ;
flags = ICTRL0_AUTOINC ;
}
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
sys = set_cis_map ( s , 0 , MAP_ACTIVE |
( ( cis_width ) ? MAP_16BIT : 0 ) ) ;
if ( ! sys ) {
dev_dbg ( & s - > dev , " could not map memory \n " ) ;
memset ( ptr , 0xff , len ) ;
return - 1 ;
}
writeb ( flags , sys + CISREG_ICTRL0 ) ;
writeb ( addr & 0xff , sys + CISREG_IADDR0 ) ;
writeb ( ( addr > > 8 ) & 0xff , sys + CISREG_IADDR1 ) ;
writeb ( ( addr > > 16 ) & 0xff , sys + CISREG_IADDR2 ) ;
writeb ( ( addr > > 24 ) & 0xff , sys + CISREG_IADDR3 ) ;
for ( ; len > 0 ; len - - , buf + + )
* buf = readb ( sys + CISREG_IDATA0 ) ;
} else {
u_int inc = 1 , card_offset , flags ;
2014-12-10 18:06:10 +03:00
if ( addr > CISTPL_MAX_CIS_SIZE ) {
2010-03-02 10:57:33 +03:00
dev_dbg ( & s - > dev ,
" attempt to read CIS mem at addr %#x " , addr ) ;
2014-12-10 18:06:10 +03:00
memset ( ptr , 0xff , len ) ;
return - 1 ;
}
2010-03-02 10:57:33 +03:00
flags = MAP_ACTIVE | ( ( cis_width ) ? MAP_16BIT : 0 ) ;
if ( attr ) {
flags | = MAP_ATTRIB ;
inc + + ;
addr * = 2 ;
}
card_offset = addr & ~ ( s - > map_size - 1 ) ;
while ( len ) {
sys = set_cis_map ( s , card_offset , flags ) ;
if ( ! sys ) {
dev_dbg ( & s - > dev , " could not map memory \n " ) ;
memset ( ptr , 0xff , len ) ;
return - 1 ;
}
end = sys + s - > map_size ;
sys = sys + ( addr & ( s - > map_size - 1 ) ) ;
for ( ; len > 0 ; len - - , buf + + , sys + = inc ) {
if ( sys = = end )
break ;
* buf = readb ( sys ) ;
}
card_offset + = s - > map_size ;
addr = 0 ;
}
2005-04-17 02:20:36 +04:00
}
2010-03-02 10:57:33 +03:00
dev_dbg ( & s - > dev , " %#2.2x %#2.2x %#2.2x %#2.2x ... \n " ,
* ( u_char * ) ( ptr + 0 ) , * ( u_char * ) ( ptr + 1 ) ,
* ( u_char * ) ( ptr + 2 ) , * ( u_char * ) ( ptr + 3 ) ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-06-28 03:28:53 +04:00
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
/**
* pcmcia_write_cis_mem ( ) - low - level function to write CIS memory
*
2010-03-30 20:07:50 +04:00
* Probably only useful for writing one - byte registers . Must be called
* with ops_mutex held .
2010-03-02 10:57:33 +03:00
*/
2010-07-24 14:23:21 +04:00
int pcmcia_write_cis_mem ( struct pcmcia_socket * s , int attr , u_int addr ,
2005-04-17 02:20:36 +04:00
u_int len , void * ptr )
{
2010-03-02 10:57:33 +03:00
void __iomem * sys , * end ;
unsigned char * buf = ptr ;
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
dev_dbg ( & s - > dev ,
" pcmcia_write_cis_mem(%d, %#x, %u) \n " , attr , addr , len ) ;
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
if ( attr & IS_INDIRECT ) {
/* Indirect accesses use a bunch of special registers at fixed
locations in common memory */
u_char flags = ICTRL0_COMMON | ICTRL0_AUTOINC | ICTRL0_BYTEGRAN ;
if ( attr & IS_ATTR ) {
addr * = 2 ;
flags = ICTRL0_AUTOINC ;
}
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
sys = set_cis_map ( s , 0 , MAP_ACTIVE |
( ( cis_width ) ? MAP_16BIT : 0 ) ) ;
if ( ! sys ) {
dev_dbg ( & s - > dev , " could not map memory \n " ) ;
2010-07-24 14:23:21 +04:00
return - EINVAL ;
2010-03-02 10:57:33 +03:00
}
2005-06-28 03:28:53 +04:00
2010-03-02 10:57:33 +03:00
writeb ( flags , sys + CISREG_ICTRL0 ) ;
writeb ( addr & 0xff , sys + CISREG_IADDR0 ) ;
writeb ( ( addr > > 8 ) & 0xff , sys + CISREG_IADDR1 ) ;
writeb ( ( addr > > 16 ) & 0xff , sys + CISREG_IADDR2 ) ;
writeb ( ( addr > > 24 ) & 0xff , sys + CISREG_IADDR3 ) ;
for ( ; len > 0 ; len - - , buf + + )
writeb ( * buf , sys + CISREG_IDATA0 ) ;
} else {
u_int inc = 1 , card_offset , flags ;
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
flags = MAP_ACTIVE | ( ( cis_width ) ? MAP_16BIT : 0 ) ;
if ( attr & IS_ATTR ) {
flags | = MAP_ATTRIB ;
inc + + ;
addr * = 2 ;
}
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
card_offset = addr & ~ ( s - > map_size - 1 ) ;
while ( len ) {
sys = set_cis_map ( s , card_offset , flags ) ;
if ( ! sys ) {
dev_dbg ( & s - > dev , " could not map memory \n " ) ;
2010-07-24 14:23:21 +04:00
return - EINVAL ;
2010-03-02 10:57:33 +03:00
}
end = sys + s - > map_size ;
sys = sys + ( addr & ( s - > map_size - 1 ) ) ;
for ( ; len > 0 ; len - - , buf + + , sys + = inc ) {
if ( sys = = end )
break ;
writeb ( * buf , sys ) ;
}
card_offset + = s - > map_size ;
addr = 0 ;
}
}
2010-07-24 14:23:21 +04:00
return 0 ;
2010-03-02 10:57:33 +03:00
}
2009-12-08 00:11:45 +03:00
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
/**
* read_cis_cache ( ) - read CIS memory or its associated cache
*
* This is a wrapper around read_cis_mem , with the same interface ,
* but which caches information , for cards whose CIS may not be
* readable all the time .
*/
2010-02-17 20:01:31 +03:00
static int read_cis_cache ( struct pcmcia_socket * s , int attr , u_int addr ,
size_t len , void * ptr )
2005-04-17 02:20:36 +04:00
{
2010-01-02 19:27:33 +03:00
struct cis_cache_entry * cis ;
2010-02-17 20:01:31 +03:00
int ret = 0 ;
2005-04-17 02:20:36 +04:00
2010-01-02 19:27:33 +03:00
if ( s - > state & SOCKET_CARDBUS )
2010-02-17 20:01:31 +03:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2010-01-13 00:05:36 +03:00
mutex_lock ( & s - > ops_mutex ) ;
2010-01-02 19:27:33 +03:00
if ( s - > fake_cis ) {
if ( s - > fake_cis_len > = addr + len )
memcpy ( ptr , s - > fake_cis + addr , len ) ;
2010-02-17 20:01:31 +03:00
else {
2010-01-02 19:27:33 +03:00
memset ( ptr , 0xff , len ) ;
2010-02-17 20:01:31 +03:00
ret = - EINVAL ;
}
2010-01-13 00:05:36 +03:00
mutex_unlock ( & s - > ops_mutex ) ;
2010-02-17 20:01:31 +03:00
return ret ;
2010-01-02 19:27:33 +03:00
}
list_for_each_entry ( cis , & s - > cis_cache , node ) {
if ( cis - > addr = = addr & & cis - > len = = len & & cis - > attr = = attr ) {
memcpy ( ptr , cis - > cache , len ) ;
2010-01-13 00:05:36 +03:00
mutex_unlock ( & s - > ops_mutex ) ;
2010-02-17 20:01:31 +03:00
return 0 ;
2010-01-02 19:27:33 +03:00
}
2005-04-17 02:20:36 +04:00
}
2005-06-28 03:28:52 +04:00
ret = pcmcia_read_cis_mem ( s , attr , addr , len , ptr ) ;
2005-04-17 02:20:36 +04:00
if ( ret = = 0 ) {
/* Copy data into the cache */
cis = kmalloc ( sizeof ( struct cis_cache_entry ) + len , GFP_KERNEL ) ;
if ( cis ) {
cis - > addr = addr ;
cis - > len = len ;
cis - > attr = attr ;
memcpy ( cis - > cache , ptr , len ) ;
list_add ( & cis - > node , & s - > cis_cache ) ;
}
}
2010-03-30 20:07:50 +04:00
mutex_unlock ( & s - > ops_mutex ) ;
2010-02-17 20:01:31 +03:00
return ret ;
2005-04-17 02:20:36 +04:00
}
static void
remove_cis_cache ( struct pcmcia_socket * s , int attr , u_int addr , u_int len )
{
struct cis_cache_entry * cis ;
2010-01-13 00:05:36 +03:00
mutex_lock ( & s - > ops_mutex ) ;
2005-04-17 02:20:36 +04:00
list_for_each_entry ( cis , & s - > cis_cache , node )
if ( cis - > addr = = addr & & cis - > len = = len & & cis - > attr = = attr ) {
list_del ( & cis - > node ) ;
kfree ( cis ) ;
break ;
}
2010-01-13 00:05:36 +03:00
mutex_unlock ( & s - > ops_mutex ) ;
2005-04-17 02:20:36 +04:00
}
2010-01-02 14:28:04 +03:00
/**
* destroy_cis_cache ( ) - destroy the CIS cache
* @ s : pcmcia_socket for which CIS cache shall be destroyed
*
2010-01-13 00:05:36 +03:00
* This destroys the CIS cache but keeps any fake CIS alive . Must be
* called with ops_mutex held .
2010-01-02 14:28:04 +03:00
*/
2005-04-17 02:20:36 +04:00
void destroy_cis_cache ( struct pcmcia_socket * s )
{
struct list_head * l , * n ;
2010-01-02 14:28:04 +03:00
struct cis_cache_entry * cis ;
2005-04-17 02:20:36 +04:00
list_for_each_safe ( l , n , & s - > cis_cache ) {
2010-01-02 14:28:04 +03:00
cis = list_entry ( l , struct cis_cache_entry , node ) ;
2005-04-17 02:20:36 +04:00
list_del ( & cis - > node ) ;
kfree ( cis ) ;
}
}
2010-03-02 10:57:33 +03:00
/**
* verify_cis_cache ( ) - does the CIS match what is in the CIS cache ?
*/
2005-04-17 02:20:36 +04:00
int verify_cis_cache ( struct pcmcia_socket * s )
{
struct cis_cache_entry * cis ;
char * buf ;
2010-02-17 20:01:31 +03:00
int ret ;
2005-04-17 02:20:36 +04:00
2010-01-02 19:27:33 +03:00
if ( s - > state & SOCKET_CARDBUS )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
buf = kmalloc ( 256 , GFP_KERNEL ) ;
2008-11-02 21:55:45 +03:00
if ( buf = = NULL ) {
2014-10-10 20:12:47 +04:00
dev_warn ( & s - > dev , " no memory for verifying CIS \n " ) ;
2008-08-03 12:22:47 +04:00
return - ENOMEM ;
2008-11-02 21:55:45 +03:00
}
2010-03-30 20:07:50 +04:00
mutex_lock ( & s - > ops_mutex ) ;
2005-04-17 02:20:36 +04:00
list_for_each_entry ( cis , & s - > cis_cache , node ) {
int len = cis - > len ;
if ( len > 256 )
len = 256 ;
2010-01-02 19:27:33 +03:00
2010-02-17 20:01:31 +03:00
ret = pcmcia_read_cis_mem ( s , cis - > attr , cis - > addr , len , buf ) ;
if ( ret | | memcmp ( buf , cis - > cache , len ) ! = 0 ) {
2005-04-17 02:20:36 +04:00
kfree ( buf ) ;
2010-03-30 20:07:50 +04:00
mutex_unlock ( & s - > ops_mutex ) ;
2005-04-17 02:20:36 +04:00
return - 1 ;
}
}
kfree ( buf ) ;
2010-03-30 20:07:50 +04:00
mutex_unlock ( & s - > ops_mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2010-03-02 10:57:33 +03:00
/**
* pcmcia_replace_cis ( ) - use a replacement CIS instead of the card ' s CIS
*
* For really bad cards , we provide a facility for uploading a
* replacement CIS .
*/
2008-07-28 21:44:05 +04:00
int pcmcia_replace_cis ( struct pcmcia_socket * s ,
const u8 * data , const size_t len )
2005-04-17 02:20:36 +04:00
{
2008-08-03 12:22:47 +04:00
if ( len > CISTPL_MAX_CIS_SIZE ) {
2014-10-10 20:12:47 +04:00
dev_warn ( & s - > dev , " replacement CIS too big \n " ) ;
2008-08-03 12:22:47 +04:00
return - EINVAL ;
}
2010-01-13 00:05:36 +03:00
mutex_lock ( & s - > ops_mutex ) ;
2008-08-03 12:22:47 +04:00
kfree ( s - > fake_cis ) ;
s - > fake_cis = kmalloc ( len , GFP_KERNEL ) ;
if ( s - > fake_cis = = NULL ) {
2014-10-10 20:12:47 +04:00
dev_warn ( & s - > dev , " no memory to replace CIS \n " ) ;
2010-01-13 00:05:36 +03:00
mutex_unlock ( & s - > ops_mutex ) ;
2008-08-03 12:22:47 +04:00
return - ENOMEM ;
}
s - > fake_cis_len = len ;
memcpy ( s - > fake_cis , data , len ) ;
2010-02-17 20:01:31 +03:00
dev_info ( & s - > dev , " Using replacement CIS \n " ) ;
2010-01-13 00:05:36 +03:00
mutex_unlock ( & s - > ops_mutex ) ;
2008-08-03 12:22:47 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2010-03-02 10:57:33 +03:00
/* The high-level CIS tuple services */
2005-04-17 02:20:36 +04:00
2014-08-06 16:29:48 +04:00
struct tuple_flags {
2010-03-02 10:57:33 +03:00
u_int link_space : 4 ;
u_int has_link : 1 ;
u_int mfc_fn : 3 ;
u_int space : 4 ;
2014-08-06 16:29:48 +04:00
} ;
2005-04-17 02:20:36 +04:00
2014-08-06 16:29:48 +04:00
# define LINK_SPACE(f) (((struct tuple_flags *)(&(f)))->link_space)
# define HAS_LINK(f) (((struct tuple_flags *)(&(f)))->has_link)
# define MFC_FN(f) (((struct tuple_flags *)(&(f)))->mfc_fn)
# define SPACE(f) (((struct tuple_flags *)(&(f)))->space)
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
int pccard_get_first_tuple ( struct pcmcia_socket * s , unsigned int function ,
tuple_t * tuple )
2005-04-17 02:20:36 +04:00
{
2010-03-02 10:57:33 +03:00
if ( ! s )
return - EINVAL ;
if ( ! ( s - > state & SOCKET_PRESENT ) | | ( s - > state & SOCKET_CARDBUS ) )
return - ENODEV ;
tuple - > TupleLink = tuple - > Flags = 0 ;
/* Assume presence of a LONGLINK_C to address 0 */
tuple - > CISOffset = tuple - > LinkOffset = 0 ;
SPACE ( tuple - > Flags ) = HAS_LINK ( tuple - > Flags ) = 1 ;
if ( ( s - > functions > 1 ) & & ! ( tuple - > Attributes & TUPLE_RETURN_COMMON ) ) {
cisdata_t req = tuple - > DesiredTuple ;
tuple - > DesiredTuple = CISTPL_LONGLINK_MFC ;
if ( pccard_get_next_tuple ( s , function , tuple ) = = 0 ) {
tuple - > DesiredTuple = CISTPL_LINKTARGET ;
if ( pccard_get_next_tuple ( s , function , tuple ) ! = 0 )
return - ENOSPC ;
} else
tuple - > CISOffset = tuple - > TupleLink = 0 ;
tuple - > DesiredTuple = req ;
}
return pccard_get_next_tuple ( s , function , tuple ) ;
2005-04-17 02:20:36 +04:00
}
static int follow_link ( struct pcmcia_socket * s , tuple_t * tuple )
{
2010-03-02 10:57:33 +03:00
u_char link [ 5 ] ;
u_int ofs ;
int ret ;
if ( MFC_FN ( tuple - > Flags ) ) {
/* Get indirect link from the MFC tuple */
ret = read_cis_cache ( s , LINK_SPACE ( tuple - > Flags ) ,
tuple - > LinkOffset , 5 , link ) ;
if ( ret )
return - 1 ;
ofs = get_unaligned_le32 ( link + 1 ) ;
SPACE ( tuple - > Flags ) = ( link [ 0 ] = = CISTPL_MFC_ATTR ) ;
/* Move to the next indirect link */
tuple - > LinkOffset + = 5 ;
MFC_FN ( tuple - > Flags ) - - ;
} else if ( HAS_LINK ( tuple - > Flags ) ) {
ofs = tuple - > LinkOffset ;
SPACE ( tuple - > Flags ) = LINK_SPACE ( tuple - > Flags ) ;
HAS_LINK ( tuple - > Flags ) = 0 ;
} else
2010-02-17 20:01:31 +03:00
return - 1 ;
2010-03-02 10:57:33 +03:00
if ( SPACE ( tuple - > Flags ) ) {
/* This is ugly, but a common CIS error is to code the long
link offset incorrectly , so we check the right spot . . . */
ret = read_cis_cache ( s , SPACE ( tuple - > Flags ) , ofs , 5 , link ) ;
if ( ret )
return - 1 ;
if ( ( link [ 0 ] = = CISTPL_LINKTARGET ) & & ( link [ 1 ] > = 3 ) & &
( strncmp ( link + 2 , " CIS " , 3 ) = = 0 ) )
return ofs ;
remove_cis_cache ( s , SPACE ( tuple - > Flags ) , ofs , 5 ) ;
/* Then, we try the wrong spot... */
ofs = ofs > > 1 ;
}
2010-02-17 20:01:31 +03:00
ret = read_cis_cache ( s , SPACE ( tuple - > Flags ) , ofs , 5 , link ) ;
if ( ret )
return - 1 ;
2005-04-17 02:20:36 +04:00
if ( ( link [ 0 ] = = CISTPL_LINKTARGET ) & & ( link [ 1 ] > = 3 ) & &
2010-03-02 10:57:33 +03:00
( strncmp ( link + 2 , " CIS " , 3 ) = = 0 ) )
return ofs ;
2005-04-17 02:20:36 +04:00
remove_cis_cache ( s , SPACE ( tuple - > Flags ) , ofs , 5 ) ;
2010-03-02 10:57:33 +03:00
return - 1 ;
2005-04-17 02:20:36 +04:00
}
2010-03-02 10:57:33 +03:00
int pccard_get_next_tuple ( struct pcmcia_socket * s , unsigned int function ,
tuple_t * tuple )
2005-04-17 02:20:36 +04:00
{
2010-03-02 10:57:33 +03:00
u_char link [ 2 ] , tmp ;
int ofs , i , attr ;
int ret ;
2009-12-08 00:11:45 +03:00
2010-03-02 10:57:33 +03:00
if ( ! s )
return - EINVAL ;
if ( ! ( s - > state & SOCKET_PRESENT ) | | ( s - > state & SOCKET_CARDBUS ) )
return - ENODEV ;
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
link [ 1 ] = tuple - > TupleLink ;
ofs = tuple - > CISOffset + tuple - > TupleLink ;
attr = SPACE ( tuple - > Flags ) ;
for ( i = 0 ; i < MAX_TUPLES ; i + + ) {
if ( link [ 1 ] = = 0xff )
link [ 0 ] = CISTPL_END ;
else {
ret = read_cis_cache ( s , attr , ofs , 2 , link ) ;
if ( ret )
return - 1 ;
if ( link [ 0 ] = = CISTPL_NULL ) {
ofs + + ;
continue ;
}
2005-04-17 02:20:36 +04:00
}
2009-12-08 00:11:45 +03:00
2010-03-02 10:57:33 +03:00
/* End of chain? Follow long link if possible */
if ( link [ 0 ] = = CISTPL_END ) {
ofs = follow_link ( s , tuple ) ;
if ( ofs < 0 )
return - ENOSPC ;
attr = SPACE ( tuple - > Flags ) ;
ret = read_cis_cache ( s , attr , ofs , 2 , link ) ;
if ( ret )
return - 1 ;
}
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
/* Is this a link tuple? Make a note of it */
if ( ( link [ 0 ] = = CISTPL_LONGLINK_A ) | |
( link [ 0 ] = = CISTPL_LONGLINK_C ) | |
( link [ 0 ] = = CISTPL_LONGLINK_MFC ) | |
( link [ 0 ] = = CISTPL_LINKTARGET ) | |
( link [ 0 ] = = CISTPL_INDIRECT ) | |
( link [ 0 ] = = CISTPL_NO_LINK ) ) {
switch ( link [ 0 ] ) {
case CISTPL_LONGLINK_A :
HAS_LINK ( tuple - > Flags ) = 1 ;
LINK_SPACE ( tuple - > Flags ) = attr | IS_ATTR ;
ret = read_cis_cache ( s , attr , ofs + 2 , 4 ,
& tuple - > LinkOffset ) ;
if ( ret )
return - 1 ;
break ;
case CISTPL_LONGLINK_C :
HAS_LINK ( tuple - > Flags ) = 1 ;
LINK_SPACE ( tuple - > Flags ) = attr & ~ IS_ATTR ;
ret = read_cis_cache ( s , attr , ofs + 2 , 4 ,
& tuple - > LinkOffset ) ;
if ( ret )
return - 1 ;
break ;
case CISTPL_INDIRECT :
HAS_LINK ( tuple - > Flags ) = 1 ;
LINK_SPACE ( tuple - > Flags ) = IS_ATTR |
IS_INDIRECT ;
tuple - > LinkOffset = 0 ;
break ;
case CISTPL_LONGLINK_MFC :
tuple - > LinkOffset = ofs + 3 ;
LINK_SPACE ( tuple - > Flags ) = attr ;
if ( function = = BIND_FN_ALL ) {
/* Follow all the MFC links */
ret = read_cis_cache ( s , attr , ofs + 2 ,
1 , & tmp ) ;
if ( ret )
return - 1 ;
MFC_FN ( tuple - > Flags ) = tmp ;
} else {
/* Follow exactly one of the links */
MFC_FN ( tuple - > Flags ) = 1 ;
tuple - > LinkOffset + = function * 5 ;
}
break ;
case CISTPL_NO_LINK :
HAS_LINK ( tuple - > Flags ) = 0 ;
break ;
}
if ( ( tuple - > Attributes & TUPLE_RETURN_LINK ) & &
( tuple - > DesiredTuple = = RETURN_FIRST_TUPLE ) )
break ;
} else
if ( tuple - > DesiredTuple = = RETURN_FIRST_TUPLE )
break ;
if ( link [ 0 ] = = tuple - > DesiredTuple )
break ;
ofs + = link [ 1 ] + 2 ;
}
if ( i = = MAX_TUPLES ) {
dev_dbg ( & s - > dev , " cs: overrun in pcmcia_get_next_tuple \n " ) ;
return - ENOSPC ;
}
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
tuple - > TupleCode = link [ 0 ] ;
tuple - > TupleLink = link [ 1 ] ;
tuple - > CISOffset = ofs + 2 ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
int pccard_get_tuple_data ( struct pcmcia_socket * s , tuple_t * tuple )
{
2010-03-02 10:57:33 +03:00
u_int len ;
int ret ;
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
if ( ! s )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
if ( tuple - > TupleLink < tuple - > TupleOffset )
return - ENOSPC ;
len = tuple - > TupleLink - tuple - > TupleOffset ;
tuple - > TupleDataLen = tuple - > TupleLink ;
if ( len = = 0 )
return 0 ;
ret = read_cis_cache ( s , SPACE ( tuple - > Flags ) ,
tuple - > CISOffset + tuple - > TupleOffset ,
min ( len , ( u_int ) tuple - > TupleDataMax ) ,
tuple - > TupleData ) ;
if ( ret )
return - 1 ;
2008-08-03 12:07:45 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2010-03-02 10:57:33 +03:00
/* Parsing routines for individual tuples */
2005-04-17 02:20:36 +04:00
static int parse_device ( tuple_t * tuple , cistpl_device_t * device )
{
2010-03-02 10:57:33 +03:00
int i ;
u_char scale ;
u_char * p , * q ;
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
p = ( u_char * ) tuple - > TupleData ;
q = p + tuple - > TupleDataLen ;
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
device - > ndev = 0 ;
for ( i = 0 ; i < CISTPL_MAX_DEVICES ; i + + ) {
2009-12-08 00:11:45 +03:00
2010-03-02 10:57:33 +03:00
if ( * p = = 0xff )
break ;
device - > dev [ i ] . type = ( * p > > 4 ) ;
device - > dev [ i ] . wp = ( * p & 0x08 ) ? 1 : 0 ;
switch ( * p & 0x07 ) {
case 0 :
device - > dev [ i ] . speed = 0 ;
break ;
case 1 :
device - > dev [ i ] . speed = 250 ;
break ;
case 2 :
device - > dev [ i ] . speed = 200 ;
break ;
case 3 :
device - > dev [ i ] . speed = 150 ;
break ;
case 4 :
device - > dev [ i ] . speed = 100 ;
break ;
case 7 :
2009-12-08 00:11:45 +03:00
if ( + + p = = q )
return - EINVAL ;
2010-03-02 10:57:33 +03:00
device - > dev [ i ] . speed = SPEED_CVT ( * p ) ;
while ( * p & 0x80 )
if ( + + p = = q )
return - EINVAL ;
break ;
default :
return - EINVAL ;
}
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
if ( + + p = = q )
return - EINVAL ;
if ( * p = = 0xff )
break ;
scale = * p & 7 ;
if ( scale = = 7 )
return - EINVAL ;
device - > dev [ i ] . size = ( ( * p > > 3 ) + 1 ) * ( 512 < < ( scale * 2 ) ) ;
device - > ndev + + ;
if ( + + p = = q )
break ;
}
2009-12-08 00:11:45 +03:00
2010-03-02 10:57:33 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int parse_checksum ( tuple_t * tuple , cistpl_checksum_t * csum )
{
2010-03-02 10:57:33 +03:00
u_char * p ;
if ( tuple - > TupleDataLen < 5 )
return - EINVAL ;
p = ( u_char * ) tuple - > TupleData ;
csum - > addr = tuple - > CISOffset + get_unaligned_le16 ( p ) - 2 ;
csum - > len = get_unaligned_le16 ( p + 2 ) ;
csum - > sum = * ( p + 4 ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int parse_longlink ( tuple_t * tuple , cistpl_longlink_t * link )
{
2010-03-02 10:57:33 +03:00
if ( tuple - > TupleDataLen < 4 )
return - EINVAL ;
link - > addr = get_unaligned_le32 ( tuple - > TupleData ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2010-03-02 10:57:33 +03:00
static int parse_longlink_mfc ( tuple_t * tuple , cistpl_longlink_mfc_t * link )
2005-04-17 02:20:36 +04:00
{
2010-03-02 10:57:33 +03:00
u_char * p ;
int i ;
p = ( u_char * ) tuple - > TupleData ;
link - > nfn = * p ; p + + ;
if ( tuple - > TupleDataLen < = link - > nfn * 5 )
return - EINVAL ;
for ( i = 0 ; i < link - > nfn ; i + + ) {
link - > fn [ i ] . space = * p ; p + + ;
link - > fn [ i ] . addr = get_unaligned_le32 ( p ) ;
p + = 4 ;
}
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int parse_strings ( u_char * p , u_char * q , int max ,
char * s , u_char * ofs , u_char * found )
{
2010-03-02 10:57:33 +03:00
int i , j , ns ;
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
if ( p = = q )
return - EINVAL ;
ns = 0 ; j = 0 ;
for ( i = 0 ; i < max ; i + + ) {
if ( * p = = 0xff )
break ;
ofs [ i ] = j ;
ns + + ;
for ( ; ; ) {
s [ j + + ] = ( * p = = 0xff ) ? ' \0 ' : * p ;
if ( ( * p = = ' \0 ' ) | | ( * p = = 0xff ) )
break ;
if ( + + p = = q )
return - EINVAL ;
}
if ( ( * p = = 0xff ) | | ( + + p = = q ) )
break ;
2005-04-17 02:20:36 +04:00
}
2010-03-02 10:57:33 +03:00
if ( found ) {
* found = ns ;
return 0 ;
}
2008-08-03 14:22:40 +04:00
return ( ns = = max ) ? 0 : - EINVAL ;
2005-04-17 02:20:36 +04:00
}
static int parse_vers_1 ( tuple_t * tuple , cistpl_vers_1_t * vers_1 )
{
2010-03-02 10:57:33 +03:00
u_char * p , * q ;
2009-12-08 00:11:45 +03:00
2010-03-02 10:57:33 +03:00
p = ( u_char * ) tuple - > TupleData ;
q = p + tuple - > TupleDataLen ;
2009-12-08 00:11:45 +03:00
2010-03-02 10:57:33 +03:00
vers_1 - > major = * p ; p + + ;
vers_1 - > minor = * p ; p + + ;
if ( p > = q )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
return parse_strings ( p , q , CISTPL_VERS_1_MAX_PROD_STRINGS ,
vers_1 - > str , vers_1 - > ofs , & vers_1 - > ns ) ;
2005-04-17 02:20:36 +04:00
}
static int parse_altstr ( tuple_t * tuple , cistpl_altstr_t * altstr )
{
2010-03-02 10:57:33 +03:00
u_char * p , * q ;
2009-12-08 00:11:45 +03:00
2010-03-02 10:57:33 +03:00
p = ( u_char * ) tuple - > TupleData ;
q = p + tuple - > TupleDataLen ;
2009-12-08 00:11:45 +03:00
2010-03-02 10:57:33 +03:00
return parse_strings ( p , q , CISTPL_MAX_ALTSTR_STRINGS ,
altstr - > str , altstr - > ofs , & altstr - > ns ) ;
2005-04-17 02:20:36 +04:00
}
static int parse_jedec ( tuple_t * tuple , cistpl_jedec_t * jedec )
{
2010-03-02 10:57:33 +03:00
u_char * p , * q ;
int nid ;
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
p = ( u_char * ) tuple - > TupleData ;
q = p + tuple - > TupleDataLen ;
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
for ( nid = 0 ; nid < CISTPL_MAX_DEVICES ; nid + + ) {
if ( p > q - 2 )
break ;
jedec - > id [ nid ] . mfr = p [ 0 ] ;
jedec - > id [ nid ] . info = p [ 1 ] ;
p + = 2 ;
}
jedec - > nid = nid ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int parse_manfid ( tuple_t * tuple , cistpl_manfid_t * m )
{
2010-03-02 10:57:33 +03:00
if ( tuple - > TupleDataLen < 4 )
return - EINVAL ;
m - > manf = get_unaligned_le16 ( tuple - > TupleData ) ;
m - > card = get_unaligned_le16 ( tuple - > TupleData + 2 ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int parse_funcid ( tuple_t * tuple , cistpl_funcid_t * f )
{
2010-03-02 10:57:33 +03:00
u_char * p ;
if ( tuple - > TupleDataLen < 2 )
return - EINVAL ;
p = ( u_char * ) tuple - > TupleData ;
f - > func = p [ 0 ] ;
f - > sysinit = p [ 1 ] ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int parse_funce ( tuple_t * tuple , cistpl_funce_t * f )
{
2010-03-02 10:57:33 +03:00
u_char * p ;
int i ;
if ( tuple - > TupleDataLen < 1 )
return - EINVAL ;
p = ( u_char * ) tuple - > TupleData ;
f - > type = p [ 0 ] ;
for ( i = 1 ; i < tuple - > TupleDataLen ; i + + )
f - > data [ i - 1 ] = p [ i ] ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int parse_config ( tuple_t * tuple , cistpl_config_t * config )
{
2010-03-02 10:57:33 +03:00
int rasz , rmsz , i ;
u_char * p ;
p = ( u_char * ) tuple - > TupleData ;
rasz = * p & 0x03 ;
rmsz = ( * p & 0x3c ) > > 2 ;
if ( tuple - > TupleDataLen < rasz + rmsz + 4 )
return - EINVAL ;
config - > last_idx = * ( + + p ) ;
p + + ;
config - > base = 0 ;
for ( i = 0 ; i < = rasz ; i + + )
config - > base + = p [ i ] < < ( 8 * i ) ;
p + = rasz + 1 ;
for ( i = 0 ; i < 4 ; i + + )
config - > rmask [ i ] = 0 ;
for ( i = 0 ; i < = rmsz ; i + + )
config - > rmask [ i > > 2 ] + = p [ i ] < < ( 8 * ( i % 4 ) ) ;
config - > subtuples = tuple - > TupleDataLen - ( rasz + rmsz + 4 ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2010-03-02 10:57:33 +03:00
/* The following routines are all used to parse the nightmarish
* config table entries .
*/
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
static u_char * parse_power ( u_char * p , u_char * q , cistpl_power_t * pwr )
{
int i ;
u_int scale ;
if ( p = = q )
return NULL ;
pwr - > present = * p ;
pwr - > flags = 0 ;
p + + ;
for ( i = 0 ; i < 7 ; i + + )
if ( pwr - > present & ( 1 < < i ) ) {
if ( p = = q )
return NULL ;
pwr - > param [ i ] = POWER_CVT ( * p ) ;
scale = POWER_SCALE ( * p ) ;
while ( * p & 0x80 ) {
if ( + + p = = q )
return NULL ;
if ( ( * p & 0x7f ) < 100 )
pwr - > param [ i ] + =
( * p & 0x7f ) * scale / 100 ;
else if ( * p = = 0x7d )
pwr - > flags | = CISTPL_POWER_HIGHZ_OK ;
else if ( * p = = 0x7e )
pwr - > param [ i ] = 0 ;
else if ( * p = = 0x7f )
pwr - > flags | = CISTPL_POWER_HIGHZ_REQ ;
else
return NULL ;
}
p + + ;
}
return p ;
}
2009-12-08 00:11:45 +03:00
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
static u_char * parse_timing ( u_char * p , u_char * q , cistpl_timing_t * timing )
2005-04-17 02:20:36 +04:00
{
2010-03-02 10:57:33 +03:00
u_char scale ;
if ( p = = q )
return NULL ;
scale = * p ;
if ( ( scale & 3 ) ! = 3 ) {
2009-12-08 00:11:45 +03:00
if ( + + p = = q )
return NULL ;
2010-03-02 10:57:33 +03:00
timing - > wait = SPEED_CVT ( * p ) ;
timing - > waitscale = exponent [ scale & 3 ] ;
} else
timing - > wait = 0 ;
scale > > = 2 ;
if ( ( scale & 7 ) ! = 7 ) {
if ( + + p = = q )
return NULL ;
timing - > ready = SPEED_CVT ( * p ) ;
timing - > rdyscale = exponent [ scale & 7 ] ;
} else
timing - > ready = 0 ;
scale > > = 3 ;
if ( scale ! = 7 ) {
if ( + + p = = q )
return NULL ;
timing - > reserved = SPEED_CVT ( * p ) ;
timing - > rsvscale = exponent [ scale ] ;
} else
timing - > reserved = 0 ;
p + + ;
return p ;
2005-04-17 02:20:36 +04:00
}
2010-03-02 10:57:33 +03:00
static u_char * parse_io ( u_char * p , u_char * q , cistpl_io_t * io )
2005-04-17 02:20:36 +04:00
{
2010-03-02 10:57:33 +03:00
int i , j , bsz , lsz ;
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
if ( p = = q )
2009-12-08 00:11:45 +03:00
return NULL ;
2010-03-02 10:57:33 +03:00
io - > flags = * p ;
if ( ! ( * p & 0x80 ) ) {
io - > nwin = 1 ;
io - > win [ 0 ] . base = 0 ;
io - > win [ 0 ] . len = ( 1 < < ( io - > flags & CISTPL_IO_LINES_MASK ) ) ;
return p + 1 ;
}
2009-12-08 00:11:45 +03:00
if ( + + p = = q )
return NULL ;
2010-03-02 10:57:33 +03:00
io - > nwin = ( * p & 0x0f ) + 1 ;
bsz = ( * p & 0x30 ) > > 4 ;
if ( bsz = = 3 )
bsz + + ;
lsz = ( * p & 0xc0 ) > > 6 ;
if ( lsz = = 3 )
lsz + + ;
p + + ;
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
for ( i = 0 ; i < io - > nwin ; i + + ) {
io - > win [ i ] . base = 0 ;
io - > win [ i ] . len = 1 ;
for ( j = 0 ; j < bsz ; j + + , p + + ) {
if ( p = = q )
return NULL ;
io - > win [ i ] . base + = * p < < ( j * 8 ) ;
}
for ( j = 0 ; j < lsz ; j + + , p + + ) {
if ( p = = q )
return NULL ;
io - > win [ i ] . len + = * p < < ( j * 8 ) ;
}
2005-04-17 02:20:36 +04:00
}
2010-03-02 10:57:33 +03:00
return p ;
2005-04-17 02:20:36 +04:00
}
static u_char * parse_mem ( u_char * p , u_char * q , cistpl_mem_t * mem )
{
2010-03-02 10:57:33 +03:00
int i , j , asz , lsz , has_ha ;
u_int len , ca , ha ;
if ( p = = q )
return NULL ;
mem - > nwin = ( * p & 0x07 ) + 1 ;
lsz = ( * p & 0x18 ) > > 3 ;
asz = ( * p & 0x60 ) > > 5 ;
has_ha = ( * p & 0x80 ) ;
if ( + + p = = q )
return NULL ;
for ( i = 0 ; i < mem - > nwin ; i + + ) {
len = ca = ha = 0 ;
for ( j = 0 ; j < lsz ; j + + , p + + ) {
if ( p = = q )
return NULL ;
len + = * p < < ( j * 8 ) ;
}
for ( j = 0 ; j < asz ; j + + , p + + ) {
if ( p = = q )
return NULL ;
ca + = * p < < ( j * 8 ) ;
}
if ( has_ha )
for ( j = 0 ; j < asz ; j + + , p + + ) {
if ( p = = q )
return NULL ;
ha + = * p < < ( j * 8 ) ;
}
mem - > win [ i ] . len = len < < 8 ;
mem - > win [ i ] . card_addr = ca < < 8 ;
mem - > win [ i ] . host_addr = ha < < 8 ;
2005-04-17 02:20:36 +04:00
}
2010-03-02 10:57:33 +03:00
return p ;
2005-04-17 02:20:36 +04:00
}
static u_char * parse_irq ( u_char * p , u_char * q , cistpl_irq_t * irq )
{
2010-03-02 10:57:33 +03:00
if ( p = = q )
2008-08-03 14:22:40 +04:00
return NULL ;
2010-03-02 10:57:33 +03:00
irq - > IRQInfo1 = * p ; p + + ;
if ( irq - > IRQInfo1 & IRQ_INFO2_VALID ) {
if ( p + 2 > q )
return NULL ;
irq - > IRQInfo2 = ( p [ 1 ] < < 8 ) + p [ 0 ] ;
p + = 2 ;
}
return p ;
2005-04-17 02:20:36 +04:00
}
static int parse_cftable_entry ( tuple_t * tuple ,
cistpl_cftable_entry_t * entry )
{
2010-03-02 10:57:33 +03:00
u_char * p , * q , features ;
p = tuple - > TupleData ;
q = p + tuple - > TupleDataLen ;
entry - > index = * p & 0x3f ;
entry - > flags = 0 ;
2005-04-17 02:20:36 +04:00
if ( * p & 0x40 )
2010-03-02 10:57:33 +03:00
entry - > flags | = CISTPL_CFTABLE_DEFAULT ;
if ( * p & 0x80 ) {
if ( + + p = = q )
return - EINVAL ;
if ( * p & 0x10 )
entry - > flags | = CISTPL_CFTABLE_BVDS ;
if ( * p & 0x20 )
entry - > flags | = CISTPL_CFTABLE_WP ;
if ( * p & 0x40 )
entry - > flags | = CISTPL_CFTABLE_RDYBSY ;
if ( * p & 0x80 )
entry - > flags | = CISTPL_CFTABLE_MWAIT ;
entry - > interface = * p & 0x0f ;
} else
entry - > interface = 0 ;
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
/* Process optional features */
if ( + + p = = q )
2008-08-03 14:22:40 +04:00
return - EINVAL ;
2010-03-02 10:57:33 +03:00
features = * p ; p + + ;
2009-12-08 00:11:45 +03:00
2010-03-02 10:57:33 +03:00
/* Power options */
if ( ( features & 3 ) > 0 ) {
p = parse_power ( p , q , & entry - > vcc ) ;
if ( p = = NULL )
return - EINVAL ;
} else
entry - > vcc . present = 0 ;
if ( ( features & 3 ) > 1 ) {
p = parse_power ( p , q , & entry - > vpp1 ) ;
if ( p = = NULL )
return - EINVAL ;
} else
entry - > vpp1 . present = 0 ;
if ( ( features & 3 ) > 2 ) {
p = parse_power ( p , q , & entry - > vpp2 ) ;
if ( p = = NULL )
return - EINVAL ;
} else
entry - > vpp2 . present = 0 ;
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
/* Timing options */
if ( features & 0x04 ) {
p = parse_timing ( p , q , & entry - > timing ) ;
if ( p = = NULL )
return - EINVAL ;
} else {
entry - > timing . wait = 0 ;
entry - > timing . ready = 0 ;
entry - > timing . reserved = 0 ;
}
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
/* I/O window options */
if ( features & 0x08 ) {
p = parse_io ( p , q , & entry - > io ) ;
if ( p = = NULL )
return - EINVAL ;
} else
entry - > io . nwin = 0 ;
/* Interrupt options */
if ( features & 0x10 ) {
p = parse_irq ( p , q , & entry - > irq ) ;
if ( p = = NULL )
return - EINVAL ;
} else
entry - > irq . IRQInfo1 = 0 ;
2009-12-08 00:11:45 +03:00
2010-03-02 10:57:33 +03:00
switch ( features & 0x60 ) {
case 0x00 :
entry - > mem . nwin = 0 ;
break ;
case 0x20 :
entry - > mem . nwin = 1 ;
entry - > mem . win [ 0 ] . len = get_unaligned_le16 ( p ) < < 8 ;
entry - > mem . win [ 0 ] . card_addr = 0 ;
entry - > mem . win [ 0 ] . host_addr = 0 ;
p + = 2 ;
if ( p > q )
return - EINVAL ;
break ;
case 0x40 :
entry - > mem . nwin = 1 ;
entry - > mem . win [ 0 ] . len = get_unaligned_le16 ( p ) < < 8 ;
entry - > mem . win [ 0 ] . card_addr = get_unaligned_le16 ( p + 2 ) < < 8 ;
entry - > mem . win [ 0 ] . host_addr = 0 ;
p + = 4 ;
if ( p > q )
return - EINVAL ;
break ;
case 0x60 :
p = parse_mem ( p , q , & entry - > mem ) ;
if ( p = = NULL )
return - EINVAL ;
break ;
}
/* Misc features */
if ( features & 0x80 ) {
if ( p = = q )
return - EINVAL ;
entry - > flags | = ( * p < < 8 ) ;
while ( * p & 0x80 )
if ( + + p = = q )
return - EINVAL ;
p + + ;
}
entry - > subtuples = q - p ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int parse_device_geo ( tuple_t * tuple , cistpl_device_geo_t * geo )
{
2010-03-02 10:57:33 +03:00
u_char * p , * q ;
int n ;
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
p = ( u_char * ) tuple - > TupleData ;
q = p + tuple - > TupleDataLen ;
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
for ( n = 0 ; n < CISTPL_MAX_DEVICES ; n + + ) {
if ( p > q - 6 )
break ;
geo - > geo [ n ] . buswidth = p [ 0 ] ;
geo - > geo [ n ] . erase_block = 1 < < ( p [ 1 ] - 1 ) ;
geo - > geo [ n ] . read_block = 1 < < ( p [ 2 ] - 1 ) ;
geo - > geo [ n ] . write_block = 1 < < ( p [ 3 ] - 1 ) ;
geo - > geo [ n ] . partition = 1 < < ( p [ 4 ] - 1 ) ;
geo - > geo [ n ] . interleave = 1 < < ( p [ 5 ] - 1 ) ;
p + = 6 ;
}
geo - > ngeo = n ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int parse_vers_2 ( tuple_t * tuple , cistpl_vers_2_t * v2 )
{
2010-03-02 10:57:33 +03:00
u_char * p , * q ;
if ( tuple - > TupleDataLen < 10 )
return - EINVAL ;
p = tuple - > TupleData ;
q = p + tuple - > TupleDataLen ;
v2 - > vers = p [ 0 ] ;
v2 - > comply = p [ 1 ] ;
v2 - > dindex = get_unaligned_le16 ( p + 2 ) ;
v2 - > vspec8 = p [ 6 ] ;
v2 - > vspec9 = p [ 7 ] ;
v2 - > nhdr = p [ 8 ] ;
p + = 9 ;
return parse_strings ( p , q , 2 , v2 - > str , & v2 - > vendor , NULL ) ;
2005-04-17 02:20:36 +04:00
}
static int parse_org ( tuple_t * tuple , cistpl_org_t * org )
{
2010-03-02 10:57:33 +03:00
u_char * p , * q ;
int i ;
p = tuple - > TupleData ;
q = p + tuple - > TupleDataLen ;
if ( p = = q )
return - EINVAL ;
org - > data_org = * p ;
2008-08-03 14:22:40 +04:00
if ( + + p = = q )
return - EINVAL ;
2010-03-02 10:57:33 +03:00
for ( i = 0 ; i < 30 ; i + + ) {
org - > desc [ i ] = * p ;
if ( * p = = ' \0 ' )
break ;
if ( + + p = = q )
return - EINVAL ;
}
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int parse_format ( tuple_t * tuple , cistpl_format_t * fmt )
{
2010-03-02 10:57:33 +03:00
u_char * p ;
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
if ( tuple - > TupleDataLen < 10 )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
p = tuple - > TupleData ;
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
fmt - > type = p [ 0 ] ;
fmt - > edc = p [ 1 ] ;
fmt - > offset = get_unaligned_le32 ( p + 2 ) ;
fmt - > length = get_unaligned_le32 ( p + 6 ) ;
2005-04-17 02:20:36 +04:00
2010-03-02 10:57:33 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2008-08-31 17:50:33 +04:00
int pcmcia_parse_tuple ( tuple_t * tuple , cisparse_t * parse )
2005-04-17 02:20:36 +04:00
{
2010-03-02 10:57:33 +03:00
int ret = 0 ;
if ( tuple - > TupleDataLen > tuple - > TupleDataMax )
return - EINVAL ;
switch ( tuple - > TupleCode ) {
case CISTPL_DEVICE :
case CISTPL_DEVICE_A :
ret = parse_device ( tuple , & parse - > device ) ;
break ;
case CISTPL_CHECKSUM :
ret = parse_checksum ( tuple , & parse - > checksum ) ;
break ;
case CISTPL_LONGLINK_A :
case CISTPL_LONGLINK_C :
ret = parse_longlink ( tuple , & parse - > longlink ) ;
break ;
case CISTPL_LONGLINK_MFC :
ret = parse_longlink_mfc ( tuple , & parse - > longlink_mfc ) ;
break ;
case CISTPL_VERS_1 :
ret = parse_vers_1 ( tuple , & parse - > version_1 ) ;
break ;
case CISTPL_ALTSTR :
ret = parse_altstr ( tuple , & parse - > altstr ) ;
break ;
case CISTPL_JEDEC_A :
case CISTPL_JEDEC_C :
ret = parse_jedec ( tuple , & parse - > jedec ) ;
break ;
case CISTPL_MANFID :
ret = parse_manfid ( tuple , & parse - > manfid ) ;
break ;
case CISTPL_FUNCID :
ret = parse_funcid ( tuple , & parse - > funcid ) ;
break ;
case CISTPL_FUNCE :
ret = parse_funce ( tuple , & parse - > funce ) ;
break ;
case CISTPL_CONFIG :
ret = parse_config ( tuple , & parse - > config ) ;
break ;
case CISTPL_CFTABLE_ENTRY :
ret = parse_cftable_entry ( tuple , & parse - > cftable_entry ) ;
break ;
case CISTPL_DEVICE_GEO :
case CISTPL_DEVICE_GEO_A :
ret = parse_device_geo ( tuple , & parse - > device_geo ) ;
break ;
case CISTPL_VERS_2 :
ret = parse_vers_2 ( tuple , & parse - > vers_2 ) ;
break ;
case CISTPL_ORG :
ret = parse_org ( tuple , & parse - > org ) ;
break ;
case CISTPL_FORMAT :
case CISTPL_FORMAT_A :
ret = parse_format ( tuple , & parse - > format ) ;
break ;
case CISTPL_NO_LINK :
case CISTPL_LINKTARGET :
ret = 0 ;
break ;
default :
ret = - EINVAL ;
break ;
}
if ( ret )
pr_debug ( " parse_tuple failed %d \n " , ret ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
2008-08-31 17:50:33 +04:00
EXPORT_SYMBOL ( pcmcia_parse_tuple ) ;
2005-04-17 02:20:36 +04:00
2009-12-08 00:11:45 +03:00
2010-01-02 14:58:10 +03:00
/**
* pccard_validate_cis ( ) - check whether card has a sensible CIS
* @ s : the struct pcmcia_socket we are to check
* @ info : returns the number of tuples in the ( valid ) CIS , or 0
*
* This tries to determine if a card has a sensible CIS . In @ info , it
* returns the number of tuples in the CIS , or 0 if the CIS looks bad . The
* checks include making sure several critical tuples are present and
* valid ; seeing if the total number of tuples is reasonable ; and
* looking for tuples that use reserved codes .
*
* The function returns 0 on success .
*/
2009-10-19 01:51:09 +04:00
int pccard_validate_cis ( struct pcmcia_socket * s , unsigned int * info )
2005-04-17 02:20:36 +04:00
{
2010-01-02 14:58:10 +03:00
tuple_t * tuple ;
cisparse_t * p ;
unsigned int count = 0 ;
int ret , reserved , dev_ok = 0 , ident_ok = 0 ;
2005-04-17 02:20:36 +04:00
2010-01-02 14:58:10 +03:00
if ( ! s )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2014-12-10 18:06:40 +03:00
if ( s - > functions | | ! ( s - > state & SOCKET_PRESENT ) ) {
2010-04-17 19:37:33 +04:00
WARN_ON ( 1 ) ;
return - EINVAL ;
}
2010-01-02 14:58:10 +03:00
/* We do not want to validate the CIS cache... */
2010-01-13 00:05:36 +03:00
mutex_lock ( & s - > ops_mutex ) ;
2010-01-02 14:58:10 +03:00
destroy_cis_cache ( s ) ;
2010-01-13 00:05:36 +03:00
mutex_unlock ( & s - > ops_mutex ) ;
2010-01-02 14:28:04 +03:00
2010-01-02 14:58:10 +03:00
tuple = kmalloc ( sizeof ( * tuple ) , GFP_KERNEL ) ;
if ( tuple = = NULL ) {
dev_warn ( & s - > dev , " no memory to validate CIS \n " ) ;
return - ENOMEM ;
}
p = kmalloc ( sizeof ( * p ) , GFP_KERNEL ) ;
if ( p = = NULL ) {
kfree ( tuple ) ;
dev_warn ( & s - > dev , " no memory to validate CIS \n " ) ;
return - ENOMEM ;
}
2005-04-17 02:20:36 +04:00
2010-01-02 14:58:10 +03:00
count = reserved = 0 ;
tuple - > DesiredTuple = RETURN_FIRST_TUPLE ;
tuple - > Attributes = TUPLE_RETURN_COMMON ;
ret = pccard_get_first_tuple ( s , BIND_FN_ALL , tuple ) ;
2008-08-03 12:07:45 +04:00
if ( ret ! = 0 )
2010-01-02 14:58:10 +03:00
goto done ;
/* First tuple should be DEVICE; we should really have either that
or a CFTABLE_ENTRY of some sort */
if ( ( tuple - > TupleCode = = CISTPL_DEVICE ) | |
( ! pccard_read_tuple ( s , BIND_FN_ALL , CISTPL_CFTABLE_ENTRY , p ) ) | |
( ! pccard_read_tuple ( s , BIND_FN_ALL , CISTPL_CFTABLE_ENTRY_CB , p ) ) )
dev_ok + + ;
/* All cards should have a MANFID tuple, and/or a VERS_1 or VERS_2
tuple , for card identification . Certain old D - Link and Linksys
cards have only a broken VERS_2 tuple ; hence the bogus test . */
if ( ( pccard_read_tuple ( s , BIND_FN_ALL , CISTPL_MANFID , p ) = = 0 ) | |
( pccard_read_tuple ( s , BIND_FN_ALL , CISTPL_VERS_1 , p ) = = 0 ) | |
( pccard_read_tuple ( s , BIND_FN_ALL , CISTPL_VERS_2 , p ) ! = - ENOSPC ) )
ident_ok + + ;
if ( ! dev_ok & & ! ident_ok )
goto done ;
for ( count = 1 ; count < MAX_TUPLES ; count + + ) {
ret = pccard_get_next_tuple ( s , BIND_FN_ALL , tuple ) ;
if ( ret ! = 0 )
break ;
if ( ( ( tuple - > TupleCode > 0x23 ) & & ( tuple - > TupleCode < 0x40 ) ) | |
( ( tuple - > TupleCode > 0x47 ) & & ( tuple - > TupleCode < 0x80 ) ) | |
( ( tuple - > TupleCode > 0x90 ) & & ( tuple - > TupleCode < 0xff ) ) )
reserved + + ;
}
if ( ( count = = MAX_TUPLES ) | | ( reserved > 5 ) | |
( ( ! dev_ok | | ! ident_ok ) & & ( count > 10 ) ) )
count = 0 ;
ret = 0 ;
2005-04-17 02:20:36 +04:00
done :
2010-01-02 14:58:10 +03:00
/* invalidate CIS cache on failure */
if ( ! dev_ok | | ! ident_ok | | ! count ) {
2015-06-14 22:52:46 +03:00
mutex_lock ( & s - > ops_mutex ) ;
destroy_cis_cache ( s ) ;
mutex_unlock ( & s - > ops_mutex ) ;
/* We differentiate between dev_ok, ident_ok and count
failures to allow for an override for anonymous cards
in ds . c */
if ( ! dev_ok | | ! ident_ok )
2014-12-10 18:07:14 +03:00
ret = - EIO ;
2015-06-14 22:52:46 +03:00
else
ret = - EFAULT ;
2010-01-02 14:58:10 +03:00
}
if ( info )
* info = count ;
kfree ( tuple ) ;
kfree ( p ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
2010-01-06 15:57:43 +03:00
# define to_socket(_dev) container_of(_dev, struct pcmcia_socket, dev)
static ssize_t pccard_extract_cis ( struct pcmcia_socket * s , char * buf ,
loff_t off , size_t count )
{
tuple_t tuple ;
int status , i ;
loff_t pointer = 0 ;
ssize_t ret = 0 ;
u_char * tuplebuffer ;
u_char * tempbuffer ;
tuplebuffer = kmalloc ( sizeof ( u_char ) * 256 , GFP_KERNEL ) ;
if ( ! tuplebuffer )
return - ENOMEM ;
tempbuffer = kmalloc ( sizeof ( u_char ) * 258 , GFP_KERNEL ) ;
if ( ! tempbuffer ) {
ret = - ENOMEM ;
goto free_tuple ;
}
memset ( & tuple , 0 , sizeof ( tuple_t ) ) ;
tuple . Attributes = TUPLE_RETURN_LINK | TUPLE_RETURN_COMMON ;
tuple . DesiredTuple = RETURN_FIRST_TUPLE ;
tuple . TupleOffset = 0 ;
status = pccard_get_first_tuple ( s , BIND_FN_ALL , & tuple ) ;
while ( ! status ) {
tuple . TupleData = tuplebuffer ;
tuple . TupleDataMax = 255 ;
memset ( tuplebuffer , 0 , sizeof ( u_char ) * 255 ) ;
status = pccard_get_tuple_data ( s , & tuple ) ;
if ( status )
break ;
if ( off < ( pointer + 2 + tuple . TupleDataLen ) ) {
tempbuffer [ 0 ] = tuple . TupleCode & 0xff ;
tempbuffer [ 1 ] = tuple . TupleLink & 0xff ;
for ( i = 0 ; i < tuple . TupleDataLen ; i + + )
tempbuffer [ i + 2 ] = tuplebuffer [ i ] & 0xff ;
for ( i = 0 ; i < ( 2 + tuple . TupleDataLen ) ; i + + ) {
if ( ( ( i + pointer ) > = off ) & &
( i + pointer ) < ( off + count ) ) {
buf [ ret ] = tempbuffer [ i ] ;
ret + + ;
}
}
}
pointer + = 2 + tuple . TupleDataLen ;
if ( pointer > = ( off + count ) )
break ;
if ( tuple . TupleCode = = CISTPL_END )
break ;
status = pccard_get_next_tuple ( s , BIND_FN_ALL , & tuple ) ;
}
kfree ( tempbuffer ) ;
free_tuple :
kfree ( tuplebuffer ) ;
return ret ;
}
2010-05-13 05:28:57 +04:00
static ssize_t pccard_show_cis ( struct file * filp , struct kobject * kobj ,
2010-01-06 15:57:43 +03:00
struct bin_attribute * bin_attr ,
char * buf , loff_t off , size_t count )
{
unsigned int size = 0x200 ;
if ( off > = size )
count = 0 ;
else {
struct pcmcia_socket * s ;
2010-04-17 19:37:33 +04:00
unsigned int chains = 1 ;
2010-01-06 15:57:43 +03:00
if ( off + count > size )
count = size - off ;
s = to_socket ( container_of ( kobj , struct device , kobj ) ) ;
if ( ! ( s - > state & SOCKET_PRESENT ) )
return - ENODEV ;
2010-04-17 19:37:33 +04:00
if ( ! s - > functions & & pccard_validate_cis ( s , & chains ) )
2010-01-06 15:57:43 +03:00
return - EIO ;
if ( ! chains )
return - ENODATA ;
count = pccard_extract_cis ( s , buf , off , count ) ;
}
return count ;
}
2010-05-13 05:28:57 +04:00
static ssize_t pccard_store_cis ( struct file * filp , struct kobject * kobj ,
2010-01-06 15:57:43 +03:00
struct bin_attribute * bin_attr ,
char * buf , loff_t off , size_t count )
{
struct pcmcia_socket * s ;
int error ;
s = to_socket ( container_of ( kobj , struct device , kobj ) ) ;
if ( off )
return - EINVAL ;
if ( count > = CISTPL_MAX_CIS_SIZE )
return - EINVAL ;
if ( ! ( s - > state & SOCKET_PRESENT ) )
return - ENODEV ;
error = pcmcia_replace_cis ( s , buf , count ) ;
if ( error )
return - EIO ;
2010-01-17 21:30:53 +03:00
pcmcia_parse_uevents ( s , PCMCIA_UEVENT_REQUERY ) ;
2010-01-06 15:57:43 +03:00
return count ;
}
struct bin_attribute pccard_cis_attr = {
. attr = { . name = " cis " , . mode = S_IRUGO | S_IWUSR } ,
. size = 0x200 ,
. read = pccard_show_cis ,
. write = pccard_store_cis ,
} ;