2006-03-26 13:38:29 +04:00
/*
* Stuff used by all variants of the driver
*
2006-04-11 09:55:14 +04:00
* Copyright ( c ) 2001 by Stefan Eilers ,
2006-03-26 13:38:29 +04:00
* Hansjoerg Lipp < hjlipp @ web . de > ,
* Tilman Schmidt < tilman @ imap . cc > .
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation ; either version 2 of
* the License , or ( at your option ) any later version .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
# include "gigaset.h"
# include <linux/ctype.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
/* Version Information */
2006-04-11 09:55:14 +04:00
# define DRIVER_AUTHOR "Hansjoerg Lipp <hjlipp@web.de>, Tilman Schmidt <tilman@imap.cc>, Stefan Eilers"
2006-03-26 13:38:29 +04:00
# define DRIVER_DESC "Driver for Gigaset 307x"
/* Module parameters */
int gigaset_debuglevel = DEBUG_DEFAULT ;
EXPORT_SYMBOL_GPL ( gigaset_debuglevel ) ;
module_param_named ( debug , gigaset_debuglevel , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( debug , " debug level " ) ;
2006-04-11 09:55:14 +04:00
/* driver state flags */
2006-04-11 09:55:04 +04:00
# define VALID_MINOR 0x01
# define VALID_ID 0x02
# define ASSIGNED 0x04
2006-03-26 13:38:29 +04:00
/* bitwise byte inversion table */
__u8 gigaset_invtab [ 256 ] = {
0x00 , 0x80 , 0x40 , 0xc0 , 0x20 , 0xa0 , 0x60 , 0xe0 ,
0x10 , 0x90 , 0x50 , 0xd0 , 0x30 , 0xb0 , 0x70 , 0xf0 ,
0x08 , 0x88 , 0x48 , 0xc8 , 0x28 , 0xa8 , 0x68 , 0xe8 ,
0x18 , 0x98 , 0x58 , 0xd8 , 0x38 , 0xb8 , 0x78 , 0xf8 ,
0x04 , 0x84 , 0x44 , 0xc4 , 0x24 , 0xa4 , 0x64 , 0xe4 ,
0x14 , 0x94 , 0x54 , 0xd4 , 0x34 , 0xb4 , 0x74 , 0xf4 ,
0x0c , 0x8c , 0x4c , 0xcc , 0x2c , 0xac , 0x6c , 0xec ,
0x1c , 0x9c , 0x5c , 0xdc , 0x3c , 0xbc , 0x7c , 0xfc ,
0x02 , 0x82 , 0x42 , 0xc2 , 0x22 , 0xa2 , 0x62 , 0xe2 ,
0x12 , 0x92 , 0x52 , 0xd2 , 0x32 , 0xb2 , 0x72 , 0xf2 ,
0x0a , 0x8a , 0x4a , 0xca , 0x2a , 0xaa , 0x6a , 0xea ,
0x1a , 0x9a , 0x5a , 0xda , 0x3a , 0xba , 0x7a , 0xfa ,
0x06 , 0x86 , 0x46 , 0xc6 , 0x26 , 0xa6 , 0x66 , 0xe6 ,
0x16 , 0x96 , 0x56 , 0xd6 , 0x36 , 0xb6 , 0x76 , 0xf6 ,
0x0e , 0x8e , 0x4e , 0xce , 0x2e , 0xae , 0x6e , 0xee ,
0x1e , 0x9e , 0x5e , 0xde , 0x3e , 0xbe , 0x7e , 0xfe ,
0x01 , 0x81 , 0x41 , 0xc1 , 0x21 , 0xa1 , 0x61 , 0xe1 ,
0x11 , 0x91 , 0x51 , 0xd1 , 0x31 , 0xb1 , 0x71 , 0xf1 ,
0x09 , 0x89 , 0x49 , 0xc9 , 0x29 , 0xa9 , 0x69 , 0xe9 ,
0x19 , 0x99 , 0x59 , 0xd9 , 0x39 , 0xb9 , 0x79 , 0xf9 ,
0x05 , 0x85 , 0x45 , 0xc5 , 0x25 , 0xa5 , 0x65 , 0xe5 ,
0x15 , 0x95 , 0x55 , 0xd5 , 0x35 , 0xb5 , 0x75 , 0xf5 ,
0x0d , 0x8d , 0x4d , 0xcd , 0x2d , 0xad , 0x6d , 0xed ,
0x1d , 0x9d , 0x5d , 0xdd , 0x3d , 0xbd , 0x7d , 0xfd ,
0x03 , 0x83 , 0x43 , 0xc3 , 0x23 , 0xa3 , 0x63 , 0xe3 ,
0x13 , 0x93 , 0x53 , 0xd3 , 0x33 , 0xb3 , 0x73 , 0xf3 ,
0x0b , 0x8b , 0x4b , 0xcb , 0x2b , 0xab , 0x6b , 0xeb ,
0x1b , 0x9b , 0x5b , 0xdb , 0x3b , 0xbb , 0x7b , 0xfb ,
0x07 , 0x87 , 0x47 , 0xc7 , 0x27 , 0xa7 , 0x67 , 0xe7 ,
0x17 , 0x97 , 0x57 , 0xd7 , 0x37 , 0xb7 , 0x77 , 0xf7 ,
0x0f , 0x8f , 0x4f , 0xcf , 0x2f , 0xaf , 0x6f , 0xef ,
0x1f , 0x9f , 0x5f , 0xdf , 0x3f , 0xbf , 0x7f , 0xff
} ;
EXPORT_SYMBOL_GPL ( gigaset_invtab ) ;
void gigaset_dbg_buffer ( enum debuglevel level , const unsigned char * msg ,
2006-04-11 09:55:11 +04:00
size_t len , const unsigned char * buf )
2006-03-26 13:38:29 +04:00
{
unsigned char outbuf [ 80 ] ;
2006-04-11 09:55:04 +04:00
unsigned char c ;
2006-03-26 13:38:29 +04:00
size_t space = sizeof outbuf - 1 ;
unsigned char * out = outbuf ;
2006-04-11 09:55:11 +04:00
size_t numin = len ;
2006-03-26 13:38:29 +04:00
2006-04-11 09:55:11 +04:00
while ( numin - - ) {
2006-04-11 09:55:04 +04:00
c = * buf + + ;
if ( c = = ' ~ ' | | c = = ' ^ ' | | c = = ' \\ ' ) {
2006-04-11 09:55:11 +04:00
if ( ! space - - )
2006-04-11 09:55:04 +04:00
break ;
* out + + = ' \\ ' ;
}
if ( c & 0x80 ) {
2006-04-11 09:55:11 +04:00
if ( ! space - - )
2006-04-11 09:55:04 +04:00
break ;
* out + + = ' ~ ' ;
c ^ = 0x80 ;
}
if ( c < 0x20 | | c = = 0x7f ) {
2006-04-11 09:55:11 +04:00
if ( ! space - - )
2006-04-11 09:55:04 +04:00
break ;
2006-03-26 13:38:29 +04:00
* out + + = ' ^ ' ;
2006-04-11 09:55:04 +04:00
c ^ = 0x40 ;
2006-03-26 13:38:29 +04:00
}
2006-04-11 09:55:11 +04:00
if ( ! space - - )
2006-04-11 09:55:04 +04:00
break ;
* out + + = c ;
2006-03-26 13:38:29 +04:00
}
* out = 0 ;
2006-04-11 09:55:04 +04:00
gig_dbg ( level , " %s (%u bytes): %s " , msg , ( unsigned ) len , outbuf ) ;
2006-03-26 13:38:29 +04:00
}
EXPORT_SYMBOL_GPL ( gigaset_dbg_buffer ) ;
static int setflags ( struct cardstate * cs , unsigned flags , unsigned delay )
{
int r ;
r = cs - > ops - > set_modem_ctrl ( cs , cs - > control_state , flags ) ;
cs - > control_state = flags ;
if ( r < 0 )
return r ;
if ( delay ) {
set_current_state ( TASK_INTERRUPTIBLE ) ;
schedule_timeout ( delay * HZ / 1000 ) ;
}
return 0 ;
}
int gigaset_enterconfigmode ( struct cardstate * cs )
{
int i , r ;
cs - > control_state = TIOCM_RTS ; //FIXME
r = setflags ( cs , TIOCM_DTR , 200 ) ;
if ( r < 0 )
goto error ;
r = setflags ( cs , 0 , 200 ) ;
if ( r < 0 )
goto error ;
for ( i = 0 ; i < 5 ; + + i ) {
r = setflags ( cs , TIOCM_RTS , 100 ) ;
if ( r < 0 )
goto error ;
r = setflags ( cs , 0 , 100 ) ;
if ( r < 0 )
goto error ;
}
r = setflags ( cs , TIOCM_RTS | TIOCM_DTR , 800 ) ;
if ( r < 0 )
goto error ;
return 0 ;
error :
2006-04-11 09:55:04 +04:00
dev_err ( cs - > dev , " error %d on setuartbits \n " , - r ) ;
2006-03-26 13:38:29 +04:00
cs - > control_state = TIOCM_RTS | TIOCM_DTR ; // FIXME is this a good value?
cs - > ops - > set_modem_ctrl ( cs , 0 , TIOCM_RTS | TIOCM_DTR ) ;
return - 1 ; //r
}
static int test_timeout ( struct at_state_t * at_state )
{
if ( ! at_state - > timer_expires )
return 0 ;
if ( - - at_state - > timer_expires ) {
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_MCMD , " decreased timer of %p to %lu " ,
at_state , at_state - > timer_expires ) ;
2006-03-26 13:38:29 +04:00
return 0 ;
}
if ( ! gigaset_add_event ( at_state - > cs , at_state , EV_TIMEOUT , NULL ,
2006-04-11 09:55:16 +04:00
at_state - > timer_index , NULL ) ) {
2006-03-26 13:38:29 +04:00
//FIXME what should we do?
}
return 1 ;
}
static void timer_tick ( unsigned long data )
{
struct cardstate * cs = ( struct cardstate * ) data ;
unsigned long flags ;
unsigned channel ;
struct at_state_t * at_state ;
int timeout = 0 ;
spin_lock_irqsave ( & cs - > lock , flags ) ;
for ( channel = 0 ; channel < cs - > channels ; + + channel )
if ( test_timeout ( & cs - > bcs [ channel ] . at_state ) )
timeout = 1 ;
if ( test_timeout ( & cs - > at_state ) )
timeout = 1 ;
list_for_each_entry ( at_state , & cs - > temp_at_states , list )
if ( test_timeout ( at_state ) )
timeout = 1 ;
2006-04-11 09:55:16 +04:00
if ( cs - > running ) {
2006-04-11 09:55:03 +04:00
mod_timer ( & cs - > timer , jiffies + msecs_to_jiffies ( GIG_TICK ) ) ;
2006-03-26 13:38:29 +04:00
if ( timeout ) {
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_CMD , " scheduling timeout " ) ;
2006-03-26 13:38:29 +04:00
tasklet_schedule ( & cs - > event_tasklet ) ;
}
}
spin_unlock_irqrestore ( & cs - > lock , flags ) ;
}
int gigaset_get_channel ( struct bc_state * bcs )
{
unsigned long flags ;
spin_lock_irqsave ( & bcs - > cs - > lock , flags ) ;
if ( bcs - > use_count ) {
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_ANY , " could not allocate channel %d " ,
bcs - > channel ) ;
2006-03-26 13:38:29 +04:00
spin_unlock_irqrestore ( & bcs - > cs - > lock , flags ) ;
return 0 ;
}
+ + bcs - > use_count ;
bcs - > busy = 1 ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_ANY , " allocated channel %d " , bcs - > channel ) ;
2006-03-26 13:38:29 +04:00
spin_unlock_irqrestore ( & bcs - > cs - > lock , flags ) ;
return 1 ;
}
void gigaset_free_channel ( struct bc_state * bcs )
{
unsigned long flags ;
spin_lock_irqsave ( & bcs - > cs - > lock , flags ) ;
if ( ! bcs - > busy ) {
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_ANY , " could not free channel %d " , bcs - > channel ) ;
2006-03-26 13:38:29 +04:00
spin_unlock_irqrestore ( & bcs - > cs - > lock , flags ) ;
return ;
}
- - bcs - > use_count ;
bcs - > busy = 0 ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_ANY , " freed channel %d " , bcs - > channel ) ;
2006-03-26 13:38:29 +04:00
spin_unlock_irqrestore ( & bcs - > cs - > lock , flags ) ;
}
int gigaset_get_channels ( struct cardstate * cs )
{
unsigned long flags ;
int i ;
spin_lock_irqsave ( & cs - > lock , flags ) ;
for ( i = 0 ; i < cs - > channels ; + + i )
if ( cs - > bcs [ i ] . use_count ) {
spin_unlock_irqrestore ( & cs - > lock , flags ) ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_ANY , " could not allocate all channels " ) ;
2006-03-26 13:38:29 +04:00
return 0 ;
}
for ( i = 0 ; i < cs - > channels ; + + i )
+ + cs - > bcs [ i ] . use_count ;
spin_unlock_irqrestore ( & cs - > lock , flags ) ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_ANY , " allocated all channels " ) ;
2006-03-26 13:38:29 +04:00
return 1 ;
}
void gigaset_free_channels ( struct cardstate * cs )
{
unsigned long flags ;
int i ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_ANY , " unblocking all channels " ) ;
2006-03-26 13:38:29 +04:00
spin_lock_irqsave ( & cs - > lock , flags ) ;
for ( i = 0 ; i < cs - > channels ; + + i )
- - cs - > bcs [ i ] . use_count ;
spin_unlock_irqrestore ( & cs - > lock , flags ) ;
}
void gigaset_block_channels ( struct cardstate * cs )
{
unsigned long flags ;
int i ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_ANY , " blocking all channels " ) ;
2006-03-26 13:38:29 +04:00
spin_lock_irqsave ( & cs - > lock , flags ) ;
for ( i = 0 ; i < cs - > channels ; + + i )
+ + cs - > bcs [ i ] . use_count ;
spin_unlock_irqrestore ( & cs - > lock , flags ) ;
}
static void clear_events ( struct cardstate * cs )
{
struct event_t * ev ;
unsigned head , tail ;
2006-04-11 09:55:16 +04:00
unsigned long flags ;
2006-03-26 13:38:29 +04:00
2006-04-11 09:55:16 +04:00
spin_lock_irqsave ( & cs - > ev_lock , flags ) ;
2006-03-26 13:38:29 +04:00
2006-04-11 09:55:16 +04:00
head = cs - > ev_head ;
tail = cs - > ev_tail ;
2006-03-26 13:38:29 +04:00
while ( tail ! = head ) {
ev = cs - > events + head ;
kfree ( ev - > ptr ) ;
head = ( head + 1 ) % MAX_EVENTS ;
}
2006-04-11 09:55:16 +04:00
cs - > ev_head = tail ;
spin_unlock_irqrestore ( & cs - > ev_lock , flags ) ;
2006-03-26 13:38:29 +04:00
}
struct event_t * gigaset_add_event ( struct cardstate * cs ,
2006-04-11 09:55:04 +04:00
struct at_state_t * at_state , int type ,
void * ptr , int parameter , void * arg )
2006-03-26 13:38:29 +04:00
{
unsigned long flags ;
unsigned next , tail ;
struct event_t * event = NULL ;
spin_lock_irqsave ( & cs - > ev_lock , flags ) ;
2006-04-11 09:55:16 +04:00
tail = cs - > ev_tail ;
2006-03-26 13:38:29 +04:00
next = ( tail + 1 ) % MAX_EVENTS ;
2006-04-11 09:55:16 +04:00
if ( unlikely ( next = = cs - > ev_head ) )
2006-03-26 13:38:29 +04:00
err ( " event queue full " ) ;
else {
event = cs - > events + tail ;
event - > type = type ;
event - > at_state = at_state ;
event - > cid = - 1 ;
event - > ptr = ptr ;
event - > arg = arg ;
event - > parameter = parameter ;
2006-04-11 09:55:16 +04:00
cs - > ev_tail = next ;
2006-03-26 13:38:29 +04:00
}
spin_unlock_irqrestore ( & cs - > ev_lock , flags ) ;
return event ;
}
EXPORT_SYMBOL_GPL ( gigaset_add_event ) ;
static void free_strings ( struct at_state_t * at_state )
{
int i ;
for ( i = 0 ; i < STR_NUM ; + + i ) {
kfree ( at_state - > str_var [ i ] ) ;
at_state - > str_var [ i ] = NULL ;
}
}
static void clear_at_state ( struct at_state_t * at_state )
{
free_strings ( at_state ) ;
}
static void dealloc_at_states ( struct cardstate * cs )
{
struct at_state_t * cur , * next ;
list_for_each_entry_safe ( cur , next , & cs - > temp_at_states , list ) {
list_del ( & cur - > list ) ;
free_strings ( cur ) ;
kfree ( cur ) ;
}
}
static void gigaset_freebcs ( struct bc_state * bcs )
{
int i ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " freeing bcs[%d]->hw " , bcs - > channel ) ;
2006-03-26 13:38:29 +04:00
if ( ! bcs - > cs - > ops - > freebcshw ( bcs ) ) {
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " failed " ) ;
2006-03-26 13:38:29 +04:00
}
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " clearing bcs[%d]->at_state " , bcs - > channel ) ;
2006-03-26 13:38:29 +04:00
clear_at_state ( & bcs - > at_state ) ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " freeing bcs[%d]->skb " , bcs - > channel ) ;
2006-03-26 13:38:29 +04:00
if ( bcs - > skb )
dev_kfree_skb ( bcs - > skb ) ;
for ( i = 0 ; i < AT_NUM ; + + i ) {
kfree ( bcs - > commands [ i ] ) ;
bcs - > commands [ i ] = NULL ;
}
}
2006-04-11 09:55:14 +04:00
static struct cardstate * alloc_cs ( struct gigaset_driver * drv )
{
unsigned long flags ;
unsigned i ;
static struct cardstate * ret = NULL ;
spin_lock_irqsave ( & drv - > lock , flags ) ;
for ( i = 0 ; i < drv - > minors ; + + i ) {
if ( ! ( drv - > flags [ i ] & VALID_MINOR ) ) {
drv - > flags [ i ] = VALID_MINOR ;
ret = drv - > cs + i ;
}
if ( ret )
break ;
}
spin_unlock_irqrestore ( & drv - > lock , flags ) ;
return ret ;
}
static void free_cs ( struct cardstate * cs )
{
unsigned long flags ;
struct gigaset_driver * drv = cs - > driver ;
spin_lock_irqsave ( & drv - > lock , flags ) ;
drv - > flags [ cs - > minor_index ] = 0 ;
spin_unlock_irqrestore ( & drv - > lock , flags ) ;
}
static void make_valid ( struct cardstate * cs , unsigned mask )
{
unsigned long flags ;
struct gigaset_driver * drv = cs - > driver ;
spin_lock_irqsave ( & drv - > lock , flags ) ;
drv - > flags [ cs - > minor_index ] | = mask ;
spin_unlock_irqrestore ( & drv - > lock , flags ) ;
}
static void make_invalid ( struct cardstate * cs , unsigned mask )
{
unsigned long flags ;
struct gigaset_driver * drv = cs - > driver ;
spin_lock_irqsave ( & drv - > lock , flags ) ;
drv - > flags [ cs - > minor_index ] & = ~ mask ;
spin_unlock_irqrestore ( & drv - > lock , flags ) ;
}
2006-03-26 13:38:29 +04:00
void gigaset_freecs ( struct cardstate * cs )
{
int i ;
unsigned long flags ;
if ( ! cs )
return ;
2006-04-11 09:55:12 +04:00
mutex_lock ( & cs - > mutex ) ;
2006-03-26 13:38:29 +04:00
if ( ! cs - > bcs )
goto f_cs ;
if ( ! cs - > inbuf )
goto f_bcs ;
spin_lock_irqsave ( & cs - > lock , flags ) ;
2006-04-11 09:55:16 +04:00
cs - > running = 0 ;
2006-04-11 09:55:00 +04:00
spin_unlock_irqrestore ( & cs - > lock , flags ) ; /* event handler and timer are
not rescheduled below */
2006-03-26 13:38:29 +04:00
tasklet_kill ( & cs - > event_tasklet ) ;
del_timer_sync ( & cs - > timer ) ;
switch ( cs - > cs_init ) {
default :
gigaset_if_free ( cs ) ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " clearing hw " ) ;
2006-03-26 13:38:29 +04:00
cs - > ops - > freecshw ( cs ) ;
//FIXME cmdbuf
/* fall through */
case 2 : /* error in initcshw */
/* Deregister from LL */
make_invalid ( cs , VALID_ID ) ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " clearing iif " ) ;
2006-03-26 13:38:29 +04:00
gigaset_i4l_cmd ( cs , ISDN_STAT_UNLOAD ) ;
/* fall through */
case 1 : /* error when regestering to LL */
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " clearing at_state " ) ;
2006-03-26 13:38:29 +04:00
clear_at_state ( & cs - > at_state ) ;
dealloc_at_states ( cs ) ;
/* fall through */
case 0 : /* error in one call to initbcs */
for ( i = 0 ; i < cs - > channels ; + + i ) {
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " clearing bcs[%d] " , i ) ;
2006-03-26 13:38:29 +04:00
gigaset_freebcs ( cs - > bcs + i ) ;
}
clear_events ( cs ) ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " freeing inbuf " ) ;
2006-03-26 13:38:29 +04:00
kfree ( cs - > inbuf ) ;
}
2006-04-11 09:55:04 +04:00
f_bcs : gig_dbg ( DEBUG_INIT , " freeing bcs[] " ) ;
2006-03-26 13:38:29 +04:00
kfree ( cs - > bcs ) ;
2006-04-11 09:55:04 +04:00
f_cs : gig_dbg ( DEBUG_INIT , " freeing cs " ) ;
2006-04-11 09:55:12 +04:00
mutex_unlock ( & cs - > mutex ) ;
2006-03-26 13:38:29 +04:00
free_cs ( cs ) ;
}
EXPORT_SYMBOL_GPL ( gigaset_freecs ) ;
void gigaset_at_init ( struct at_state_t * at_state , struct bc_state * bcs ,
2006-04-11 09:55:04 +04:00
struct cardstate * cs , int cid )
2006-03-26 13:38:29 +04:00
{
int i ;
INIT_LIST_HEAD ( & at_state - > list ) ;
at_state - > waiting = 0 ;
at_state - > getstring = 0 ;
at_state - > pending_commands = 0 ;
at_state - > timer_expires = 0 ;
at_state - > timer_active = 0 ;
2006-04-11 09:55:16 +04:00
at_state - > timer_index = 0 ;
at_state - > seq_index = 0 ;
2006-03-26 13:38:29 +04:00
at_state - > ConState = 0 ;
for ( i = 0 ; i < STR_NUM ; + + i )
at_state - > str_var [ i ] = NULL ;
at_state - > int_var [ VAR_ZDLE ] = 0 ;
at_state - > int_var [ VAR_ZCTP ] = - 1 ;
at_state - > int_var [ VAR_ZSAU ] = ZSAU_NULL ;
at_state - > cs = cs ;
at_state - > bcs = bcs ;
at_state - > cid = cid ;
if ( ! cid )
at_state - > replystruct = cs - > tabnocid ;
else
at_state - > replystruct = cs - > tabcid ;
}
static void gigaset_inbuf_init ( struct inbuf_t * inbuf , struct bc_state * bcs ,
2006-04-11 09:55:04 +04:00
struct cardstate * cs , int inputstate )
2006-03-26 13:38:29 +04:00
/* inbuf->read must be allocated before! */
{
atomic_set ( & inbuf - > head , 0 ) ;
atomic_set ( & inbuf - > tail , 0 ) ;
inbuf - > cs = cs ;
inbuf - > bcs = bcs ; /*base driver: NULL*/
inbuf - > rcvbuf = NULL ; //FIXME
inbuf - > inputstate = inputstate ;
}
2006-04-11 09:55:09 +04:00
/* append received bytes to inbuf */
int gigaset_fill_inbuf ( struct inbuf_t * inbuf , const unsigned char * src ,
unsigned numbytes )
{
unsigned n , head , tail , bytesleft ;
gig_dbg ( DEBUG_INTR , " received %u bytes " , numbytes ) ;
if ( ! numbytes )
return 0 ;
bytesleft = numbytes ;
tail = atomic_read ( & inbuf - > tail ) ;
head = atomic_read ( & inbuf - > head ) ;
gig_dbg ( DEBUG_INTR , " buffer state: %u -> %u " , head , tail ) ;
while ( bytesleft ) {
if ( head > tail )
n = head - 1 - tail ;
else if ( head = = 0 )
n = ( RBUFSIZE - 1 ) - tail ;
else
n = RBUFSIZE - tail ;
if ( ! n ) {
dev_err ( inbuf - > cs - > dev ,
" buffer overflow (%u bytes lost) " , bytesleft ) ;
break ;
}
if ( n > bytesleft )
n = bytesleft ;
memcpy ( inbuf - > data + tail , src , n ) ;
bytesleft - = n ;
tail = ( tail + n ) % RBUFSIZE ;
src + = n ;
}
gig_dbg ( DEBUG_INTR , " setting tail to %u " , tail ) ;
atomic_set ( & inbuf - > tail , tail ) ;
return numbytes ! = bytesleft ;
}
EXPORT_SYMBOL_GPL ( gigaset_fill_inbuf ) ;
2006-03-26 13:38:29 +04:00
/* Initialize the b-channel structure */
static struct bc_state * gigaset_initbcs ( struct bc_state * bcs ,
2006-04-11 09:55:04 +04:00
struct cardstate * cs , int channel )
2006-03-26 13:38:29 +04:00
{
int i ;
bcs - > tx_skb = NULL ; //FIXME -> hw part
skb_queue_head_init ( & bcs - > squeue ) ;
bcs - > corrupted = 0 ;
bcs - > trans_down = 0 ;
bcs - > trans_up = 0 ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " setting up bcs[%d]->at_state " , channel ) ;
2006-03-26 13:38:29 +04:00
gigaset_at_init ( & bcs - > at_state , bcs , cs , - 1 ) ;
bcs - > rcvbytes = 0 ;
# ifdef CONFIG_GIGASET_DEBUG
bcs - > emptycount = 0 ;
# endif
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " allocating bcs[%d]->skb " , channel ) ;
2006-03-26 13:38:29 +04:00
bcs - > fcs = PPP_INITFCS ;
bcs - > inputstate = 0 ;
if ( cs - > ignoreframes ) {
bcs - > inputstate | = INS_skip_frame ;
bcs - > skb = NULL ;
} else if ( ( bcs - > skb = dev_alloc_skb ( SBUFSIZE + HW_HDR_LEN ) ) ! = NULL )
skb_reserve ( bcs - > skb , HW_HDR_LEN ) ;
else {
2006-04-11 09:55:04 +04:00
dev_warn ( cs - > dev , " could not allocate skb \n " ) ;
2006-03-26 13:38:29 +04:00
bcs - > inputstate | = INS_skip_frame ;
}
bcs - > channel = channel ;
bcs - > cs = cs ;
bcs - > chstate = 0 ;
bcs - > use_count = 1 ;
bcs - > busy = 0 ;
bcs - > ignore = cs - > ignoreframes ;
for ( i = 0 ; i < AT_NUM ; + + i )
bcs - > commands [ i ] = NULL ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " setting up bcs[%d]->hw " , channel ) ;
2006-03-26 13:38:29 +04:00
if ( cs - > ops - > initbcshw ( bcs ) )
return bcs ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " failed " ) ;
2006-03-26 13:38:29 +04:00
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " freeing bcs[%d]->skb " , channel ) ;
2006-03-26 13:38:29 +04:00
if ( bcs - > skb )
dev_kfree_skb ( bcs - > skb ) ;
return NULL ;
}
/* gigaset_initcs
* Allocate and initialize cardstate structure for Gigaset driver
* Calls hardware dependent gigaset_initcshw ( ) function
* Calls B channel initialization function gigaset_initbcs ( ) for each B channel
* parameters :
2006-04-11 09:55:04 +04:00
* drv hardware driver the device belongs to
2006-03-26 13:38:29 +04:00
* channels number of B channels supported by device
2006-04-11 09:55:00 +04:00
* onechannel ! = 0 : B channel data and AT commands share one
* communication channel
2006-03-26 13:38:29 +04:00
* = = 0 : B channels have separate communication channels
* ignoreframes number of frames to ignore after setting up B channel
* cidmode ! = 0 : start in CallID mode
* modulename name of driver module ( used for I4L registration )
* return value :
* pointer to cardstate structure
*/
struct cardstate * gigaset_initcs ( struct gigaset_driver * drv , int channels ,
int onechannel , int ignoreframes ,
int cidmode , const char * modulename )
{
struct cardstate * cs = NULL ;
2006-04-11 09:55:16 +04:00
unsigned long flags ;
2006-03-26 13:38:29 +04:00
int i ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " allocating cs " ) ;
2006-03-26 13:38:29 +04:00
cs = alloc_cs ( drv ) ;
if ( ! cs )
goto error ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " allocating bcs[0..%d] " , channels - 1 ) ;
2006-03-26 13:38:29 +04:00
cs - > bcs = kmalloc ( channels * sizeof ( struct bc_state ) , GFP_KERNEL ) ;
if ( ! cs - > bcs )
goto error ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " allocating inbuf " ) ;
2006-03-26 13:38:29 +04:00
cs - > inbuf = kmalloc ( sizeof ( struct inbuf_t ) , GFP_KERNEL ) ;
if ( ! cs - > inbuf )
goto error ;
cs - > cs_init = 0 ;
cs - > channels = channels ;
cs - > onechannel = onechannel ;
cs - > ignoreframes = ignoreframes ;
INIT_LIST_HEAD ( & cs - > temp_at_states ) ;
2006-04-11 09:55:16 +04:00
cs - > running = 0 ;
2006-03-26 13:38:29 +04:00
init_timer ( & cs - > timer ) ; /* clear next & prev */
spin_lock_init ( & cs - > ev_lock ) ;
2006-04-11 09:55:16 +04:00
cs - > ev_tail = 0 ;
cs - > ev_head = 0 ;
2006-04-11 09:55:12 +04:00
mutex_init ( & cs - > mutex ) ;
mutex_lock ( & cs - > mutex ) ;
2006-04-11 09:55:00 +04:00
tasklet_init ( & cs - > event_tasklet , & gigaset_handle_event ,
( unsigned long ) cs ) ;
2006-03-26 13:38:29 +04:00
atomic_set ( & cs - > commands_pending , 0 ) ;
cs - > cur_at_seq = 0 ;
cs - > gotfwver = - 1 ;
cs - > open_count = 0 ;
2006-04-11 09:55:04 +04:00
cs - > dev = NULL ;
2006-03-26 13:38:29 +04:00
cs - > tty = NULL ;
2006-04-11 09:55:16 +04:00
cs - > cidmode = cidmode ! = 0 ;
2006-03-26 13:38:29 +04:00
//if(onechannel) { //FIXME
cs - > tabnocid = gigaset_tab_nocid_m10x ;
cs - > tabcid = gigaset_tab_cid_m10x ;
//} else {
// cs->tabnocid = gigaset_tab_nocid;
// cs->tabcid = gigaset_tab_cid;
//}
init_waitqueue_head ( & cs - > waitqueue ) ;
cs - > waiting = 0 ;
atomic_set ( & cs - > mode , M_UNKNOWN ) ;
atomic_set ( & cs - > mstate , MS_UNINITIALIZED ) ;
for ( i = 0 ; i < channels ; + + i ) {
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " setting up bcs[%d].read " , i ) ;
2006-03-26 13:38:29 +04:00
if ( ! gigaset_initbcs ( cs - > bcs + i , cs , i ) )
goto error ;
}
+ + cs - > cs_init ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " setting up at_state " ) ;
2006-03-26 13:38:29 +04:00
spin_lock_init ( & cs - > lock ) ;
gigaset_at_init ( & cs - > at_state , NULL , cs , 0 ) ;
cs - > dle = 0 ;
cs - > cbytes = 0 ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " setting up inbuf " ) ;
2006-03-26 13:38:29 +04:00
if ( onechannel ) { //FIXME distinction necessary?
gigaset_inbuf_init ( cs - > inbuf , cs - > bcs , cs , INS_command ) ;
} else
gigaset_inbuf_init ( cs - > inbuf , NULL , cs , INS_command ) ;
2006-04-11 09:55:16 +04:00
cs - > connected = 0 ;
cs - > isdn_up = 0 ;
2006-03-26 13:38:29 +04:00
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " setting up cmdbuf " ) ;
2006-03-26 13:38:29 +04:00
cs - > cmdbuf = cs - > lastcmdbuf = NULL ;
spin_lock_init ( & cs - > cmdlock ) ;
cs - > curlen = 0 ;
cs - > cmdbytes = 0 ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " setting up iif " ) ;
2006-03-26 13:38:29 +04:00
if ( ! gigaset_register_to_LL ( cs , modulename ) ) {
2006-04-11 09:55:04 +04:00
err ( " register_isdn failed " ) ;
2006-03-26 13:38:29 +04:00
goto error ;
}
make_valid ( cs , VALID_ID ) ;
+ + cs - > cs_init ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " setting up hw " ) ;
2006-03-26 13:38:29 +04:00
if ( ! cs - > ops - > initcshw ( cs ) )
goto error ;
+ + cs - > cs_init ;
gigaset_if_init ( cs ) ;
2006-04-11 09:55:16 +04:00
spin_lock_irqsave ( & cs - > lock , flags ) ;
cs - > running = 1 ;
spin_unlock_irqrestore ( & cs - > lock , flags ) ;
2006-04-11 09:55:03 +04:00
setup_timer ( & cs - > timer , timer_tick , ( unsigned long ) cs ) ;
cs - > timer . expires = jiffies + msecs_to_jiffies ( GIG_TICK ) ;
2006-03-26 13:38:29 +04:00
/* FIXME: can jiffies increase too much until the timer is added?
* Same problem ( ? ) with mod_timer ( ) in timer_tick ( ) . */
add_timer ( & cs - > timer ) ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " cs initialized " ) ;
2006-04-11 09:55:12 +04:00
mutex_unlock ( & cs - > mutex ) ;
2006-03-26 13:38:29 +04:00
return cs ;
error : if ( cs )
2006-04-11 09:55:12 +04:00
mutex_unlock ( & cs - > mutex ) ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_INIT , " failed " ) ;
2006-03-26 13:38:29 +04:00
gigaset_freecs ( cs ) ;
return NULL ;
}
EXPORT_SYMBOL_GPL ( gigaset_initcs ) ;
2006-04-11 09:55:00 +04:00
/* ReInitialize the b-channel structure */
/* e.g. called on hangup, disconnect */
2006-03-26 13:38:29 +04:00
void gigaset_bcs_reinit ( struct bc_state * bcs )
{
struct sk_buff * skb ;
struct cardstate * cs = bcs - > cs ;
unsigned long flags ;
while ( ( skb = skb_dequeue ( & bcs - > squeue ) ) ! = NULL )
dev_kfree_skb ( skb ) ;
2006-04-11 09:55:00 +04:00
spin_lock_irqsave ( & cs - > lock , flags ) ;
2006-03-26 13:38:29 +04:00
clear_at_state ( & bcs - > at_state ) ;
bcs - > at_state . ConState = 0 ;
bcs - > at_state . timer_active = 0 ;
bcs - > at_state . timer_expires = 0 ;
2006-04-11 09:55:04 +04:00
bcs - > at_state . cid = - 1 ; /* No CID defined */
2006-03-26 13:38:29 +04:00
spin_unlock_irqrestore ( & cs - > lock , flags ) ;
bcs - > inputstate = 0 ;
# ifdef CONFIG_GIGASET_DEBUG
bcs - > emptycount = 0 ;
# endif
bcs - > fcs = PPP_INITFCS ;
bcs - > chstate = 0 ;
bcs - > ignore = cs - > ignoreframes ;
if ( bcs - > ignore )
bcs - > inputstate | = INS_skip_frame ;
cs - > ops - > reinitbcshw ( bcs ) ;
}
static void cleanup_cs ( struct cardstate * cs )
{
struct cmdbuf_t * cb , * tcb ;
int i ;
unsigned long flags ;
spin_lock_irqsave ( & cs - > lock , flags ) ;
atomic_set ( & cs - > mode , M_UNKNOWN ) ;
atomic_set ( & cs - > mstate , MS_UNINITIALIZED ) ;
clear_at_state ( & cs - > at_state ) ;
dealloc_at_states ( cs ) ;
free_strings ( & cs - > at_state ) ;
gigaset_at_init ( & cs - > at_state , NULL , cs , 0 ) ;
kfree ( cs - > inbuf - > rcvbuf ) ;
cs - > inbuf - > rcvbuf = NULL ;
cs - > inbuf - > inputstate = INS_command ;
atomic_set ( & cs - > inbuf - > head , 0 ) ;
atomic_set ( & cs - > inbuf - > tail , 0 ) ;
cb = cs - > cmdbuf ;
while ( cb ) {
tcb = cb ;
cb = cb - > next ;
kfree ( tcb ) ;
}
cs - > cmdbuf = cs - > lastcmdbuf = NULL ;
cs - > curlen = 0 ;
cs - > cmdbytes = 0 ;
cs - > gotfwver = - 1 ;
cs - > dle = 0 ;
cs - > cur_at_seq = 0 ;
atomic_set ( & cs - > commands_pending , 0 ) ;
cs - > cbytes = 0 ;
spin_unlock_irqrestore ( & cs - > lock , flags ) ;
for ( i = 0 ; i < cs - > channels ; + + i ) {
gigaset_freebcs ( cs - > bcs + i ) ;
if ( ! gigaset_initbcs ( cs - > bcs + i , cs , i ) )
break ; //FIXME error handling
}
if ( cs - > waiting ) {
cs - > cmd_result = - ENODEV ;
cs - > waiting = 0 ;
wake_up_interruptible ( & cs - > waitqueue ) ;
}
}
int gigaset_start ( struct cardstate * cs )
{
2006-04-11 09:55:16 +04:00
unsigned long flags ;
2006-04-11 09:55:12 +04:00
if ( mutex_lock_interruptible ( & cs - > mutex ) )
2006-03-26 13:38:29 +04:00
return 0 ;
2006-04-11 09:55:16 +04:00
spin_lock_irqsave ( & cs - > lock , flags ) ;
cs - > connected = 1 ;
spin_unlock_irqrestore ( & cs - > lock , flags ) ;
2006-03-26 13:38:29 +04:00
if ( atomic_read ( & cs - > mstate ) ! = MS_LOCKED ) {
cs - > ops - > set_modem_ctrl ( cs , 0 , TIOCM_DTR | TIOCM_RTS ) ;
cs - > ops - > baud_rate ( cs , B115200 ) ;
cs - > ops - > set_line_ctrl ( cs , CS8 ) ;
cs - > control_state = TIOCM_DTR | TIOCM_RTS ;
} else {
//FIXME use some saved values?
}
cs - > waiting = 1 ;
if ( ! gigaset_add_event ( cs , & cs - > at_state , EV_START , NULL , 0 , NULL ) ) {
cs - > waiting = 0 ;
//FIXME what should we do?
goto error ;
}
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_CMD , " scheduling START " ) ;
2006-03-26 13:38:29 +04:00
gigaset_schedule_event ( cs ) ;
wait_event ( cs - > waitqueue , ! cs - > waiting ) ;
2006-04-11 09:55:07 +04:00
/* set up device sysfs */
gigaset_init_dev_sysfs ( cs ) ;
2006-04-11 09:55:12 +04:00
mutex_unlock ( & cs - > mutex ) ;
2006-03-26 13:38:29 +04:00
return 1 ;
error :
2006-04-11 09:55:12 +04:00
mutex_unlock ( & cs - > mutex ) ;
2006-03-26 13:38:29 +04:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( gigaset_start ) ;
void gigaset_shutdown ( struct cardstate * cs )
{
2006-04-11 09:55:12 +04:00
mutex_lock ( & cs - > mutex ) ;
2006-03-26 13:38:29 +04:00
cs - > waiting = 1 ;
if ( ! gigaset_add_event ( cs , & cs - > at_state , EV_SHUTDOWN , NULL , 0 , NULL ) ) {
//FIXME what should we do?
goto exit ;
}
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_CMD , " scheduling SHUTDOWN " ) ;
2006-03-26 13:38:29 +04:00
gigaset_schedule_event ( cs ) ;
if ( wait_event_interruptible ( cs - > waitqueue , ! cs - > waiting ) ) {
2006-04-11 09:55:04 +04:00
warn ( " %s: aborted " , __func__ ) ;
2006-03-26 13:38:29 +04:00
//FIXME
}
if ( atomic_read ( & cs - > mstate ) ! = MS_LOCKED ) {
//FIXME?
//gigaset_baud_rate(cs, B115200);
//gigaset_set_line_ctrl(cs, CS8);
//gigaset_set_modem_ctrl(cs, TIOCM_DTR|TIOCM_RTS, 0);
//cs->control_state = 0;
} else {
//FIXME use some saved values?
}
cleanup_cs ( cs ) ;
exit :
2006-04-11 09:55:12 +04:00
mutex_unlock ( & cs - > mutex ) ;
2006-03-26 13:38:29 +04:00
}
EXPORT_SYMBOL_GPL ( gigaset_shutdown ) ;
void gigaset_stop ( struct cardstate * cs )
{
2006-04-11 09:55:12 +04:00
mutex_lock ( & cs - > mutex ) ;
2006-03-26 13:38:29 +04:00
cs - > waiting = 1 ;
if ( ! gigaset_add_event ( cs , & cs - > at_state , EV_STOP , NULL , 0 , NULL ) ) {
//FIXME what should we do?
goto exit ;
}
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_CMD , " scheduling STOP " ) ;
2006-03-26 13:38:29 +04:00
gigaset_schedule_event ( cs ) ;
if ( wait_event_interruptible ( cs - > waitqueue , ! cs - > waiting ) ) {
2006-04-11 09:55:04 +04:00
warn ( " %s: aborted " , __func__ ) ;
2006-03-26 13:38:29 +04:00
//FIXME
}
2006-04-11 09:55:16 +04:00
/* clear device sysfs */
gigaset_free_dev_sysfs ( cs ) ;
2006-03-26 13:38:29 +04:00
cleanup_cs ( cs ) ;
exit :
2006-04-11 09:55:12 +04:00
mutex_unlock ( & cs - > mutex ) ;
2006-03-26 13:38:29 +04:00
}
EXPORT_SYMBOL_GPL ( gigaset_stop ) ;
static LIST_HEAD ( drivers ) ;
static spinlock_t driver_lock = SPIN_LOCK_UNLOCKED ;
struct cardstate * gigaset_get_cs_by_id ( int id )
{
unsigned long flags ;
static struct cardstate * ret = NULL ;
static struct cardstate * cs ;
struct gigaset_driver * drv ;
unsigned i ;
spin_lock_irqsave ( & driver_lock , flags ) ;
list_for_each_entry ( drv , & drivers , list ) {
spin_lock ( & drv - > lock ) ;
for ( i = 0 ; i < drv - > minors ; + + i ) {
if ( drv - > flags [ i ] & VALID_ID ) {
cs = drv - > cs + i ;
if ( cs - > myid = = id )
ret = cs ;
}
if ( ret )
break ;
}
spin_unlock ( & drv - > lock ) ;
if ( ret )
break ;
}
spin_unlock_irqrestore ( & driver_lock , flags ) ;
return ret ;
}
void gigaset_debugdrivers ( void )
{
unsigned long flags ;
static struct cardstate * cs ;
struct gigaset_driver * drv ;
unsigned i ;
spin_lock_irqsave ( & driver_lock , flags ) ;
list_for_each_entry ( drv , & drivers , list ) {
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_DRIVER , " driver %p " , drv ) ;
2006-03-26 13:38:29 +04:00
spin_lock ( & drv - > lock ) ;
for ( i = 0 ; i < drv - > minors ; + + i ) {
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_DRIVER , " index %u " , i ) ;
gig_dbg ( DEBUG_DRIVER , " flags 0x%02x " ,
drv - > flags [ i ] ) ;
2006-03-26 13:38:29 +04:00
cs = drv - > cs + i ;
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_DRIVER , " cardstate %p " , cs ) ;
gig_dbg ( DEBUG_DRIVER , " minor_index %u " ,
cs - > minor_index ) ;
gig_dbg ( DEBUG_DRIVER , " driver %p " , cs - > driver ) ;
gig_dbg ( DEBUG_DRIVER , " i4l id %d " , cs - > myid ) ;
2006-03-26 13:38:29 +04:00
}
spin_unlock ( & drv - > lock ) ;
}
spin_unlock_irqrestore ( & driver_lock , flags ) ;
}
EXPORT_SYMBOL_GPL ( gigaset_debugdrivers ) ;
struct cardstate * gigaset_get_cs_by_tty ( struct tty_struct * tty )
{
if ( tty - > index < 0 | | tty - > index > = tty - > driver - > num )
return NULL ;
return gigaset_get_cs_by_minor ( tty - > index + tty - > driver - > minor_start ) ;
}
struct cardstate * gigaset_get_cs_by_minor ( unsigned minor )
{
unsigned long flags ;
static struct cardstate * ret = NULL ;
struct gigaset_driver * drv ;
unsigned index ;
spin_lock_irqsave ( & driver_lock , flags ) ;
list_for_each_entry ( drv , & drivers , list ) {
if ( minor < drv - > minor | | minor > = drv - > minor + drv - > minors )
continue ;
index = minor - drv - > minor ;
spin_lock ( & drv - > lock ) ;
if ( drv - > flags [ index ] & VALID_MINOR )
ret = drv - > cs + index ;
spin_unlock ( & drv - > lock ) ;
if ( ret )
break ;
}
spin_unlock_irqrestore ( & driver_lock , flags ) ;
return ret ;
}
void gigaset_freedriver ( struct gigaset_driver * drv )
{
unsigned long flags ;
spin_lock_irqsave ( & driver_lock , flags ) ;
list_del ( & drv - > list ) ;
spin_unlock_irqrestore ( & driver_lock , flags ) ;
gigaset_if_freedriver ( drv ) ;
module_put ( drv - > owner ) ;
kfree ( drv - > cs ) ;
kfree ( drv - > flags ) ;
kfree ( drv ) ;
}
EXPORT_SYMBOL_GPL ( gigaset_freedriver ) ;
/* gigaset_initdriver
* Allocate and initialize gigaset_driver structure . Initialize interface .
* parameters :
2006-04-11 09:55:04 +04:00
* minor First minor number
* minors Number of minors this driver can handle
* procname Name of the driver
* devname Name of the device files ( prefix without minor number )
* devfsname Devfs name of the device files without % d
2006-03-26 13:38:29 +04:00
* return value :
2006-04-11 09:55:04 +04:00
* Pointer to the gigaset_driver structure on success , NULL on failure .
2006-03-26 13:38:29 +04:00
*/
struct gigaset_driver * gigaset_initdriver ( unsigned minor , unsigned minors ,
2006-04-11 09:55:04 +04:00
const char * procname ,
const char * devname ,
const char * devfsname ,
const struct gigaset_ops * ops ,
struct module * owner )
2006-03-26 13:38:29 +04:00
{
struct gigaset_driver * drv ;
unsigned long flags ;
unsigned i ;
drv = kmalloc ( sizeof * drv , GFP_KERNEL ) ;
if ( ! drv )
return NULL ;
if ( ! try_module_get ( owner ) )
return NULL ;
drv - > cs = NULL ;
drv - > have_tty = 0 ;
drv - > minor = minor ;
drv - > minors = minors ;
spin_lock_init ( & drv - > lock ) ;
drv - > blocked = 0 ;
drv - > ops = ops ;
drv - > owner = owner ;
INIT_LIST_HEAD ( & drv - > list ) ;
drv - > cs = kmalloc ( minors * sizeof * drv - > cs , GFP_KERNEL ) ;
if ( ! drv - > cs )
goto out1 ;
drv - > flags = kmalloc ( minors * sizeof * drv - > flags , GFP_KERNEL ) ;
if ( ! drv - > flags )
goto out2 ;
for ( i = 0 ; i < minors ; + + i ) {
drv - > flags [ i ] = 0 ;
drv - > cs [ i ] . driver = drv ;
drv - > cs [ i ] . ops = drv - > ops ;
drv - > cs [ i ] . minor_index = i ;
}
gigaset_if_initdriver ( drv , procname , devname , devfsname ) ;
spin_lock_irqsave ( & driver_lock , flags ) ;
list_add ( & drv - > list , & drivers ) ;
spin_unlock_irqrestore ( & driver_lock , flags ) ;
return drv ;
out2 :
kfree ( drv - > cs ) ;
out1 :
kfree ( drv ) ;
module_put ( owner ) ;
return NULL ;
}
EXPORT_SYMBOL_GPL ( gigaset_initdriver ) ;
/* For drivers without fixed assignment device<->cardstate (usb) */
struct cardstate * gigaset_getunassignedcs ( struct gigaset_driver * drv )
{
unsigned long flags ;
struct cardstate * cs = NULL ;
unsigned i ;
spin_lock_irqsave ( & drv - > lock , flags ) ;
if ( drv - > blocked )
goto exit ;
for ( i = 0 ; i < drv - > minors ; + + i ) {
if ( ( drv - > flags [ i ] & VALID_MINOR ) & &
! ( drv - > flags [ i ] & ASSIGNED ) ) {
drv - > flags [ i ] | = ASSIGNED ;
cs = drv - > cs + i ;
break ;
}
}
exit :
spin_unlock_irqrestore ( & drv - > lock , flags ) ;
return cs ;
}
EXPORT_SYMBOL_GPL ( gigaset_getunassignedcs ) ;
void gigaset_unassign ( struct cardstate * cs )
{
unsigned long flags ;
unsigned * minor_flags ;
struct gigaset_driver * drv ;
if ( ! cs )
return ;
drv = cs - > driver ;
spin_lock_irqsave ( & drv - > lock , flags ) ;
minor_flags = drv - > flags + cs - > minor_index ;
if ( * minor_flags & VALID_MINOR )
* minor_flags & = ~ ASSIGNED ;
spin_unlock_irqrestore ( & drv - > lock , flags ) ;
}
EXPORT_SYMBOL_GPL ( gigaset_unassign ) ;
void gigaset_blockdriver ( struct gigaset_driver * drv )
{
unsigned long flags ;
spin_lock_irqsave ( & drv - > lock , flags ) ;
drv - > blocked = 1 ;
spin_unlock_irqrestore ( & drv - > lock , flags ) ;
}
EXPORT_SYMBOL_GPL ( gigaset_blockdriver ) ;
static int __init gigaset_init_module ( void )
{
/* in accordance with the principle of least astonishment,
* setting the ' debug ' parameter to 1 activates a sensible
* set of default debug levels
*/
if ( gigaset_debuglevel = = 1 )
gigaset_debuglevel = DEBUG_DEFAULT ;
info ( DRIVER_AUTHOR ) ;
info ( DRIVER_DESC ) ;
return 0 ;
}
static void __exit gigaset_exit_module ( void )
{
}
module_init ( gigaset_init_module ) ;
module_exit ( gigaset_exit_module ) ;
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;