2005-04-16 15:20:36 -07:00
/* $Id: teles_cs.c,v 1.1.2.2 2004/01/25 15:07:06 keil Exp $ */
/*======================================================================
A teles S0 PCMCIA client driver
Based on skeleton by David Hinds , dhinds @ allegro . stanford . edu
Written by Christof Petig , christof . petig @ wtal . de
Also inspired by ELSA PCMCIA driver
by Klaus Lichtenwalder < Lichtenwalder @ ACM . org >
Extentions to new hisax_pcmcia by Karsten Keil
minor changes to be compatible with kernel 2.4 . x
by Jan . Schubert @ GMX . li
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/ptrace.h>
# include <linux/slab.h>
# include <linux/string.h>
# include <linux/timer.h>
# include <linux/ioport.h>
# include <asm/io.h>
# include <asm/system.h>
# include <pcmcia/cs_types.h>
# include <pcmcia/cs.h>
# include <pcmcia/cistpl.h>
# include <pcmcia/cisreg.h>
# include <pcmcia/ds.h>
# include "hisax_cfg.h"
MODULE_DESCRIPTION ( " ISDN4Linux: PCMCIA client driver for Teles PCMCIA cards " ) ;
MODULE_AUTHOR ( " Christof Petig, christof.petig@wtal.de, Karsten Keil, kkeil@suse.de " ) ;
MODULE_LICENSE ( " GPL " ) ;
/*
All the PCMCIA modules use PCMCIA_DEBUG to control debugging . If
you do not define PCMCIA_DEBUG at all , all the debug code will be
left out . If you compile with PCMCIA_DEBUG = 0 , the debug code will
be present but disabled - - but it can then be enabled for specific
modules at load time with a ' pc_debug = # ' option to insmod .
*/
# ifdef PCMCIA_DEBUG
static int pc_debug = PCMCIA_DEBUG ;
module_param ( pc_debug , int , 0 ) ;
# define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args);
static char * version =
" teles_cs.c 2.10 2002/07/30 22:23:34 kkeil " ;
# else
# define DEBUG(n, args...)
# endif
/*====================================================================*/
/* Parameters that can be set with 'insmod' */
static int protocol = 2 ; /* EURO-ISDN Default */
module_param ( protocol , int , 0 ) ;
/*====================================================================*/
/*
The event ( ) function is this driver ' s Card Services event handler .
It will be called by Card Services when an appropriate card status
event is received . The config ( ) and release ( ) entry points are
used to configure or release a socket , in response to card insertion
and ejection events . They are invoked from the teles_cs event
handler .
*/
2006-03-31 17:26:06 +02:00
static int teles_cs_config ( struct pcmcia_device * link ) ;
2006-03-31 17:21:06 +02:00
static void teles_cs_release ( struct pcmcia_device * link ) ;
2005-04-16 15:20:36 -07:00
/*
The attach ( ) and detach ( ) entry points are used to create and destroy
" instances " of the driver , where each instance represents everything
needed to manage one actual PCMCIA card .
*/
2005-11-14 21:23:14 +01:00
static void teles_detach ( struct pcmcia_device * p_dev ) ;
2005-04-16 15:20:36 -07:00
/*
A linked list of " instances " of the teles_cs device . Each actual
PCMCIA card corresponds to one device instance , and is described
2006-03-31 17:21:06 +02:00
by one struct pcmcia_device structure ( defined in ds . h ) .
2005-04-16 15:20:36 -07:00
You may not want to use a linked list for this - - for example , the
2006-03-31 17:21:06 +02:00
memory card driver uses an array of struct pcmcia_device pointers , where minor
2005-04-16 15:20:36 -07:00
device numbers are used to derive the corresponding array index .
*/
/*
A driver needs to provide a dev_node_t structure for each device
on a card . In some cases , there is only one device per card ( for
example , ethernet cards , modems ) . In other cases , there may be
many actual or logical devices ( SCSI adapters , memory cards with
multiple partitions ) . The dev_node_t structures need to be kept
2006-03-31 17:21:06 +02:00
in a linked list starting at the ' dev ' field of a struct pcmcia_device
2005-04-16 15:20:36 -07:00
structure . We allocate them in the card ' s private data structure ,
because they generally shouldn ' t be allocated dynamically .
In this case , we also provide a flag to indicate if a device is
" stopped " due to a power management event , or card ejection . The
device IO routines can use a flag like this to throttle IO to a
card that is not ready to accept it .
*/
typedef struct local_info_t {
2006-03-05 10:45:09 +01:00
struct pcmcia_device * p_dev ;
2005-04-16 15:20:36 -07:00
dev_node_t node ;
int busy ;
int cardnr ;
} local_info_t ;
/*======================================================================
teles_attach ( ) creates an " instance " of the driver , allocatingx
local data structures for one device . The device is registered
with Card Services .
The dev_link structure is initialized , but we don ' t actually
configure the card at this point - - we wait until we receive a
card insertion event .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
2006-03-31 17:26:06 +02:00
static int teles_probe ( struct pcmcia_device * link )
2005-04-16 15:20:36 -07:00
{
local_info_t * local ;
DEBUG ( 0 , " teles_attach() \n " ) ;
/* Allocate space for private device-specific data */
2006-12-08 02:39:35 -08:00
local = kzalloc ( sizeof ( local_info_t ) , GFP_KERNEL ) ;
2005-11-14 21:25:51 +01:00
if ( ! local ) return - ENOMEM ;
2005-04-16 15:20:36 -07:00
local - > cardnr = - 1 ;
2006-03-05 10:45:09 +01:00
2006-03-31 17:21:06 +02:00
local - > p_dev = link ;
2006-03-05 10:45:09 +01:00
link - > priv = local ;
2005-04-16 15:20:36 -07:00
/* Interrupt setup */
link - > irq . Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_FIRST_SHARED ;
link - > irq . IRQInfo1 = IRQ_LEVEL_ID | IRQ_SHARE_ID ;
link - > irq . Handler = NULL ;
/*
General socket configuration defaults can go here . In this
client , we assume very little , and rely on the CIS for almost
everything . In most clients , many details ( i . e . , number , sizes ,
and attributes of IO windows ) are fixed by the nature of the
device , and can be hard - wired here .
*/
link - > io . NumPorts1 = 96 ;
link - > io . Attributes1 = IO_DATA_PATH_WIDTH_AUTO ;
link - > io . IOAddrLines = 5 ;
link - > conf . Attributes = CONF_ENABLE_IRQ ;
link - > conf . IntType = INT_MEMORY_AND_IO ;
2006-03-31 17:26:06 +02:00
return teles_cs_config ( link ) ;
2005-04-16 15:20:36 -07:00
} /* teles_attach */
/*======================================================================
This deletes a driver " instance " . The device is de - registered
with Card Services . If it has been released , all local data
structures are freed . Otherwise , the structures will be freed
when the device is released .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
2006-03-31 17:21:06 +02:00
static void teles_detach ( struct pcmcia_device * link )
2005-04-16 15:20:36 -07:00
{
2006-03-02 00:09:29 +01:00
local_info_t * info = link - > priv ;
2005-04-16 15:20:36 -07:00
2006-03-02 00:09:29 +01:00
DEBUG ( 0 , " teles_detach(0x%p) \n " , link ) ;
2005-04-16 15:20:36 -07:00
2006-03-02 00:09:29 +01:00
info - > busy = 1 ;
teles_cs_release ( link ) ;
2005-04-16 15:20:36 -07:00
2006-03-02 00:09:29 +01:00
kfree ( info ) ;
2005-04-16 15:20:36 -07:00
} /* teles_detach */
/*======================================================================
teles_cs_config ( ) is scheduled to run after a CARD_INSERTION event
is received , to configure the PCMCIA socket , and to make the
device available to the system .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
2006-03-31 17:21:06 +02:00
static int get_tuple ( struct pcmcia_device * handle , tuple_t * tuple ,
2005-04-16 15:20:36 -07:00
cisparse_t * parse )
{
int i = pcmcia_get_tuple_data ( handle , tuple ) ;
if ( i ! = CS_SUCCESS ) return i ;
return pcmcia_parse_tuple ( handle , tuple , parse ) ;
}
2006-03-31 17:21:06 +02:00
static int first_tuple ( struct pcmcia_device * handle , tuple_t * tuple ,
2005-04-16 15:20:36 -07:00
cisparse_t * parse )
{
int i = pcmcia_get_first_tuple ( handle , tuple ) ;
if ( i ! = CS_SUCCESS ) return i ;
return get_tuple ( handle , tuple , parse ) ;
}
2006-03-31 17:21:06 +02:00
static int next_tuple ( struct pcmcia_device * handle , tuple_t * tuple ,
2005-04-16 15:20:36 -07:00
cisparse_t * parse )
{
int i = pcmcia_get_next_tuple ( handle , tuple ) ;
if ( i ! = CS_SUCCESS ) return i ;
return get_tuple ( handle , tuple , parse ) ;
}
2006-03-31 17:26:06 +02:00
static int teles_cs_config ( struct pcmcia_device * link )
2005-04-16 15:20:36 -07:00
{
tuple_t tuple ;
cisparse_t parse ;
local_info_t * dev ;
int i , j , last_fn ;
u_short buf [ 128 ] ;
cistpl_cftable_entry_t * cf = & parse . cftable_entry ;
IsdnCard_t icard ;
DEBUG ( 0 , " teles_config(0x%p) \n " , link ) ;
dev = link - > priv ;
tuple . TupleData = ( cisdata_t * ) buf ;
tuple . TupleOffset = 0 ; tuple . TupleDataMax = 255 ;
tuple . Attributes = 0 ;
tuple . DesiredTuple = CISTPL_CFTABLE_ENTRY ;
2006-03-31 17:21:06 +02:00
i = first_tuple ( link , & tuple , & parse ) ;
2005-04-16 15:20:36 -07:00
while ( i = = CS_SUCCESS ) {
if ( ( cf - > io . nwin > 0 ) & & cf - > io . win [ 0 ] . base ) {
printk ( KERN_INFO " (teles_cs: looks like the 96 model) \n " ) ;
link - > conf . ConfigIndex = cf - > index ;
link - > io . BasePort1 = cf - > io . win [ 0 ] . base ;
2006-03-31 17:21:06 +02:00
i = pcmcia_request_io ( link , & link - > io ) ;
2005-04-16 15:20:36 -07:00
if ( i = = CS_SUCCESS ) break ;
} else {
printk ( KERN_INFO " (teles_cs: looks like the 97 model) \n " ) ;
link - > conf . ConfigIndex = cf - > index ;
for ( i = 0 , j = 0x2f0 ; j > 0x100 ; j - = 0x10 ) {
link - > io . BasePort1 = j ;
2006-03-31 17:21:06 +02:00
i = pcmcia_request_io ( link , & link - > io ) ;
2005-04-16 15:20:36 -07:00
if ( i = = CS_SUCCESS ) break ;
}
break ;
}
2006-03-31 17:21:06 +02:00
i = next_tuple ( link , & tuple , & parse ) ;
2005-04-16 15:20:36 -07:00
}
if ( i ! = CS_SUCCESS ) {
last_fn = RequestIO ;
goto cs_failed ;
}
2006-03-31 17:21:06 +02:00
i = pcmcia_request_irq ( link , & link - > irq ) ;
2005-04-16 15:20:36 -07:00
if ( i ! = CS_SUCCESS ) {
link - > irq . AssignedIRQ = 0 ;
last_fn = RequestIRQ ;
goto cs_failed ;
}
2006-03-31 17:21:06 +02:00
i = pcmcia_request_configuration ( link , & link - > conf ) ;
2005-04-16 15:20:36 -07:00
if ( i ! = CS_SUCCESS ) {
last_fn = RequestConfiguration ;
goto cs_failed ;
}
/* At this point, the dev_node_t structure(s) should be
initialized and arranged in a linked list at link - > dev . */ /* */
sprintf ( dev - > node . dev_name , " teles " ) ;
dev - > node . major = dev - > node . minor = 0x0 ;
2006-03-05 10:45:09 +01:00
link - > dev_node = & dev - > node ;
2005-04-16 15:20:36 -07:00
/* Finally, report what we've done */
2006-01-15 12:43:16 +01:00
printk ( KERN_INFO " %s: index 0x%02x: " ,
dev - > node . dev_name , link - > conf . ConfigIndex ) ;
2005-04-16 15:20:36 -07:00
if ( link - > conf . Attributes & CONF_ENABLE_IRQ )
printk ( " , irq %d " , link - > irq . AssignedIRQ ) ;
if ( link - > io . NumPorts1 )
printk ( " , io 0x%04x-0x%04x " , link - > io . BasePort1 ,
link - > io . BasePort1 + link - > io . NumPorts1 - 1 ) ;
if ( link - > io . NumPorts2 )
printk ( " & 0x%04x-0x%04x " , link - > io . BasePort2 ,
link - > io . BasePort2 + link - > io . NumPorts2 - 1 ) ;
printk ( " \n " ) ;
icard . para [ 0 ] = link - > irq . AssignedIRQ ;
icard . para [ 1 ] = link - > io . BasePort1 ;
icard . protocol = protocol ;
icard . typ = ISDN_CTYPE_TELESPCMCIA ;
i = hisax_init_pcmcia ( link , & ( ( ( local_info_t * ) link - > priv ) - > busy ) , & icard ) ;
if ( i < 0 ) {
printk ( KERN_ERR " teles_cs: failed to initialize Teles PCMCIA %d at i/o %#x \n " ,
i , link - > io . BasePort1 ) ;
teles_cs_release ( link ) ;
2006-03-31 17:26:06 +02:00
return - ENODEV ;
}
( ( local_info_t * ) link - > priv ) - > cardnr = i ;
return 0 ;
2005-04-16 15:20:36 -07:00
cs_failed :
2006-03-31 17:21:06 +02:00
cs_error ( link , last_fn , i ) ;
2005-04-16 15:20:36 -07:00
teles_cs_release ( link ) ;
2006-03-31 17:26:06 +02:00
return - ENODEV ;
2005-04-16 15:20:36 -07:00
} /* teles_cs_config */
/*======================================================================
After a card is removed , teles_cs_release ( ) will unregister the net
device , and release the PCMCIA configuration . If the device is
still open , this will be postponed until it is closed .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
2006-03-31 17:21:06 +02:00
static void teles_cs_release ( struct pcmcia_device * link )
2005-04-16 15:20:36 -07:00
{
local_info_t * local = link - > priv ;
DEBUG ( 0 , " teles_cs_release(0x%p) \n " , link ) ;
if ( local ) {
if ( local - > cardnr > = 0 ) {
/* no unregister function with hisax */
HiSax_closecard ( local - > cardnr ) ;
}
}
2006-01-15 09:32:39 +01:00
2006-03-31 17:21:06 +02:00
pcmcia_disable_device ( link ) ;
2005-04-16 15:20:36 -07:00
} /* teles_cs_release */
2006-03-31 17:21:06 +02:00
static int teles_suspend ( struct pcmcia_device * link )
2005-11-14 21:21:18 +01:00
{
local_info_t * dev = link - > priv ;
dev - > busy = 1 ;
return 0 ;
}
2006-03-31 17:21:06 +02:00
static int teles_resume ( struct pcmcia_device * link )
2005-11-14 21:21:18 +01:00
{
local_info_t * dev = link - > priv ;
dev - > busy = 0 ;
return 0 ;
}
2005-04-16 15:20:36 -07:00
2005-06-27 16:28:24 -07:00
static struct pcmcia_device_id teles_ids [ ] = {
PCMCIA_DEVICE_PROD_ID12 ( " TELES " , " S0/PC " , 0x67b50eae , 0xe9e70119 ) ,
PCMCIA_DEVICE_NULL ,
} ;
MODULE_DEVICE_TABLE ( pcmcia , teles_ids ) ;
2005-04-16 15:20:36 -07:00
static struct pcmcia_driver teles_cs_driver = {
. owner = THIS_MODULE ,
. drv = {
. name = " teles_cs " ,
} ,
2006-03-31 17:26:06 +02:00
. probe = teles_probe ,
2005-11-14 21:23:14 +01:00
. remove = teles_detach ,
2005-06-27 16:28:24 -07:00
. id_table = teles_ids ,
2005-11-14 21:21:18 +01:00
. suspend = teles_suspend ,
. resume = teles_resume ,
2005-04-16 15:20:36 -07:00
} ;
static int __init init_teles_cs ( void )
{
return pcmcia_register_driver ( & teles_cs_driver ) ;
}
static void __exit exit_teles_cs ( void )
{
pcmcia_unregister_driver ( & teles_cs_driver ) ;
}
module_init ( init_teles_cs ) ;
module_exit ( exit_teles_cs ) ;