2005-04-17 02:20:36 +04:00
/*
* linux / drivers / s390 / net / lcs . c
*
* Linux for S / 390 Lan Channel Station Network Driver
*
* Copyright ( C ) 1999 - 2001 IBM Deutschland Entwicklung GmbH ,
* IBM Corporation
* Author ( s ) : Original Code written by
* DJ Barrow ( djbarrow @ de . ibm . com , barrow_dj @ yahoo . com )
* Rewritten by
2005-11-10 15:51:42 +03:00
* Frank Pavlic ( fpavlic @ de . ibm . com ) and
2005-04-17 02:20:36 +04:00
* Martin Schwidefsky < schwidefsky @ de . ibm . com >
*
* 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 , or ( at your option )
* any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
2008-12-25 15:39:33 +03:00
# define KMSG_COMPONENT "lcs"
# define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
2005-04-17 02:20:36 +04:00
# include <linux/module.h>
# include <linux/if.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/trdevice.h>
# include <linux/fddidevice.h>
# include <linux/inetdevice.h>
# include <linux/in.h>
# include <linux/igmp.h>
# include <linux/delay.h>
2009-03-24 06:27:43 +03:00
# include <linux/kthread.h>
2005-04-17 02:20:36 +04:00
# include <net/arp.h>
# include <net/ip.h>
# include <asm/debug.h>
# include <asm/idals.h>
# include <asm/timex.h>
# include <linux/device.h>
# include <asm/ccwgroup.h>
# include "lcs.h"
# include "cu3088.h"
# if !defined(CONFIG_NET_ETHERNET) && \
! defined ( CONFIG_TR ) & & ! defined ( CONFIG_FDDI )
# error Cannot compile lcs.c without some net devices switched on.
# endif
/**
* initialization string for output
*/
2006-02-01 14:06:31 +03:00
static char version [ ] __initdata = " LCS driver " ;
2005-04-17 02:20:36 +04:00
static char debug_buffer [ 255 ] ;
/**
* Some prototypes .
*/
static void lcs_tasklet ( unsigned long ) ;
2006-12-06 22:18:20 +03:00
static void lcs_start_kernel_thread ( struct work_struct * ) ;
2005-04-17 02:20:36 +04:00
static void lcs_get_frames_cb ( struct lcs_channel * , struct lcs_buffer * ) ;
2009-01-20 09:14:32 +03:00
# ifdef CONFIG_IP_MULTICAST
2005-04-17 02:20:36 +04:00
static int lcs_send_delipm ( struct lcs_card * , struct lcs_ipm_list * ) ;
2009-01-20 09:14:32 +03:00
# endif /* CONFIG_IP_MULTICAST */
2006-05-24 11:51:17 +04:00
static int lcs_recovery ( void * ptr ) ;
2005-04-17 02:20:36 +04:00
/**
* Debug Facility Stuff
*/
static debug_info_t * lcs_dbf_setup ;
static debug_info_t * lcs_dbf_trace ;
/**
* LCS Debug Facility functions
*/
static void
lcs_unregister_debug_facility ( void )
{
if ( lcs_dbf_setup )
debug_unregister ( lcs_dbf_setup ) ;
if ( lcs_dbf_trace )
debug_unregister ( lcs_dbf_trace ) ;
}
static int
lcs_register_debug_facility ( void )
{
2005-06-26 01:55:33 +04:00
lcs_dbf_setup = debug_register ( " lcs_setup " , 2 , 1 , 8 ) ;
2008-02-08 15:09:04 +03:00
lcs_dbf_trace = debug_register ( " lcs_trace " , 4 , 1 , 8 ) ;
2005-04-17 02:20:36 +04:00
if ( lcs_dbf_setup = = NULL | | lcs_dbf_trace = = NULL ) {
2008-12-25 15:39:33 +03:00
pr_err ( " Not enough memory for debug facility. \n " ) ;
2005-04-17 02:20:36 +04:00
lcs_unregister_debug_facility ( ) ;
return - ENOMEM ;
}
debug_register_view ( lcs_dbf_setup , & debug_hex_ascii_view ) ;
2006-02-07 19:04:36 +03:00
debug_set_level ( lcs_dbf_setup , 2 ) ;
2005-04-17 02:20:36 +04:00
debug_register_view ( lcs_dbf_trace , & debug_hex_ascii_view ) ;
2006-02-07 19:04:36 +03:00
debug_set_level ( lcs_dbf_trace , 2 ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/**
* Allocate io buffers .
*/
static int
lcs_alloc_channel ( struct lcs_channel * channel )
{
int cnt ;
LCS_DBF_TEXT ( 2 , setup , " ichalloc " ) ;
for ( cnt = 0 ; cnt < LCS_NUM_BUFFS ; cnt + + ) {
/* alloc memory fo iobuffer */
2006-03-24 14:15:31 +03:00
channel - > iob [ cnt ] . data =
kzalloc ( LCS_IOBUFFERSIZE , GFP_DMA | GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( channel - > iob [ cnt ] . data = = NULL )
break ;
2006-12-04 17:40:59 +03:00
channel - > iob [ cnt ] . state = LCS_BUF_STATE_EMPTY ;
2005-04-17 02:20:36 +04:00
}
if ( cnt < LCS_NUM_BUFFS ) {
/* Not all io buffers could be allocated. */
LCS_DBF_TEXT ( 2 , setup , " echalloc " ) ;
while ( cnt - - > 0 )
kfree ( channel - > iob [ cnt ] . data ) ;
return - ENOMEM ;
}
return 0 ;
}
/**
* Free io buffers .
*/
static void
lcs_free_channel ( struct lcs_channel * channel )
{
int cnt ;
LCS_DBF_TEXT ( 2 , setup , " ichfree " ) ;
for ( cnt = 0 ; cnt < LCS_NUM_BUFFS ; cnt + + ) {
2005-11-07 12:01:30 +03:00
kfree ( channel - > iob [ cnt ] . data ) ;
2005-04-17 02:20:36 +04:00
channel - > iob [ cnt ] . data = NULL ;
}
}
/*
* Cleanup channel .
*/
static void
lcs_cleanup_channel ( struct lcs_channel * channel )
{
LCS_DBF_TEXT ( 3 , setup , " cleanch " ) ;
/* Kill write channel tasklets. */
tasklet_kill ( & channel - > irq_tasklet ) ;
/* Free channel buffers. */
lcs_free_channel ( channel ) ;
}
/**
* LCS free memory for card and channels .
*/
static void
lcs_free_card ( struct lcs_card * card )
{
LCS_DBF_TEXT ( 2 , setup , " remcard " ) ;
LCS_DBF_HEX ( 2 , setup , & card , sizeof ( void * ) ) ;
kfree ( card ) ;
}
/**
* LCS alloc memory for card and channels
*/
static struct lcs_card *
lcs_alloc_card ( void )
{
struct lcs_card * card ;
int rc ;
LCS_DBF_TEXT ( 2 , setup , " alloclcs " ) ;
2006-03-24 14:15:31 +03:00
card = kzalloc ( sizeof ( struct lcs_card ) , GFP_KERNEL | GFP_DMA ) ;
2005-04-17 02:20:36 +04:00
if ( card = = NULL )
return NULL ;
card - > lan_type = LCS_FRAME_TYPE_AUTO ;
card - > pkt_seq = 0 ;
card - > lancmd_timeout = LCS_LANCMD_TIMEOUT_DEFAULT ;
/* Allocate io buffers for the read channel. */
rc = lcs_alloc_channel ( & card - > read ) ;
if ( rc ) {
LCS_DBF_TEXT ( 2 , setup , " iccwerr " ) ;
lcs_free_card ( card ) ;
return NULL ;
}
/* Allocate io buffers for the write channel. */
rc = lcs_alloc_channel ( & card - > write ) ;
if ( rc ) {
LCS_DBF_TEXT ( 2 , setup , " iccwerr " ) ;
lcs_cleanup_channel ( & card - > read ) ;
lcs_free_card ( card ) ;
return NULL ;
}
# ifdef CONFIG_IP_MULTICAST
INIT_LIST_HEAD ( & card - > ipm_list ) ;
# endif
LCS_DBF_HEX ( 2 , setup , & card , sizeof ( void * ) ) ;
return card ;
}
/*
* Setup read channel .
*/
static void
lcs_setup_read_ccws ( struct lcs_card * card )
{
int cnt ;
LCS_DBF_TEXT ( 2 , setup , " ireadccw " ) ;
/* Setup read ccws. */
memset ( card - > read . ccws , 0 , sizeof ( struct ccw1 ) * ( LCS_NUM_BUFFS + 1 ) ) ;
for ( cnt = 0 ; cnt < LCS_NUM_BUFFS ; cnt + + ) {
card - > read . ccws [ cnt ] . cmd_code = LCS_CCW_READ ;
card - > read . ccws [ cnt ] . count = LCS_IOBUFFERSIZE ;
card - > read . ccws [ cnt ] . flags =
CCW_FLAG_CC | CCW_FLAG_SLI | CCW_FLAG_PCI ;
/*
* Note : we have allocated the buffer with GFP_DMA , so
* we do not need to do set_normalized_cda .
*/
card - > read . ccws [ cnt ] . cda =
( __u32 ) __pa ( card - > read . iob [ cnt ] . data ) ;
( ( struct lcs_header * )
card - > read . iob [ cnt ] . data ) - > offset = LCS_ILLEGAL_OFFSET ;
card - > read . iob [ cnt ] . callback = lcs_get_frames_cb ;
2006-12-04 17:40:59 +03:00
card - > read . iob [ cnt ] . state = LCS_BUF_STATE_READY ;
2005-04-17 02:20:36 +04:00
card - > read . iob [ cnt ] . count = LCS_IOBUFFERSIZE ;
}
card - > read . ccws [ 0 ] . flags & = ~ CCW_FLAG_PCI ;
card - > read . ccws [ LCS_NUM_BUFFS - 1 ] . flags & = ~ CCW_FLAG_PCI ;
card - > read . ccws [ LCS_NUM_BUFFS - 1 ] . flags | = CCW_FLAG_SUSPEND ;
/* Last ccw is a tic (transfer in channel). */
card - > read . ccws [ LCS_NUM_BUFFS ] . cmd_code = LCS_CCW_TRANSFER ;
card - > read . ccws [ LCS_NUM_BUFFS ] . cda =
( __u32 ) __pa ( card - > read . ccws ) ;
/* Setg initial state of the read channel. */
2006-12-04 17:40:59 +03:00
card - > read . state = LCS_CH_STATE_INIT ;
2005-04-17 02:20:36 +04:00
card - > read . io_idx = 0 ;
card - > read . buf_idx = 0 ;
}
static void
lcs_setup_read ( struct lcs_card * card )
{
LCS_DBF_TEXT ( 3 , setup , " initread " ) ;
lcs_setup_read_ccws ( card ) ;
/* Initialize read channel tasklet. */
card - > read . irq_tasklet . data = ( unsigned long ) & card - > read ;
card - > read . irq_tasklet . func = lcs_tasklet ;
/* Initialize waitqueue. */
init_waitqueue_head ( & card - > read . wait_q ) ;
}
/*
* Setup write channel .
*/
static void
lcs_setup_write_ccws ( struct lcs_card * card )
{
int cnt ;
LCS_DBF_TEXT ( 3 , setup , " iwritccw " ) ;
/* Setup write ccws. */
memset ( card - > write . ccws , 0 , sizeof ( struct ccw1 ) * LCS_NUM_BUFFS + 1 ) ;
for ( cnt = 0 ; cnt < LCS_NUM_BUFFS ; cnt + + ) {
card - > write . ccws [ cnt ] . cmd_code = LCS_CCW_WRITE ;
card - > write . ccws [ cnt ] . count = 0 ;
card - > write . ccws [ cnt ] . flags =
CCW_FLAG_SUSPEND | CCW_FLAG_CC | CCW_FLAG_SLI ;
/*
* Note : we have allocated the buffer with GFP_DMA , so
* we do not need to do set_normalized_cda .
*/
card - > write . ccws [ cnt ] . cda =
( __u32 ) __pa ( card - > write . iob [ cnt ] . data ) ;
}
/* Last ccw is a tic (transfer in channel). */
card - > write . ccws [ LCS_NUM_BUFFS ] . cmd_code = LCS_CCW_TRANSFER ;
card - > write . ccws [ LCS_NUM_BUFFS ] . cda =
( __u32 ) __pa ( card - > write . ccws ) ;
/* Set initial state of the write channel. */
2006-12-04 17:40:59 +03:00
card - > read . state = LCS_CH_STATE_INIT ;
2005-04-17 02:20:36 +04:00
card - > write . io_idx = 0 ;
card - > write . buf_idx = 0 ;
}
static void
lcs_setup_write ( struct lcs_card * card )
{
LCS_DBF_TEXT ( 3 , setup , " initwrit " ) ;
lcs_setup_write_ccws ( card ) ;
/* Initialize write channel tasklet. */
card - > write . irq_tasklet . data = ( unsigned long ) & card - > write ;
card - > write . irq_tasklet . func = lcs_tasklet ;
/* Initialize waitqueue. */
init_waitqueue_head ( & card - > write . wait_q ) ;
}
static void
lcs_set_allowed_threads ( struct lcs_card * card , unsigned long threads )
{
unsigned long flags ;
spin_lock_irqsave ( & card - > mask_lock , flags ) ;
card - > thread_allowed_mask = threads ;
spin_unlock_irqrestore ( & card - > mask_lock , flags ) ;
wake_up ( & card - > wait_q ) ;
}
static inline int
lcs_threads_running ( struct lcs_card * card , unsigned long threads )
{
unsigned long flags ;
int rc = 0 ;
spin_lock_irqsave ( & card - > mask_lock , flags ) ;
rc = ( card - > thread_running_mask & threads ) ;
spin_unlock_irqrestore ( & card - > mask_lock , flags ) ;
return rc ;
}
static int
lcs_wait_for_threads ( struct lcs_card * card , unsigned long threads )
{
return wait_event_interruptible ( card - > wait_q ,
lcs_threads_running ( card , threads ) = = 0 ) ;
}
static inline int
lcs_set_thread_start_bit ( struct lcs_card * card , unsigned long thread )
{
unsigned long flags ;
spin_lock_irqsave ( & card - > mask_lock , flags ) ;
if ( ! ( card - > thread_allowed_mask & thread ) | |
( card - > thread_start_mask & thread ) ) {
spin_unlock_irqrestore ( & card - > mask_lock , flags ) ;
return - EPERM ;
}
card - > thread_start_mask | = thread ;
spin_unlock_irqrestore ( & card - > mask_lock , flags ) ;
return 0 ;
}
static void
lcs_clear_thread_running_bit ( struct lcs_card * card , unsigned long thread )
{
unsigned long flags ;
spin_lock_irqsave ( & card - > mask_lock , flags ) ;
card - > thread_running_mask & = ~ thread ;
spin_unlock_irqrestore ( & card - > mask_lock , flags ) ;
wake_up ( & card - > wait_q ) ;
}
static inline int
__lcs_do_run_thread ( struct lcs_card * card , unsigned long thread )
{
unsigned long flags ;
int rc = 0 ;
spin_lock_irqsave ( & card - > mask_lock , flags ) ;
if ( card - > thread_start_mask & thread ) {
if ( ( card - > thread_allowed_mask & thread ) & &
! ( card - > thread_running_mask & thread ) ) {
rc = 1 ;
card - > thread_start_mask & = ~ thread ;
card - > thread_running_mask | = thread ;
} else
rc = - EPERM ;
}
spin_unlock_irqrestore ( & card - > mask_lock , flags ) ;
return rc ;
}
static int
lcs_do_run_thread ( struct lcs_card * card , unsigned long thread )
{
int rc = 0 ;
wait_event ( card - > wait_q ,
( rc = __lcs_do_run_thread ( card , thread ) ) > = 0 ) ;
return rc ;
}
static int
lcs_do_start_thread ( struct lcs_card * card , unsigned long thread )
{
unsigned long flags ;
int rc = 0 ;
spin_lock_irqsave ( & card - > mask_lock , flags ) ;
LCS_DBF_TEXT_ ( 4 , trace , " %02x%02x%02x " ,
( u8 ) card - > thread_start_mask ,
( u8 ) card - > thread_allowed_mask ,
( u8 ) card - > thread_running_mask ) ;
rc = ( card - > thread_start_mask & thread ) ;
spin_unlock_irqrestore ( & card - > mask_lock , flags ) ;
return rc ;
}
/**
* Initialize channels , card and state machines .
*/
static void
lcs_setup_card ( struct lcs_card * card )
{
LCS_DBF_TEXT ( 2 , setup , " initcard " ) ;
LCS_DBF_HEX ( 2 , setup , & card , sizeof ( void * ) ) ;
lcs_setup_read ( card ) ;
lcs_setup_write ( card ) ;
/* Set cards initial state. */
card - > state = DEV_STATE_DOWN ;
card - > tx_buffer = NULL ;
card - > tx_emitted = 0 ;
init_waitqueue_head ( & card - > wait_q ) ;
spin_lock_init ( & card - > lock ) ;
spin_lock_init ( & card - > ipm_lock ) ;
spin_lock_init ( & card - > mask_lock ) ;
# ifdef CONFIG_IP_MULTICAST
INIT_LIST_HEAD ( & card - > ipm_list ) ;
# endif
INIT_LIST_HEAD ( & card - > lancmd_waiters ) ;
}
static inline void
lcs_clear_multicast_list ( struct lcs_card * card )
{
# ifdef CONFIG_IP_MULTICAST
struct lcs_ipm_list * ipm ;
unsigned long flags ;
/* Free multicast list. */
LCS_DBF_TEXT ( 3 , setup , " clmclist " ) ;
spin_lock_irqsave ( & card - > ipm_lock , flags ) ;
while ( ! list_empty ( & card - > ipm_list ) ) {
ipm = list_entry ( card - > ipm_list . next ,
struct lcs_ipm_list , list ) ;
list_del ( & ipm - > list ) ;
if ( ipm - > ipm_state ! = LCS_IPM_STATE_SET_REQUIRED ) {
spin_unlock_irqrestore ( & card - > ipm_lock , flags ) ;
lcs_send_delipm ( card , ipm ) ;
spin_lock_irqsave ( & card - > ipm_lock , flags ) ;
}
kfree ( ipm ) ;
}
spin_unlock_irqrestore ( & card - > ipm_lock , flags ) ;
# endif
}
/**
* Cleanup channels , card and state machines .
*/
static void
lcs_cleanup_card ( struct lcs_card * card )
{
LCS_DBF_TEXT ( 3 , setup , " cleancrd " ) ;
LCS_DBF_HEX ( 2 , setup , & card , sizeof ( void * ) ) ;
if ( card - > dev ! = NULL )
free_netdev ( card - > dev ) ;
/* Cleanup channels. */
lcs_cleanup_channel ( & card - > write ) ;
lcs_cleanup_channel ( & card - > read ) ;
}
/**
* Start channel .
*/
static int
lcs_start_channel ( struct lcs_channel * channel )
{
unsigned long flags ;
int rc ;
2008-10-10 23:33:09 +04:00
LCS_DBF_TEXT_ ( 4 , trace , " ssch%s " , dev_name ( & channel - > ccwdev - > dev ) ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( get_ccwdev_lock ( channel - > ccwdev ) , flags ) ;
rc = ccw_device_start ( channel - > ccwdev ,
channel - > ccws + channel - > io_idx , 0 , 0 ,
DOIO_DENY_PREFETCH | DOIO_ALLOW_SUSPEND ) ;
if ( rc = = 0 )
2006-12-04 17:40:59 +03:00
channel - > state = LCS_CH_STATE_RUNNING ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( get_ccwdev_lock ( channel - > ccwdev ) , flags ) ;
if ( rc ) {
2008-10-10 23:33:09 +04:00
LCS_DBF_TEXT_ ( 4 , trace , " essh%s " ,
dev_name ( & channel - > ccwdev - > dev ) ) ;
2008-12-25 15:39:33 +03:00
dev_err ( & channel - > ccwdev - > dev ,
" Starting an LCS device resulted in an error, "
" rc=%d! \n " , rc ) ;
2005-04-17 02:20:36 +04:00
}
return rc ;
}
static int
lcs_clear_channel ( struct lcs_channel * channel )
{
unsigned long flags ;
int rc ;
LCS_DBF_TEXT ( 4 , trace , " clearch " ) ;
2008-10-10 23:33:09 +04:00
LCS_DBF_TEXT_ ( 4 , trace , " %s " , dev_name ( & channel - > ccwdev - > dev ) ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( get_ccwdev_lock ( channel - > ccwdev ) , flags ) ;
rc = ccw_device_clear ( channel - > ccwdev , ( addr_t ) channel ) ;
spin_unlock_irqrestore ( get_ccwdev_lock ( channel - > ccwdev ) , flags ) ;
if ( rc ) {
2008-10-10 23:33:09 +04:00
LCS_DBF_TEXT_ ( 4 , trace , " ecsc%s " ,
dev_name ( & channel - > ccwdev - > dev ) ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
2006-12-04 17:40:59 +03:00
wait_event ( channel - > wait_q , ( channel - > state = = LCS_CH_STATE_CLEARED ) ) ;
channel - > state = LCS_CH_STATE_STOPPED ;
2005-04-17 02:20:36 +04:00
return rc ;
}
/**
* Stop channel .
*/
static int
lcs_stop_channel ( struct lcs_channel * channel )
{
unsigned long flags ;
int rc ;
2006-12-04 17:40:59 +03:00
if ( channel - > state = = LCS_CH_STATE_STOPPED )
2005-04-17 02:20:36 +04:00
return 0 ;
LCS_DBF_TEXT ( 4 , trace , " haltsch " ) ;
2008-10-10 23:33:09 +04:00
LCS_DBF_TEXT_ ( 4 , trace , " %s " , dev_name ( & channel - > ccwdev - > dev ) ) ;
2006-12-04 17:40:59 +03:00
channel - > state = LCS_CH_STATE_INIT ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( get_ccwdev_lock ( channel - > ccwdev ) , flags ) ;
rc = ccw_device_halt ( channel - > ccwdev , ( addr_t ) channel ) ;
spin_unlock_irqrestore ( get_ccwdev_lock ( channel - > ccwdev ) , flags ) ;
if ( rc ) {
2008-10-10 23:33:09 +04:00
LCS_DBF_TEXT_ ( 4 , trace , " ehsc%s " ,
dev_name ( & channel - > ccwdev - > dev ) ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
/* Asynchronous halt initialted. Wait for its completion. */
2006-12-04 17:40:59 +03:00
wait_event ( channel - > wait_q , ( channel - > state = = LCS_CH_STATE_HALTED ) ) ;
2005-04-17 02:20:36 +04:00
lcs_clear_channel ( channel ) ;
return 0 ;
}
/**
* start read and write channel
*/
static int
lcs_start_channels ( struct lcs_card * card )
{
int rc ;
LCS_DBF_TEXT ( 2 , trace , " chstart " ) ;
/* start read channel */
rc = lcs_start_channel ( & card - > read ) ;
if ( rc )
return rc ;
/* start write channel */
rc = lcs_start_channel ( & card - > write ) ;
if ( rc )
lcs_stop_channel ( & card - > read ) ;
return rc ;
}
/**
* stop read and write channel
*/
static int
lcs_stop_channels ( struct lcs_card * card )
{
LCS_DBF_TEXT ( 2 , trace , " chhalt " ) ;
lcs_stop_channel ( & card - > read ) ;
lcs_stop_channel ( & card - > write ) ;
return 0 ;
}
/**
* Get empty buffer .
*/
static struct lcs_buffer *
__lcs_get_buffer ( struct lcs_channel * channel )
{
int index ;
LCS_DBF_TEXT ( 5 , trace , " _getbuff " ) ;
index = channel - > io_idx ;
do {
2006-12-04 17:40:59 +03:00
if ( channel - > iob [ index ] . state = = LCS_BUF_STATE_EMPTY ) {
channel - > iob [ index ] . state = LCS_BUF_STATE_LOCKED ;
2005-04-17 02:20:36 +04:00
return channel - > iob + index ;
}
index = ( index + 1 ) & ( LCS_NUM_BUFFS - 1 ) ;
} while ( index ! = channel - > io_idx ) ;
return NULL ;
}
static struct lcs_buffer *
lcs_get_buffer ( struct lcs_channel * channel )
{
struct lcs_buffer * buffer ;
unsigned long flags ;
LCS_DBF_TEXT ( 5 , trace , " getbuff " ) ;
spin_lock_irqsave ( get_ccwdev_lock ( channel - > ccwdev ) , flags ) ;
buffer = __lcs_get_buffer ( channel ) ;
spin_unlock_irqrestore ( get_ccwdev_lock ( channel - > ccwdev ) , flags ) ;
return buffer ;
}
/**
* Resume channel program if the channel is suspended .
*/
static int
__lcs_resume_channel ( struct lcs_channel * channel )
{
int rc ;
2006-12-04 17:40:59 +03:00
if ( channel - > state ! = LCS_CH_STATE_SUSPENDED )
2005-04-17 02:20:36 +04:00
return 0 ;
if ( channel - > ccws [ channel - > io_idx ] . flags & CCW_FLAG_SUSPEND )
return 0 ;
2008-10-10 23:33:09 +04:00
LCS_DBF_TEXT_ ( 5 , trace , " rsch%s " , dev_name ( & channel - > ccwdev - > dev ) ) ;
2005-04-17 02:20:36 +04:00
rc = ccw_device_resume ( channel - > ccwdev ) ;
if ( rc ) {
2008-10-10 23:33:09 +04:00
LCS_DBF_TEXT_ ( 4 , trace , " ersc%s " ,
dev_name ( & channel - > ccwdev - > dev ) ) ;
2008-12-25 15:39:33 +03:00
dev_err ( & channel - > ccwdev - > dev ,
" Sending data from the LCS device to the LAN failed "
" with rc=%d \n " , rc ) ;
2005-04-17 02:20:36 +04:00
} else
2006-12-04 17:40:59 +03:00
channel - > state = LCS_CH_STATE_RUNNING ;
2005-04-17 02:20:36 +04:00
return rc ;
}
/**
* Make a buffer ready for processing .
*/
static inline void
__lcs_ready_buffer_bits ( struct lcs_channel * channel , int index )
{
int prev , next ;
LCS_DBF_TEXT ( 5 , trace , " rdybits " ) ;
prev = ( index - 1 ) & ( LCS_NUM_BUFFS - 1 ) ;
next = ( index + 1 ) & ( LCS_NUM_BUFFS - 1 ) ;
/* Check if we may clear the suspend bit of this buffer. */
if ( channel - > ccws [ next ] . flags & CCW_FLAG_SUSPEND ) {
/* Check if we have to set the PCI bit. */
if ( ! ( channel - > ccws [ prev ] . flags & CCW_FLAG_SUSPEND ) )
/* Suspend bit of the previous buffer is not set. */
channel - > ccws [ index ] . flags | = CCW_FLAG_PCI ;
/* Suspend bit of the next buffer is set. */
channel - > ccws [ index ] . flags & = ~ CCW_FLAG_SUSPEND ;
}
}
static int
lcs_ready_buffer ( struct lcs_channel * channel , struct lcs_buffer * buffer )
{
unsigned long flags ;
int index , rc ;
LCS_DBF_TEXT ( 5 , trace , " rdybuff " ) ;
2006-12-04 17:40:59 +03:00
BUG_ON ( buffer - > state ! = LCS_BUF_STATE_LOCKED & &
buffer - > state ! = LCS_BUF_STATE_PROCESSED ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( get_ccwdev_lock ( channel - > ccwdev ) , flags ) ;
2006-12-04 17:40:59 +03:00
buffer - > state = LCS_BUF_STATE_READY ;
2005-04-17 02:20:36 +04:00
index = buffer - channel - > iob ;
/* Set length. */
channel - > ccws [ index ] . count = buffer - > count ;
/* Check relevant PCI/suspend bits. */
__lcs_ready_buffer_bits ( channel , index ) ;
rc = __lcs_resume_channel ( channel ) ;
spin_unlock_irqrestore ( get_ccwdev_lock ( channel - > ccwdev ) , flags ) ;
return rc ;
}
/**
* Mark the buffer as processed . Take care of the suspend bit
* of the previous buffer . This function is called from
* interrupt context , so the lock must not be taken .
*/
static int
__lcs_processed_buffer ( struct lcs_channel * channel , struct lcs_buffer * buffer )
{
int index , prev , next ;
LCS_DBF_TEXT ( 5 , trace , " prcsbuff " ) ;
2006-12-04 17:40:59 +03:00
BUG_ON ( buffer - > state ! = LCS_BUF_STATE_READY ) ;
buffer - > state = LCS_BUF_STATE_PROCESSED ;
2005-04-17 02:20:36 +04:00
index = buffer - channel - > iob ;
prev = ( index - 1 ) & ( LCS_NUM_BUFFS - 1 ) ;
next = ( index + 1 ) & ( LCS_NUM_BUFFS - 1 ) ;
/* Set the suspend bit and clear the PCI bit of this buffer. */
channel - > ccws [ index ] . flags | = CCW_FLAG_SUSPEND ;
channel - > ccws [ index ] . flags & = ~ CCW_FLAG_PCI ;
/* Check the suspend bit of the previous buffer. */
2006-12-04 17:40:59 +03:00
if ( channel - > iob [ prev ] . state = = LCS_BUF_STATE_READY ) {
2005-04-17 02:20:36 +04:00
/*
* Previous buffer is in state ready . It might have
* happened in lcs_ready_buffer that the suspend bit
* has not been cleared to avoid an endless loop .
* Do it now .
*/
__lcs_ready_buffer_bits ( channel , prev ) ;
}
/* Clear PCI bit of next buffer. */
channel - > ccws [ next ] . flags & = ~ CCW_FLAG_PCI ;
return __lcs_resume_channel ( channel ) ;
}
/**
* Put a processed buffer back to state empty .
*/
static void
lcs_release_buffer ( struct lcs_channel * channel , struct lcs_buffer * buffer )
{
unsigned long flags ;
LCS_DBF_TEXT ( 5 , trace , " relbuff " ) ;
2006-12-04 17:40:59 +03:00
BUG_ON ( buffer - > state ! = LCS_BUF_STATE_LOCKED & &
buffer - > state ! = LCS_BUF_STATE_PROCESSED ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( get_ccwdev_lock ( channel - > ccwdev ) , flags ) ;
2006-12-04 17:40:59 +03:00
buffer - > state = LCS_BUF_STATE_EMPTY ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( get_ccwdev_lock ( channel - > ccwdev ) , flags ) ;
}
/**
* Get buffer for a lan command .
*/
static struct lcs_buffer *
lcs_get_lancmd ( struct lcs_card * card , int count )
{
struct lcs_buffer * buffer ;
struct lcs_cmd * cmd ;
LCS_DBF_TEXT ( 4 , trace , " getlncmd " ) ;
/* Get buffer and wait if none is available. */
wait_event ( card - > write . wait_q ,
( ( buffer = lcs_get_buffer ( & card - > write ) ) ! = NULL ) ) ;
count + = sizeof ( struct lcs_header ) ;
* ( __u16 * ) ( buffer - > data + count ) = 0 ;
buffer - > count = count + sizeof ( __u16 ) ;
buffer - > callback = lcs_release_buffer ;
cmd = ( struct lcs_cmd * ) buffer - > data ;
cmd - > offset = count ;
cmd - > type = LCS_FRAME_TYPE_CONTROL ;
cmd - > slot = 0 ;
return buffer ;
}
static void
lcs_get_reply ( struct lcs_reply * reply )
{
WARN_ON ( atomic_read ( & reply - > refcnt ) < = 0 ) ;
atomic_inc ( & reply - > refcnt ) ;
}
static void
lcs_put_reply ( struct lcs_reply * reply )
{
WARN_ON ( atomic_read ( & reply - > refcnt ) < = 0 ) ;
if ( atomic_dec_and_test ( & reply - > refcnt ) ) {
kfree ( reply ) ;
}
}
static struct lcs_reply *
lcs_alloc_reply ( struct lcs_cmd * cmd )
{
struct lcs_reply * reply ;
LCS_DBF_TEXT ( 4 , trace , " getreply " ) ;
2006-03-24 14:15:31 +03:00
reply = kzalloc ( sizeof ( struct lcs_reply ) , GFP_ATOMIC ) ;
2005-04-17 02:20:36 +04:00
if ( ! reply )
return NULL ;
atomic_set ( & reply - > refcnt , 1 ) ;
reply - > sequence_no = cmd - > sequence_no ;
reply - > received = 0 ;
reply - > rc = 0 ;
init_waitqueue_head ( & reply - > wait_q ) ;
return reply ;
}
/**
* Notifier function for lancmd replies . Called from read irq .
*/
static void
lcs_notify_lancmd_waiters ( struct lcs_card * card , struct lcs_cmd * cmd )
{
struct list_head * l , * n ;
struct lcs_reply * reply ;
LCS_DBF_TEXT ( 4 , trace , " notiwait " ) ;
spin_lock ( & card - > lock ) ;
list_for_each_safe ( l , n , & card - > lancmd_waiters ) {
reply = list_entry ( l , struct lcs_reply , list ) ;
if ( reply - > sequence_no = = cmd - > sequence_no ) {
lcs_get_reply ( reply ) ;
list_del_init ( & reply - > list ) ;
if ( reply - > callback ! = NULL )
reply - > callback ( card , cmd ) ;
reply - > received = 1 ;
reply - > rc = cmd - > return_code ;
wake_up ( & reply - > wait_q ) ;
lcs_put_reply ( reply ) ;
break ;
}
}
spin_unlock ( & card - > lock ) ;
}
/**
* Emit buffer of a lan comand .
*/
2007-02-05 23:16:47 +03:00
static void
2005-04-17 02:20:36 +04:00
lcs_lancmd_timeout ( unsigned long data )
{
struct lcs_reply * reply , * list_reply , * r ;
unsigned long flags ;
LCS_DBF_TEXT ( 4 , trace , " timeout " ) ;
reply = ( struct lcs_reply * ) data ;
spin_lock_irqsave ( & reply - > card - > lock , flags ) ;
list_for_each_entry_safe ( list_reply , r ,
& reply - > card - > lancmd_waiters , list ) {
if ( reply = = list_reply ) {
lcs_get_reply ( reply ) ;
list_del_init ( & reply - > list ) ;
spin_unlock_irqrestore ( & reply - > card - > lock , flags ) ;
reply - > received = 1 ;
reply - > rc = - ETIME ;
wake_up ( & reply - > wait_q ) ;
lcs_put_reply ( reply ) ;
return ;
}
}
spin_unlock_irqrestore ( & reply - > card - > lock , flags ) ;
}
static int
lcs_send_lancmd ( struct lcs_card * card , struct lcs_buffer * buffer ,
void ( * reply_callback ) ( struct lcs_card * , struct lcs_cmd * ) )
{
struct lcs_reply * reply ;
struct lcs_cmd * cmd ;
struct timer_list timer ;
unsigned long flags ;
int rc ;
LCS_DBF_TEXT ( 4 , trace , " sendcmd " ) ;
cmd = ( struct lcs_cmd * ) buffer - > data ;
cmd - > return_code = 0 ;
cmd - > sequence_no = card - > sequence_no + + ;
reply = lcs_alloc_reply ( cmd ) ;
if ( ! reply )
return - ENOMEM ;
reply - > callback = reply_callback ;
reply - > card = card ;
spin_lock_irqsave ( & card - > lock , flags ) ;
list_add_tail ( & reply - > list , & card - > lancmd_waiters ) ;
spin_unlock_irqrestore ( & card - > lock , flags ) ;
buffer - > callback = lcs_release_buffer ;
rc = lcs_ready_buffer ( & card - > write , buffer ) ;
if ( rc )
return rc ;
init_timer ( & timer ) ;
timer . function = lcs_lancmd_timeout ;
timer . data = ( unsigned long ) reply ;
timer . expires = jiffies + HZ * card - > lancmd_timeout ;
add_timer ( & timer ) ;
wait_event ( reply - > wait_q , reply - > received ) ;
del_timer_sync ( & timer ) ;
LCS_DBF_TEXT_ ( 4 , trace , " rc:%d " , reply - > rc ) ;
rc = reply - > rc ;
lcs_put_reply ( reply ) ;
return rc ? - EIO : 0 ;
}
/**
* LCS startup command
*/
static int
lcs_send_startup ( struct lcs_card * card , __u8 initiator )
{
struct lcs_buffer * buffer ;
struct lcs_cmd * cmd ;
LCS_DBF_TEXT ( 2 , trace , " startup " ) ;
buffer = lcs_get_lancmd ( card , LCS_STD_CMD_SIZE ) ;
cmd = ( struct lcs_cmd * ) buffer - > data ;
cmd - > cmd_code = LCS_CMD_STARTUP ;
cmd - > initiator = initiator ;
cmd - > cmd . lcs_startup . buff_size = LCS_IOBUFFERSIZE ;
return lcs_send_lancmd ( card , buffer , NULL ) ;
}
/**
* LCS shutdown command
*/
static int
lcs_send_shutdown ( struct lcs_card * card )
{
struct lcs_buffer * buffer ;
struct lcs_cmd * cmd ;
LCS_DBF_TEXT ( 2 , trace , " shutdown " ) ;
buffer = lcs_get_lancmd ( card , LCS_STD_CMD_SIZE ) ;
cmd = ( struct lcs_cmd * ) buffer - > data ;
cmd - > cmd_code = LCS_CMD_SHUTDOWN ;
cmd - > initiator = LCS_INITIATOR_TCPIP ;
return lcs_send_lancmd ( card , buffer , NULL ) ;
}
/**
* LCS lanstat command
*/
static void
__lcs_lanstat_cb ( struct lcs_card * card , struct lcs_cmd * cmd )
{
LCS_DBF_TEXT ( 2 , trace , " statcb " ) ;
memcpy ( card - > mac , cmd - > cmd . lcs_lanstat_cmd . mac_addr , LCS_MAC_LENGTH ) ;
}
static int
lcs_send_lanstat ( struct lcs_card * card )
{
struct lcs_buffer * buffer ;
struct lcs_cmd * cmd ;
LCS_DBF_TEXT ( 2 , trace , " cmdstat " ) ;
buffer = lcs_get_lancmd ( card , LCS_STD_CMD_SIZE ) ;
cmd = ( struct lcs_cmd * ) buffer - > data ;
/* Setup lanstat command. */
cmd - > cmd_code = LCS_CMD_LANSTAT ;
cmd - > initiator = LCS_INITIATOR_TCPIP ;
cmd - > cmd . lcs_std_cmd . lan_type = card - > lan_type ;
cmd - > cmd . lcs_std_cmd . portno = card - > portno ;
return lcs_send_lancmd ( card , buffer , __lcs_lanstat_cb ) ;
}
/**
* send stoplan command
*/
static int
lcs_send_stoplan ( struct lcs_card * card , __u8 initiator )
{
struct lcs_buffer * buffer ;
struct lcs_cmd * cmd ;
LCS_DBF_TEXT ( 2 , trace , " cmdstpln " ) ;
buffer = lcs_get_lancmd ( card , LCS_STD_CMD_SIZE ) ;
cmd = ( struct lcs_cmd * ) buffer - > data ;
cmd - > cmd_code = LCS_CMD_STOPLAN ;
cmd - > initiator = initiator ;
cmd - > cmd . lcs_std_cmd . lan_type = card - > lan_type ;
cmd - > cmd . lcs_std_cmd . portno = card - > portno ;
return lcs_send_lancmd ( card , buffer , NULL ) ;
}
/**
* send startlan command
*/
static void
__lcs_send_startlan_cb ( struct lcs_card * card , struct lcs_cmd * cmd )
{
LCS_DBF_TEXT ( 2 , trace , " srtlancb " ) ;
card - > lan_type = cmd - > cmd . lcs_std_cmd . lan_type ;
card - > portno = cmd - > cmd . lcs_std_cmd . portno ;
}
static int
lcs_send_startlan ( struct lcs_card * card , __u8 initiator )
{
struct lcs_buffer * buffer ;
struct lcs_cmd * cmd ;
LCS_DBF_TEXT ( 2 , trace , " cmdstaln " ) ;
buffer = lcs_get_lancmd ( card , LCS_STD_CMD_SIZE ) ;
cmd = ( struct lcs_cmd * ) buffer - > data ;
cmd - > cmd_code = LCS_CMD_STARTLAN ;
cmd - > initiator = initiator ;
cmd - > cmd . lcs_std_cmd . lan_type = card - > lan_type ;
cmd - > cmd . lcs_std_cmd . portno = card - > portno ;
return lcs_send_lancmd ( card , buffer , __lcs_send_startlan_cb ) ;
}
# ifdef CONFIG_IP_MULTICAST
/**
* send setipm command ( Multicast )
*/
static int
lcs_send_setipm ( struct lcs_card * card , struct lcs_ipm_list * ipm_list )
{
struct lcs_buffer * buffer ;
struct lcs_cmd * cmd ;
LCS_DBF_TEXT ( 2 , trace , " cmdsetim " ) ;
buffer = lcs_get_lancmd ( card , LCS_MULTICAST_CMD_SIZE ) ;
cmd = ( struct lcs_cmd * ) buffer - > data ;
cmd - > cmd_code = LCS_CMD_SETIPM ;
cmd - > initiator = LCS_INITIATOR_TCPIP ;
cmd - > cmd . lcs_qipassist . lan_type = card - > lan_type ;
cmd - > cmd . lcs_qipassist . portno = card - > portno ;
cmd - > cmd . lcs_qipassist . version = 4 ;
cmd - > cmd . lcs_qipassist . num_ip_pairs = 1 ;
memcpy ( cmd - > cmd . lcs_qipassist . lcs_ipass_ctlmsg . ip_mac_pair ,
& ipm_list - > ipm , sizeof ( struct lcs_ip_mac_pair ) ) ;
LCS_DBF_TEXT_ ( 2 , trace , " %x " , ipm_list - > ipm . ip_addr ) ;
return lcs_send_lancmd ( card , buffer , NULL ) ;
}
/**
* send delipm command ( Multicast )
*/
static int
lcs_send_delipm ( struct lcs_card * card , struct lcs_ipm_list * ipm_list )
{
struct lcs_buffer * buffer ;
struct lcs_cmd * cmd ;
LCS_DBF_TEXT ( 2 , trace , " cmddelim " ) ;
buffer = lcs_get_lancmd ( card , LCS_MULTICAST_CMD_SIZE ) ;
cmd = ( struct lcs_cmd * ) buffer - > data ;
cmd - > cmd_code = LCS_CMD_DELIPM ;
cmd - > initiator = LCS_INITIATOR_TCPIP ;
cmd - > cmd . lcs_qipassist . lan_type = card - > lan_type ;
cmd - > cmd . lcs_qipassist . portno = card - > portno ;
cmd - > cmd . lcs_qipassist . version = 4 ;
cmd - > cmd . lcs_qipassist . num_ip_pairs = 1 ;
memcpy ( cmd - > cmd . lcs_qipassist . lcs_ipass_ctlmsg . ip_mac_pair ,
& ipm_list - > ipm , sizeof ( struct lcs_ip_mac_pair ) ) ;
LCS_DBF_TEXT_ ( 2 , trace , " %x " , ipm_list - > ipm . ip_addr ) ;
return lcs_send_lancmd ( card , buffer , NULL ) ;
}
/**
* check if multicast is supported by LCS
*/
static void
__lcs_check_multicast_cb ( struct lcs_card * card , struct lcs_cmd * cmd )
{
LCS_DBF_TEXT ( 2 , trace , " chkmccb " ) ;
card - > ip_assists_supported =
cmd - > cmd . lcs_qipassist . ip_assists_supported ;
card - > ip_assists_enabled =
cmd - > cmd . lcs_qipassist . ip_assists_enabled ;
}
static int
lcs_check_multicast_support ( struct lcs_card * card )
{
struct lcs_buffer * buffer ;
struct lcs_cmd * cmd ;
int rc ;
LCS_DBF_TEXT ( 2 , trace , " cmdqipa " ) ;
/* Send query ipassist. */
buffer = lcs_get_lancmd ( card , LCS_STD_CMD_SIZE ) ;
cmd = ( struct lcs_cmd * ) buffer - > data ;
cmd - > cmd_code = LCS_CMD_QIPASSIST ;
cmd - > initiator = LCS_INITIATOR_TCPIP ;
cmd - > cmd . lcs_qipassist . lan_type = card - > lan_type ;
cmd - > cmd . lcs_qipassist . portno = card - > portno ;
cmd - > cmd . lcs_qipassist . version = 4 ;
cmd - > cmd . lcs_qipassist . num_ip_pairs = 1 ;
rc = lcs_send_lancmd ( card , buffer , __lcs_check_multicast_cb ) ;
if ( rc ! = 0 ) {
2008-12-25 15:39:33 +03:00
pr_err ( " Query IPAssist failed. Assuming unsupported! \n " ) ;
2005-04-17 02:20:36 +04:00
return - EOPNOTSUPP ;
}
if ( card - > ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT )
return 0 ;
return - EOPNOTSUPP ;
}
/**
* set or del multicast address on LCS card
*/
static void
lcs_fix_multicast_list ( struct lcs_card * card )
{
struct list_head failed_list ;
struct lcs_ipm_list * ipm , * tmp ;
unsigned long flags ;
int rc ;
LCS_DBF_TEXT ( 4 , trace , " fixipm " ) ;
INIT_LIST_HEAD ( & failed_list ) ;
spin_lock_irqsave ( & card - > ipm_lock , flags ) ;
list_modified :
list_for_each_entry_safe ( ipm , tmp , & card - > ipm_list , list ) {
switch ( ipm - > ipm_state ) {
case LCS_IPM_STATE_SET_REQUIRED :
/* del from ipm_list so noone else can tamper with
* this entry */
list_del_init ( & ipm - > list ) ;
spin_unlock_irqrestore ( & card - > ipm_lock , flags ) ;
rc = lcs_send_setipm ( card , ipm ) ;
spin_lock_irqsave ( & card - > ipm_lock , flags ) ;
if ( rc ) {
2008-12-25 15:39:33 +03:00
pr_info ( " Adding multicast address failed. "
" Table possibly full! \n " ) ;
2005-04-17 02:20:36 +04:00
/* store ipm in failed list -> will be added
* to ipm_list again , so a retry will be done
* during the next call of this function */
list_add_tail ( & ipm - > list , & failed_list ) ;
} else {
ipm - > ipm_state = LCS_IPM_STATE_ON_CARD ;
/* re-insert into ipm_list */
list_add_tail ( & ipm - > list , & card - > ipm_list ) ;
}
goto list_modified ;
case LCS_IPM_STATE_DEL_REQUIRED :
list_del ( & ipm - > list ) ;
spin_unlock_irqrestore ( & card - > ipm_lock , flags ) ;
lcs_send_delipm ( card , ipm ) ;
spin_lock_irqsave ( & card - > ipm_lock , flags ) ;
kfree ( ipm ) ;
goto list_modified ;
case LCS_IPM_STATE_ON_CARD :
break ;
}
}
/* re-insert all entries from the failed_list into ipm_list */
2006-06-26 11:24:41 +04:00
list_for_each_entry_safe ( ipm , tmp , & failed_list , list )
list_move_tail ( & ipm - > list , & card - > ipm_list ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & card - > ipm_lock , flags ) ;
}
/**
* get mac address for the relevant Multicast address
*/
static void
2006-11-15 08:43:44 +03:00
lcs_get_mac_for_ipm ( __be32 ipm , char * mac , struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
LCS_DBF_TEXT ( 4 , trace , " getmac " ) ;
if ( dev - > type = = ARPHRD_IEEE802_TR )
ip_tr_mc_map ( ipm , mac ) ;
else
ip_eth_mc_map ( ipm , mac ) ;
}
/**
* function called by net device to handle multicast address relevant things
*/
static inline void
lcs_remove_mc_addresses ( struct lcs_card * card , struct in_device * in4_dev )
{
struct ip_mc_list * im4 ;
struct list_head * l ;
struct lcs_ipm_list * ipm ;
unsigned long flags ;
char buf [ MAX_ADDR_LEN ] ;
LCS_DBF_TEXT ( 4 , trace , " remmclst " ) ;
spin_lock_irqsave ( & card - > ipm_lock , flags ) ;
list_for_each ( l , & card - > ipm_list ) {
ipm = list_entry ( l , struct lcs_ipm_list , list ) ;
for ( im4 = in4_dev - > mc_list ; im4 ! = NULL ; im4 = im4 - > next ) {
lcs_get_mac_for_ipm ( im4 - > multiaddr , buf , card - > dev ) ;
if ( ( ipm - > ipm . ip_addr = = im4 - > multiaddr ) & &
( memcmp ( buf , & ipm - > ipm . mac_addr ,
LCS_MAC_LENGTH ) = = 0 ) )
break ;
}
if ( im4 = = NULL )
ipm - > ipm_state = LCS_IPM_STATE_DEL_REQUIRED ;
}
spin_unlock_irqrestore ( & card - > ipm_lock , flags ) ;
}
static inline struct lcs_ipm_list *
lcs_check_addr_entry ( struct lcs_card * card , struct ip_mc_list * im4 , char * buf )
{
struct lcs_ipm_list * tmp , * ipm = NULL ;
struct list_head * l ;
unsigned long flags ;
LCS_DBF_TEXT ( 4 , trace , " chkmcent " ) ;
spin_lock_irqsave ( & card - > ipm_lock , flags ) ;
list_for_each ( l , & card - > ipm_list ) {
tmp = list_entry ( l , struct lcs_ipm_list , list ) ;
if ( ( tmp - > ipm . ip_addr = = im4 - > multiaddr ) & &
( memcmp ( buf , & tmp - > ipm . mac_addr ,
LCS_MAC_LENGTH ) = = 0 ) ) {
ipm = tmp ;
break ;
}
}
spin_unlock_irqrestore ( & card - > ipm_lock , flags ) ;
return ipm ;
}
static inline void
lcs_set_mc_addresses ( struct lcs_card * card , struct in_device * in4_dev )
{
struct ip_mc_list * im4 ;
struct lcs_ipm_list * ipm ;
char buf [ MAX_ADDR_LEN ] ;
unsigned long flags ;
LCS_DBF_TEXT ( 4 , trace , " setmclst " ) ;
for ( im4 = in4_dev - > mc_list ; im4 ; im4 = im4 - > next ) {
lcs_get_mac_for_ipm ( im4 - > multiaddr , buf , card - > dev ) ;
ipm = lcs_check_addr_entry ( card , im4 , buf ) ;
if ( ipm ! = NULL )
continue ; /* Address already in list. */
ipm = ( struct lcs_ipm_list * )
2006-05-24 11:51:17 +04:00
kzalloc ( sizeof ( struct lcs_ipm_list ) , GFP_ATOMIC ) ;
2005-04-17 02:20:36 +04:00
if ( ipm = = NULL ) {
2008-12-25 15:39:33 +03:00
pr_info ( " Not enough memory to add "
" new multicast entry! \n " ) ;
2005-04-17 02:20:36 +04:00
break ;
}
memcpy ( & ipm - > ipm . mac_addr , buf , LCS_MAC_LENGTH ) ;
ipm - > ipm . ip_addr = im4 - > multiaddr ;
ipm - > ipm_state = LCS_IPM_STATE_SET_REQUIRED ;
spin_lock_irqsave ( & card - > ipm_lock , flags ) ;
2006-05-24 11:51:17 +04:00
LCS_DBF_HEX ( 2 , trace , & ipm - > ipm . ip_addr , 4 ) ;
2005-04-17 02:20:36 +04:00
list_add ( & ipm - > list , & card - > ipm_list ) ;
spin_unlock_irqrestore ( & card - > ipm_lock , flags ) ;
}
}
static int
lcs_register_mc_addresses ( void * data )
{
struct lcs_card * card ;
struct in_device * in4_dev ;
card = ( struct lcs_card * ) data ;
if ( ! lcs_do_run_thread ( card , LCS_SET_MC_THREAD ) )
return 0 ;
LCS_DBF_TEXT ( 4 , trace , " regmulti " ) ;
in4_dev = in_dev_get ( card - > dev ) ;
if ( in4_dev = = NULL )
goto out ;
read_lock ( & in4_dev - > mc_list_lock ) ;
lcs_remove_mc_addresses ( card , in4_dev ) ;
lcs_set_mc_addresses ( card , in4_dev ) ;
read_unlock ( & in4_dev - > mc_list_lock ) ;
in_dev_put ( in4_dev ) ;
2006-05-24 11:51:17 +04:00
netif_carrier_off ( card - > dev ) ;
netif_tx_disable ( card - > dev ) ;
wait_event ( card - > write . wait_q ,
2006-12-04 17:40:59 +03:00
( card - > write . state ! = LCS_CH_STATE_RUNNING ) ) ;
2005-04-17 02:20:36 +04:00
lcs_fix_multicast_list ( card ) ;
2006-05-24 11:51:17 +04:00
if ( card - > state = = DEV_STATE_UP ) {
netif_carrier_on ( card - > dev ) ;
netif_wake_queue ( card - > dev ) ;
}
2005-04-17 02:20:36 +04:00
out :
lcs_clear_thread_running_bit ( card , LCS_SET_MC_THREAD ) ;
return 0 ;
}
2009-01-20 09:14:32 +03:00
# endif /* CONFIG_IP_MULTICAST */
2005-04-17 02:20:36 +04:00
/**
* function called by net device to
* handle multicast address relevant things
*/
static void
lcs_set_multicast_list ( struct net_device * dev )
{
2009-01-20 09:14:32 +03:00
# ifdef CONFIG_IP_MULTICAST
2005-04-17 02:20:36 +04:00
struct lcs_card * card ;
LCS_DBF_TEXT ( 4 , trace , " setmulti " ) ;
2008-12-07 10:57:49 +03:00
card = ( struct lcs_card * ) dev - > ml_priv ;
2005-04-17 02:20:36 +04:00
2006-05-27 05:58:38 +04:00
if ( ! lcs_set_thread_start_bit ( card , LCS_SET_MC_THREAD ) )
2005-04-17 02:20:36 +04:00
schedule_work ( & card - > kernel_thread_starter ) ;
# endif /* CONFIG_IP_MULTICAST */
2009-01-20 09:14:32 +03:00
}
2005-04-17 02:20:36 +04:00
static long
lcs_check_irb_error ( struct ccw_device * cdev , struct irb * irb )
{
if ( ! IS_ERR ( irb ) )
return 0 ;
switch ( PTR_ERR ( irb ) ) {
case - EIO :
2008-12-25 15:39:33 +03:00
dev_warn ( & cdev - > dev ,
" An I/O-error occurred on the LCS device \n " ) ;
2005-04-17 02:20:36 +04:00
LCS_DBF_TEXT ( 2 , trace , " ckirberr " ) ;
LCS_DBF_TEXT_ ( 2 , trace , " rc%d " , - EIO ) ;
break ;
case - ETIMEDOUT :
2008-12-25 15:39:33 +03:00
dev_warn ( & cdev - > dev ,
" A command timed out on the LCS device \n " ) ;
2005-04-17 02:20:36 +04:00
LCS_DBF_TEXT ( 2 , trace , " ckirberr " ) ;
LCS_DBF_TEXT_ ( 2 , trace , " rc%d " , - ETIMEDOUT ) ;
break ;
default :
2008-12-25 15:39:33 +03:00
dev_warn ( & cdev - > dev ,
" An error occurred on the LCS device, rc=%ld \n " ,
PTR_ERR ( irb ) ) ;
2005-04-17 02:20:36 +04:00
LCS_DBF_TEXT ( 2 , trace , " ckirberr " ) ;
LCS_DBF_TEXT ( 2 , trace , " rc??? " ) ;
}
return PTR_ERR ( irb ) ;
}
2006-05-24 11:51:17 +04:00
static int
lcs_get_problem ( struct ccw_device * cdev , struct irb * irb )
{
int dstat , cstat ;
char * sense ;
sense = ( char * ) irb - > ecw ;
2008-07-14 11:58:50 +04:00
cstat = irb - > scsw . cmd . cstat ;
dstat = irb - > scsw . cmd . dstat ;
2006-05-24 11:51:17 +04:00
if ( cstat & ( SCHN_STAT_CHN_CTRL_CHK | SCHN_STAT_INTF_CTRL_CHK |
SCHN_STAT_CHN_DATA_CHK | SCHN_STAT_CHAIN_CHECK |
SCHN_STAT_PROT_CHECK | SCHN_STAT_PROG_CHECK ) ) {
LCS_DBF_TEXT ( 2 , trace , " CGENCHK " ) ;
return 1 ;
}
if ( dstat & DEV_STAT_UNIT_CHECK ) {
if ( sense [ LCS_SENSE_BYTE_1 ] &
LCS_SENSE_RESETTING_EVENT ) {
LCS_DBF_TEXT ( 2 , trace , " REVIND " ) ;
return 1 ;
}
if ( sense [ LCS_SENSE_BYTE_0 ] &
LCS_SENSE_CMD_REJECT ) {
LCS_DBF_TEXT ( 2 , trace , " CMDREJ " ) ;
return 0 ;
}
if ( ( ! sense [ LCS_SENSE_BYTE_0 ] ) & &
( ! sense [ LCS_SENSE_BYTE_1 ] ) & &
( ! sense [ LCS_SENSE_BYTE_2 ] ) & &
( ! sense [ LCS_SENSE_BYTE_3 ] ) ) {
LCS_DBF_TEXT ( 2 , trace , " ZEROSEN " ) ;
return 0 ;
}
LCS_DBF_TEXT ( 2 , trace , " DGENCHK " ) ;
return 1 ;
}
return 0 ;
}
2007-02-05 23:16:47 +03:00
static void
2006-05-24 11:51:17 +04:00
lcs_schedule_recovery ( struct lcs_card * card )
{
LCS_DBF_TEXT ( 2 , trace , " startrec " ) ;
if ( ! lcs_set_thread_start_bit ( card , LCS_RECOVERY_THREAD ) )
schedule_work ( & card - > kernel_thread_starter ) ;
}
2005-04-17 02:20:36 +04:00
/**
* IRQ Handler for LCS channels
*/
static void
lcs_irq ( struct ccw_device * cdev , unsigned long intparm , struct irb * irb )
{
struct lcs_card * card ;
struct lcs_channel * channel ;
2006-05-24 11:51:17 +04:00
int rc , index ;
int cstat , dstat ;
2005-04-17 02:20:36 +04:00
if ( lcs_check_irb_error ( cdev , irb ) )
return ;
card = CARD_FROM_DEV ( cdev ) ;
if ( card - > read . ccwdev = = cdev )
channel = & card - > read ;
else
channel = & card - > write ;
2008-07-14 11:58:50 +04:00
cstat = irb - > scsw . cmd . cstat ;
dstat = irb - > scsw . cmd . dstat ;
2008-10-10 23:33:09 +04:00
LCS_DBF_TEXT_ ( 5 , trace , " Rint%s " , dev_name ( & cdev - > dev ) ) ;
2008-07-14 11:58:50 +04:00
LCS_DBF_TEXT_ ( 5 , trace , " %4x%4x " , irb - > scsw . cmd . cstat ,
irb - > scsw . cmd . dstat ) ;
LCS_DBF_TEXT_ ( 5 , trace , " %4x%4x " , irb - > scsw . cmd . fctl ,
irb - > scsw . cmd . actl ) ;
2005-04-17 02:20:36 +04:00
2006-05-24 11:51:17 +04:00
/* Check for channel and device errors presented */
rc = lcs_get_problem ( cdev , irb ) ;
if ( rc | | ( dstat & DEV_STAT_UNIT_EXCEP ) ) {
2008-12-25 15:39:33 +03:00
dev_warn ( & cdev - > dev ,
" The LCS device stopped because of an error, "
" dstat=0x%X, cstat=0x%X \n " ,
dstat , cstat ) ;
2006-05-24 11:51:17 +04:00
if ( rc ) {
2007-10-05 18:45:47 +04:00
channel - > state = LCS_CH_STATE_ERROR ;
2006-05-24 11:51:17 +04:00
}
}
2007-10-05 18:45:47 +04:00
if ( channel - > state = = LCS_CH_STATE_ERROR ) {
lcs_schedule_recovery ( card ) ;
wake_up ( & card - > wait_q ) ;
return ;
}
2005-04-17 02:20:36 +04:00
/* How far in the ccw chain have we processed? */
2006-12-04 17:40:59 +03:00
if ( ( channel - > state ! = LCS_CH_STATE_INIT ) & &
2008-08-21 19:10:25 +04:00
( irb - > scsw . cmd . fctl & SCSW_FCTL_START_FUNC ) & &
( irb - > scsw . cmd . cpa ! = 0 ) ) {
2008-07-14 11:58:50 +04:00
index = ( struct ccw1 * ) __va ( ( addr_t ) irb - > scsw . cmd . cpa )
2005-04-17 02:20:36 +04:00
- channel - > ccws ;
2008-07-14 11:58:50 +04:00
if ( ( irb - > scsw . cmd . actl & SCSW_ACTL_SUSPENDED ) | |
( irb - > scsw . cmd . cstat & SCHN_STAT_PCI ) )
2005-04-17 02:20:36 +04:00
/* Bloody io subsystem tells us lies about cpa... */
index = ( index - 1 ) & ( LCS_NUM_BUFFS - 1 ) ;
while ( channel - > io_idx ! = index ) {
__lcs_processed_buffer ( channel ,
channel - > iob + channel - > io_idx ) ;
channel - > io_idx =
( channel - > io_idx + 1 ) & ( LCS_NUM_BUFFS - 1 ) ;
}
}
2008-07-14 11:58:50 +04:00
if ( ( irb - > scsw . cmd . dstat & DEV_STAT_DEV_END ) | |
( irb - > scsw . cmd . dstat & DEV_STAT_CHN_END ) | |
( irb - > scsw . cmd . dstat & DEV_STAT_UNIT_CHECK ) )
2005-04-17 02:20:36 +04:00
/* Mark channel as stopped. */
2006-12-04 17:40:59 +03:00
channel - > state = LCS_CH_STATE_STOPPED ;
2008-07-14 11:58:50 +04:00
else if ( irb - > scsw . cmd . actl & SCSW_ACTL_SUSPENDED )
2005-04-17 02:20:36 +04:00
/* CCW execution stopped on a suspend bit. */
2006-12-04 17:40:59 +03:00
channel - > state = LCS_CH_STATE_SUSPENDED ;
2008-07-14 11:58:50 +04:00
if ( irb - > scsw . cmd . fctl & SCSW_FCTL_HALT_FUNC ) {
if ( irb - > scsw . cmd . cc ! = 0 ) {
2005-04-17 02:20:36 +04:00
ccw_device_halt ( channel - > ccwdev , ( addr_t ) channel ) ;
return ;
}
/* The channel has been stopped by halt_IO. */
2006-12-04 17:40:59 +03:00
channel - > state = LCS_CH_STATE_HALTED ;
2005-04-17 02:20:36 +04:00
}
2008-07-14 11:58:50 +04:00
if ( irb - > scsw . cmd . fctl & SCSW_FCTL_CLEAR_FUNC )
2006-12-04 17:40:59 +03:00
channel - > state = LCS_CH_STATE_CLEARED ;
2005-04-17 02:20:36 +04:00
/* Do the rest in the tasklet. */
tasklet_schedule ( & channel - > irq_tasklet ) ;
}
/**
* Tasklet for IRQ handler
*/
static void
lcs_tasklet ( unsigned long data )
{
unsigned long flags ;
struct lcs_channel * channel ;
struct lcs_buffer * iob ;
int buf_idx ;
int rc ;
channel = ( struct lcs_channel * ) data ;
2008-10-10 23:33:09 +04:00
LCS_DBF_TEXT_ ( 5 , trace , " tlet%s " , dev_name ( & channel - > ccwdev - > dev ) ) ;
2005-04-17 02:20:36 +04:00
/* Check for processed buffers. */
iob = channel - > iob ;
buf_idx = channel - > buf_idx ;
2006-12-04 17:40:59 +03:00
while ( iob [ buf_idx ] . state = = LCS_BUF_STATE_PROCESSED ) {
2005-04-17 02:20:36 +04:00
/* Do the callback thing. */
if ( iob [ buf_idx ] . callback ! = NULL )
iob [ buf_idx ] . callback ( channel , iob + buf_idx ) ;
buf_idx = ( buf_idx + 1 ) & ( LCS_NUM_BUFFS - 1 ) ;
}
channel - > buf_idx = buf_idx ;
2006-12-04 17:40:59 +03:00
if ( channel - > state = = LCS_CH_STATE_STOPPED )
2005-04-17 02:20:36 +04:00
// FIXME: what if rc != 0 ??
rc = lcs_start_channel ( channel ) ;
spin_lock_irqsave ( get_ccwdev_lock ( channel - > ccwdev ) , flags ) ;
2006-12-04 17:40:59 +03:00
if ( channel - > state = = LCS_CH_STATE_SUSPENDED & &
channel - > iob [ channel - > io_idx ] . state = = LCS_BUF_STATE_READY ) {
2005-04-17 02:20:36 +04:00
// FIXME: what if rc != 0 ??
rc = __lcs_resume_channel ( channel ) ;
}
spin_unlock_irqrestore ( get_ccwdev_lock ( channel - > ccwdev ) , flags ) ;
/* Something happened on the channel. Wake up waiters. */
wake_up ( & channel - > wait_q ) ;
}
/**
* Finish current tx buffer and make it ready for transmit .
*/
static void
__lcs_emit_txbuffer ( struct lcs_card * card )
{
LCS_DBF_TEXT ( 5 , trace , " emittx " ) ;
* ( __u16 * ) ( card - > tx_buffer - > data + card - > tx_buffer - > count ) = 0 ;
card - > tx_buffer - > count + = 2 ;
lcs_ready_buffer ( & card - > write , card - > tx_buffer ) ;
card - > tx_buffer = NULL ;
card - > tx_emitted + + ;
}
/**
* Callback for finished tx buffers .
*/
static void
lcs_txbuffer_cb ( struct lcs_channel * channel , struct lcs_buffer * buffer )
{
struct lcs_card * card ;
LCS_DBF_TEXT ( 5 , trace , " txbuffcb " ) ;
/* Put buffer back to pool. */
lcs_release_buffer ( channel , buffer ) ;
2007-02-10 12:46:25 +03:00
card = container_of ( channel , struct lcs_card , write ) ;
2006-05-24 11:51:17 +04:00
if ( netif_queue_stopped ( card - > dev ) & & netif_carrier_ok ( card - > dev ) )
2006-02-07 19:04:36 +03:00
netif_wake_queue ( card - > dev ) ;
2005-04-17 02:20:36 +04:00
spin_lock ( & card - > lock ) ;
card - > tx_emitted - - ;
if ( card - > tx_emitted < = 0 & & card - > tx_buffer ! = NULL )
/*
* Last running tx buffer has finished . Submit partially
* filled current buffer .
*/
__lcs_emit_txbuffer ( card ) ;
spin_unlock ( & card - > lock ) ;
}
/**
* Packet transmit function called by network stack
*/
static int
__lcs_start_xmit ( struct lcs_card * card , struct sk_buff * skb ,
struct net_device * dev )
{
struct lcs_header * header ;
2006-02-07 19:04:36 +03:00
int rc = 0 ;
2005-04-17 02:20:36 +04:00
LCS_DBF_TEXT ( 5 , trace , " hardxmit " ) ;
if ( skb = = NULL ) {
card - > stats . tx_dropped + + ;
card - > stats . tx_errors + + ;
return - EIO ;
}
if ( card - > state ! = DEV_STATE_UP ) {
dev_kfree_skb ( skb ) ;
card - > stats . tx_dropped + + ;
card - > stats . tx_errors + + ;
card - > stats . tx_carrier_errors + + ;
return 0 ;
}
2006-05-24 11:51:17 +04:00
if ( skb - > protocol = = htons ( ETH_P_IPV6 ) ) {
dev_kfree_skb ( skb ) ;
return 0 ;
}
2006-02-07 19:04:36 +03:00
netif_stop_queue ( card - > dev ) ;
spin_lock ( & card - > lock ) ;
2005-04-17 02:20:36 +04:00
if ( card - > tx_buffer ! = NULL & &
card - > tx_buffer - > count + sizeof ( struct lcs_header ) +
skb - > len + sizeof ( u16 ) > LCS_IOBUFFERSIZE )
/* skb too big for current tx buffer. */
__lcs_emit_txbuffer ( card ) ;
if ( card - > tx_buffer = = NULL ) {
/* Get new tx buffer */
card - > tx_buffer = lcs_get_buffer ( & card - > write ) ;
if ( card - > tx_buffer = = NULL ) {
card - > stats . tx_dropped + + ;
2006-02-07 19:04:36 +03:00
rc = - EBUSY ;
goto out ;
2005-04-17 02:20:36 +04:00
}
card - > tx_buffer - > callback = lcs_txbuffer_cb ;
card - > tx_buffer - > count = 0 ;
}
header = ( struct lcs_header * )
( card - > tx_buffer - > data + card - > tx_buffer - > count ) ;
card - > tx_buffer - > count + = skb - > len + sizeof ( struct lcs_header ) ;
header - > offset = card - > tx_buffer - > count ;
header - > type = card - > lan_type ;
header - > slot = card - > portno ;
2007-03-28 01:55:52 +04:00
skb_copy_from_linear_data ( skb , header + 1 , skb - > len ) ;
2006-02-07 19:04:36 +03:00
spin_unlock ( & card - > lock ) ;
2005-04-17 02:20:36 +04:00
card - > stats . tx_bytes + = skb - > len ;
card - > stats . tx_packets + + ;
dev_kfree_skb ( skb ) ;
2006-02-07 19:04:36 +03:00
netif_wake_queue ( card - > dev ) ;
spin_lock ( & card - > lock ) ;
if ( card - > tx_emitted < = 0 & & card - > tx_buffer ! = NULL )
2005-04-17 02:20:36 +04:00
/* If this is the first tx buffer emit it immediately. */
__lcs_emit_txbuffer ( card ) ;
2006-02-07 19:04:36 +03:00
out :
spin_unlock ( & card - > lock ) ;
return rc ;
2005-04-17 02:20:36 +04:00
}
static int
lcs_start_xmit ( struct sk_buff * skb , struct net_device * dev )
{
struct lcs_card * card ;
int rc ;
LCS_DBF_TEXT ( 5 , trace , " pktxmit " ) ;
2008-12-07 10:57:49 +03:00
card = ( struct lcs_card * ) dev - > ml_priv ;
2005-04-17 02:20:36 +04:00
rc = __lcs_start_xmit ( card , skb , dev ) ;
return rc ;
}
/**
* send startlan and lanstat command to make LCS device ready
*/
static int
lcs_startlan_auto ( struct lcs_card * card )
{
int rc ;
LCS_DBF_TEXT ( 2 , trace , " strtauto " ) ;
# ifdef CONFIG_NET_ETHERNET
card - > lan_type = LCS_FRAME_TYPE_ENET ;
rc = lcs_send_startlan ( card , LCS_INITIATOR_TCPIP ) ;
if ( rc = = 0 )
return 0 ;
# endif
# ifdef CONFIG_TR
card - > lan_type = LCS_FRAME_TYPE_TR ;
rc = lcs_send_startlan ( card , LCS_INITIATOR_TCPIP ) ;
if ( rc = = 0 )
return 0 ;
# endif
# ifdef CONFIG_FDDI
card - > lan_type = LCS_FRAME_TYPE_FDDI ;
rc = lcs_send_startlan ( card , LCS_INITIATOR_TCPIP ) ;
if ( rc = = 0 )
return 0 ;
# endif
return - EIO ;
}
static int
lcs_startlan ( struct lcs_card * card )
{
int rc , i ;
LCS_DBF_TEXT ( 2 , trace , " startlan " ) ;
rc = 0 ;
if ( card - > portno ! = LCS_INVALID_PORT_NO ) {
if ( card - > lan_type = = LCS_FRAME_TYPE_AUTO )
rc = lcs_startlan_auto ( card ) ;
else
rc = lcs_send_startlan ( card , LCS_INITIATOR_TCPIP ) ;
} else {
for ( i = 0 ; i < = 16 ; i + + ) {
card - > portno = i ;
if ( card - > lan_type ! = LCS_FRAME_TYPE_AUTO )
rc = lcs_send_startlan ( card ,
LCS_INITIATOR_TCPIP ) ;
else
/* autodetecting lan type */
rc = lcs_startlan_auto ( card ) ;
if ( rc = = 0 )
break ;
}
}
if ( rc = = 0 )
return lcs_send_lanstat ( card ) ;
return rc ;
}
/**
* LCS detect function
* setup channels and make them I / O ready
*/
static int
lcs_detect ( struct lcs_card * card )
{
int rc = 0 ;
LCS_DBF_TEXT ( 2 , setup , " lcsdetct " ) ;
/* start/reset card */
if ( card - > dev )
netif_stop_queue ( card - > dev ) ;
rc = lcs_stop_channels ( card ) ;
if ( rc = = 0 ) {
rc = lcs_start_channels ( card ) ;
if ( rc = = 0 ) {
rc = lcs_send_startup ( card , LCS_INITIATOR_TCPIP ) ;
if ( rc = = 0 )
rc = lcs_startlan ( card ) ;
}
}
if ( rc = = 0 ) {
card - > state = DEV_STATE_UP ;
} else {
card - > state = DEV_STATE_DOWN ;
2006-12-04 17:40:59 +03:00
card - > write . state = LCS_CH_STATE_INIT ;
card - > read . state = LCS_CH_STATE_INIT ;
2005-04-17 02:20:36 +04:00
}
return rc ;
}
/**
* LCS Stop card
*/
static int
lcs_stopcard ( struct lcs_card * card )
{
int rc ;
LCS_DBF_TEXT ( 3 , setup , " stopcard " ) ;
2006-12-04 17:40:59 +03:00
if ( card - > read . state ! = LCS_CH_STATE_STOPPED & &
card - > write . state ! = LCS_CH_STATE_STOPPED & &
2007-10-05 18:45:47 +04:00
card - > read . state ! = LCS_CH_STATE_ERROR & &
card - > write . state ! = LCS_CH_STATE_ERROR & &
2005-04-17 02:20:36 +04:00
card - > state = = DEV_STATE_UP ) {
lcs_clear_multicast_list ( card ) ;
rc = lcs_send_stoplan ( card , LCS_INITIATOR_TCPIP ) ;
rc = lcs_send_shutdown ( card ) ;
}
rc = lcs_stop_channels ( card ) ;
card - > state = DEV_STATE_DOWN ;
return rc ;
}
/**
* Kernel Thread helper functions for LGW initiated commands
*/
static void
2006-12-06 22:18:20 +03:00
lcs_start_kernel_thread ( struct work_struct * work )
2005-04-17 02:20:36 +04:00
{
2006-12-06 22:18:20 +03:00
struct lcs_card * card = container_of ( work , struct lcs_card , kernel_thread_starter ) ;
2005-04-17 02:20:36 +04:00
LCS_DBF_TEXT ( 5 , trace , " krnthrd " ) ;
2006-05-24 11:51:17 +04:00
if ( lcs_do_start_thread ( card , LCS_RECOVERY_THREAD ) )
2009-03-24 06:27:43 +03:00
kthread_run ( lcs_recovery , card , " lcs_recover " ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_IP_MULTICAST
if ( lcs_do_start_thread ( card , LCS_SET_MC_THREAD ) )
2009-03-24 06:27:43 +03:00
kthread_run ( lcs_register_mc_addresses , card , " regipm " ) ;
2005-04-17 02:20:36 +04:00
# endif
}
/**
* Process control frames .
*/
static void
lcs_get_control ( struct lcs_card * card , struct lcs_cmd * cmd )
{
LCS_DBF_TEXT ( 5 , trace , " getctrl " ) ;
if ( cmd - > initiator = = LCS_INITIATOR_LGW ) {
switch ( cmd - > cmd_code ) {
case LCS_CMD_STARTUP :
case LCS_CMD_STARTLAN :
2006-05-24 11:51:17 +04:00
lcs_schedule_recovery ( card ) ;
2005-04-17 02:20:36 +04:00
break ;
case LCS_CMD_STOPLAN :
2008-12-25 15:39:33 +03:00
pr_warning ( " Stoplan for %s initiated by LGW. \n " ,
card - > dev - > name ) ;
2006-05-24 11:51:17 +04:00
if ( card - > dev )
netif_carrier_off ( card - > dev ) ;
2005-04-17 02:20:36 +04:00
break ;
default :
2008-07-14 11:59:42 +04:00
LCS_DBF_TEXT ( 5 , trace , " noLGWcmd " ) ;
2005-04-17 02:20:36 +04:00
break ;
}
} else
lcs_notify_lancmd_waiters ( card , cmd ) ;
}
/**
* Unpack network packet .
*/
static void
lcs_get_skb ( struct lcs_card * card , char * skb_data , unsigned int skb_len )
{
struct sk_buff * skb ;
LCS_DBF_TEXT ( 5 , trace , " getskb " ) ;
if ( card - > dev = = NULL | |
card - > state ! = DEV_STATE_UP )
/* The card isn't up. Ignore the packet. */
return ;
skb = dev_alloc_skb ( skb_len ) ;
if ( skb = = NULL ) {
2008-12-25 15:39:33 +03:00
dev_err ( & card - > dev - > dev ,
" Allocating a socket buffer to interface %s failed \n " ,
2005-04-17 02:20:36 +04:00
card - > dev - > name ) ;
card - > stats . rx_dropped + + ;
return ;
}
memcpy ( skb_put ( skb , skb_len ) , skb_data , skb_len ) ;
skb - > protocol = card - > lan_type_trans ( skb , card - > dev ) ;
card - > stats . rx_bytes + = skb_len ;
card - > stats . rx_packets + + ;
2008-04-24 12:15:18 +04:00
if ( skb - > protocol = = htons ( ETH_P_802_2 ) )
* ( ( __u32 * ) skb - > cb ) = + + card - > pkt_seq ;
2005-04-17 02:20:36 +04:00
netif_rx ( skb ) ;
}
/**
* LCS main routine to get packets and lancmd replies from the buffers
*/
static void
lcs_get_frames_cb ( struct lcs_channel * channel , struct lcs_buffer * buffer )
{
struct lcs_card * card ;
struct lcs_header * lcs_hdr ;
__u16 offset ;
LCS_DBF_TEXT ( 5 , trace , " lcsgtpkt " ) ;
lcs_hdr = ( struct lcs_header * ) buffer - > data ;
if ( lcs_hdr - > offset = = LCS_ILLEGAL_OFFSET ) {
LCS_DBF_TEXT ( 4 , trace , " -eiogpkt " ) ;
return ;
}
2007-02-10 12:46:25 +03:00
card = container_of ( channel , struct lcs_card , read ) ;
2005-04-17 02:20:36 +04:00
offset = 0 ;
while ( lcs_hdr - > offset ! = 0 ) {
if ( lcs_hdr - > offset < = 0 | |
lcs_hdr - > offset > LCS_IOBUFFERSIZE | |
lcs_hdr - > offset < offset ) {
/* Offset invalid. */
card - > stats . rx_length_errors + + ;
card - > stats . rx_errors + + ;
return ;
}
/* What kind of frame is it? */
if ( lcs_hdr - > type = = LCS_FRAME_TYPE_CONTROL )
/* Control frame. */
lcs_get_control ( card , ( struct lcs_cmd * ) lcs_hdr ) ;
else if ( lcs_hdr - > type = = LCS_FRAME_TYPE_ENET | |
lcs_hdr - > type = = LCS_FRAME_TYPE_TR | |
lcs_hdr - > type = = LCS_FRAME_TYPE_FDDI )
/* Normal network packet. */
lcs_get_skb ( card , ( char * ) ( lcs_hdr + 1 ) ,
lcs_hdr - > offset - offset -
sizeof ( struct lcs_header ) ) ;
else
/* Unknown frame type. */
; // FIXME: error message ?
/* Proceed to next frame. */
offset = lcs_hdr - > offset ;
lcs_hdr - > offset = LCS_ILLEGAL_OFFSET ;
lcs_hdr = ( struct lcs_header * ) ( buffer - > data + offset ) ;
}
/* The buffer is now empty. Make it ready again. */
lcs_ready_buffer ( & card - > read , buffer ) ;
}
/**
* get network statistics for ifconfig and other user programs
*/
static struct net_device_stats *
lcs_getstats ( struct net_device * dev )
{
struct lcs_card * card ;
LCS_DBF_TEXT ( 4 , trace , " netstats " ) ;
2008-12-07 10:57:49 +03:00
card = ( struct lcs_card * ) dev - > ml_priv ;
2005-04-17 02:20:36 +04:00
return & card - > stats ;
}
/**
* stop lcs device
* This function will be called by user doing ifconfig xxx down
*/
static int
lcs_stop_device ( struct net_device * dev )
{
struct lcs_card * card ;
int rc ;
LCS_DBF_TEXT ( 2 , trace , " stopdev " ) ;
2008-12-07 10:57:49 +03:00
card = ( struct lcs_card * ) dev - > ml_priv ;
2006-05-24 11:51:17 +04:00
netif_carrier_off ( dev ) ;
netif_tx_disable ( dev ) ;
2005-04-17 02:20:36 +04:00
dev - > flags & = ~ IFF_UP ;
2006-05-24 11:51:17 +04:00
wait_event ( card - > write . wait_q ,
2006-12-04 17:40:59 +03:00
( card - > write . state ! = LCS_CH_STATE_RUNNING ) ) ;
2005-04-17 02:20:36 +04:00
rc = lcs_stopcard ( card ) ;
if ( rc )
2008-12-25 15:39:33 +03:00
dev_err ( & card - > dev - > dev ,
" Shutting down the LCS device failed \n " ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
/**
* start lcs device and make it runnable
* This function will be called by user doing ifconfig xxx up
*/
static int
lcs_open_device ( struct net_device * dev )
{
struct lcs_card * card ;
int rc ;
LCS_DBF_TEXT ( 2 , trace , " opendev " ) ;
2008-12-07 10:57:49 +03:00
card = ( struct lcs_card * ) dev - > ml_priv ;
2005-04-17 02:20:36 +04:00
/* initialize statistics */
rc = lcs_detect ( card ) ;
if ( rc ) {
2008-12-25 15:39:33 +03:00
pr_err ( " Error in opening device! \n " ) ;
2005-04-17 02:20:36 +04:00
} else {
dev - > flags | = IFF_UP ;
2006-05-24 11:51:17 +04:00
netif_carrier_on ( dev ) ;
2005-04-17 02:20:36 +04:00
netif_wake_queue ( dev ) ;
card - > state = DEV_STATE_UP ;
}
return rc ;
}
/**
* show function for portno called by cat or similar things
*/
static ssize_t
2005-05-17 14:43:27 +04:00
lcs_portno_show ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
struct lcs_card * card ;
card = ( struct lcs_card * ) dev - > driver_data ;
if ( ! card )
return 0 ;
return sprintf ( buf , " %d \n " , card - > portno ) ;
}
/**
* store the value which is piped to file portno
*/
static ssize_t
2005-05-17 14:43:27 +04:00
lcs_portno_store ( struct device * dev , struct device_attribute * attr , const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
struct lcs_card * card ;
int value ;
card = ( struct lcs_card * ) dev - > driver_data ;
if ( ! card )
return 0 ;
sscanf ( buf , " %u " , & value ) ;
/* TODO: sanity checks */
card - > portno = value ;
return count ;
}
static DEVICE_ATTR ( portno , 0644 , lcs_portno_show , lcs_portno_store ) ;
static ssize_t
2005-05-17 14:43:27 +04:00
lcs_type_show ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
struct ccwgroup_device * cgdev ;
cgdev = to_ccwgroupdev ( dev ) ;
if ( ! cgdev )
return - ENODEV ;
return sprintf ( buf , " %s \n " , cu3088_type [ cgdev - > cdev [ 0 ] - > id . driver_info ] ) ;
}
static DEVICE_ATTR ( type , 0444 , lcs_type_show , NULL ) ;
static ssize_t
2005-05-17 14:43:27 +04:00
lcs_timeout_show ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
struct lcs_card * card ;
card = ( struct lcs_card * ) dev - > driver_data ;
return card ? sprintf ( buf , " %u \n " , card - > lancmd_timeout ) : 0 ;
}
static ssize_t
2005-05-17 14:43:27 +04:00
lcs_timeout_store ( struct device * dev , struct device_attribute * attr , const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
struct lcs_card * card ;
int value ;
card = ( struct lcs_card * ) dev - > driver_data ;
if ( ! card )
return 0 ;
sscanf ( buf , " %u " , & value ) ;
/* TODO: sanity checks */
card - > lancmd_timeout = value ;
return count ;
}
2007-02-05 23:16:47 +03:00
static DEVICE_ATTR ( lancmd_timeout , 0644 , lcs_timeout_show , lcs_timeout_store ) ;
2005-04-17 02:20:36 +04:00
2006-05-24 11:51:17 +04:00
static ssize_t
2006-05-27 05:58:38 +04:00
lcs_dev_recover_store ( struct device * dev , struct device_attribute * attr ,
2006-05-24 11:51:17 +04:00
const char * buf , size_t count )
{
struct lcs_card * card = dev - > driver_data ;
char * tmp ;
int i ;
if ( ! card )
return - EINVAL ;
if ( card - > state ! = DEV_STATE_UP )
return - EPERM ;
i = simple_strtoul ( buf , & tmp , 16 ) ;
if ( i = = 1 )
lcs_schedule_recovery ( card ) ;
return count ;
}
static DEVICE_ATTR ( recover , 0200 , NULL , lcs_dev_recover_store ) ;
2005-04-17 02:20:36 +04:00
static struct attribute * lcs_attrs [ ] = {
& dev_attr_portno . attr ,
& dev_attr_type . attr ,
& dev_attr_lancmd_timeout . attr ,
2006-05-24 11:51:17 +04:00
& dev_attr_recover . attr ,
2005-04-17 02:20:36 +04:00
NULL ,
} ;
static struct attribute_group lcs_attr_group = {
. attrs = lcs_attrs ,
} ;
/**
* lcs_probe_device is called on establishing a new ccwgroup_device .
*/
static int
lcs_probe_device ( struct ccwgroup_device * ccwgdev )
{
struct lcs_card * card ;
int ret ;
if ( ! get_device ( & ccwgdev - > dev ) )
return - ENODEV ;
LCS_DBF_TEXT ( 2 , setup , " add_dev " ) ;
card = lcs_alloc_card ( ) ;
if ( ! card ) {
2008-07-14 11:59:42 +04:00
LCS_DBF_TEXT_ ( 2 , setup , " rc%d " , - ENOMEM ) ;
2005-04-17 02:20:36 +04:00
put_device ( & ccwgdev - > dev ) ;
return - ENOMEM ;
}
ret = sysfs_create_group ( & ccwgdev - > dev . kobj , & lcs_attr_group ) ;
if ( ret ) {
lcs_free_card ( card ) ;
put_device ( & ccwgdev - > dev ) ;
return ret ;
}
ccwgdev - > dev . driver_data = card ;
ccwgdev - > cdev [ 0 ] - > handler = lcs_irq ;
ccwgdev - > cdev [ 1 ] - > handler = lcs_irq ;
2006-05-24 11:51:17 +04:00
card - > gdev = ccwgdev ;
2006-12-06 22:18:20 +03:00
INIT_WORK ( & card - > kernel_thread_starter , lcs_start_kernel_thread ) ;
2006-05-24 11:51:17 +04:00
card - > thread_start_mask = 0 ;
card - > thread_allowed_mask = 0 ;
card - > thread_running_mask = 0 ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static int
lcs_register_netdev ( struct ccwgroup_device * ccwgdev )
{
struct lcs_card * card ;
LCS_DBF_TEXT ( 2 , setup , " regnetdv " ) ;
card = ( struct lcs_card * ) ccwgdev - > dev . driver_data ;
if ( card - > dev - > reg_state ! = NETREG_UNINITIALIZED )
return 0 ;
SET_NETDEV_DEV ( card - > dev , & ccwgdev - > dev ) ;
return register_netdev ( card - > dev ) ;
}
/**
* lcs_new_device will be called by setting the group device online .
*/
2009-01-09 06:43:56 +03:00
static const struct net_device_ops lcs_netdev_ops = {
. ndo_open = lcs_open_device ,
. ndo_stop = lcs_stop_device ,
. ndo_get_stats = lcs_getstats ,
. ndo_start_xmit = lcs_start_xmit ,
} ;
static const struct net_device_ops lcs_mc_netdev_ops = {
. ndo_open = lcs_open_device ,
. ndo_stop = lcs_stop_device ,
. ndo_get_stats = lcs_getstats ,
. ndo_start_xmit = lcs_start_xmit ,
. ndo_set_multicast_list = lcs_set_multicast_list ,
} ;
2005-04-17 02:20:36 +04:00
static int
lcs_new_device ( struct ccwgroup_device * ccwgdev )
{
struct lcs_card * card ;
struct net_device * dev = NULL ;
enum lcs_dev_states recover_state ;
int rc ;
card = ( struct lcs_card * ) ccwgdev - > dev . driver_data ;
if ( ! card )
return - ENODEV ;
LCS_DBF_TEXT ( 2 , setup , " newdev " ) ;
LCS_DBF_HEX ( 3 , setup , & card , sizeof ( void * ) ) ;
card - > read . ccwdev = ccwgdev - > cdev [ 0 ] ;
card - > write . ccwdev = ccwgdev - > cdev [ 1 ] ;
recover_state = card - > state ;
ccw_device_set_online ( card - > read . ccwdev ) ;
ccw_device_set_online ( card - > write . ccwdev ) ;
LCS_DBF_TEXT ( 3 , setup , " lcsnewdv " ) ;
lcs_setup_card ( card ) ;
rc = lcs_detect ( card ) ;
if ( rc ) {
LCS_DBF_TEXT ( 2 , setup , " dtctfail " ) ;
2008-12-25 15:39:33 +03:00
dev_err ( & card - > dev - > dev ,
" Detecting a network adapter for LCS devices "
" failed with rc=%d (0x%x) \n " , rc , rc ) ;
2005-04-17 02:20:36 +04:00
lcs_stopcard ( card ) ;
goto out ;
}
if ( card - > dev ) {
LCS_DBF_TEXT ( 2 , setup , " samedev " ) ;
LCS_DBF_HEX ( 3 , setup , & card , sizeof ( void * ) ) ;
goto netdev_out ;
}
switch ( card - > lan_type ) {
# ifdef CONFIG_NET_ETHERNET
case LCS_FRAME_TYPE_ENET :
card - > lan_type_trans = eth_type_trans ;
dev = alloc_etherdev ( 0 ) ;
break ;
# endif
# ifdef CONFIG_TR
case LCS_FRAME_TYPE_TR :
card - > lan_type_trans = tr_type_trans ;
dev = alloc_trdev ( 0 ) ;
break ;
# endif
# ifdef CONFIG_FDDI
case LCS_FRAME_TYPE_FDDI :
card - > lan_type_trans = fddi_type_trans ;
dev = alloc_fddidev ( 0 ) ;
break ;
# endif
default :
LCS_DBF_TEXT ( 3 , setup , " errinit " ) ;
2008-12-25 15:39:33 +03:00
pr_err ( " Initialization failed \n " ) ;
2005-04-17 02:20:36 +04:00
goto out ;
}
if ( ! dev )
goto out ;
card - > dev = dev ;
2008-12-07 10:57:49 +03:00
card - > dev - > ml_priv = card ;
2009-01-09 06:43:56 +03:00
card - > dev - > netdev_ops = & lcs_netdev_ops ;
2005-04-17 02:20:36 +04:00
memcpy ( card - > dev - > dev_addr , card - > mac , LCS_MAC_LENGTH ) ;
# ifdef CONFIG_IP_MULTICAST
if ( ! lcs_check_multicast_support ( card ) )
2009-01-09 06:43:56 +03:00
card - > dev - > netdev_ops = & lcs_mc_netdev_ops ;
2005-04-17 02:20:36 +04:00
# endif
2005-05-12 22:35:57 +04:00
netdev_out :
2005-04-17 02:20:36 +04:00
lcs_set_allowed_threads ( card , 0xffffffff ) ;
if ( recover_state = = DEV_STATE_RECOVER ) {
lcs_set_multicast_list ( card - > dev ) ;
card - > dev - > flags | = IFF_UP ;
2006-05-24 11:51:17 +04:00
netif_carrier_on ( card - > dev ) ;
2005-04-17 02:20:36 +04:00
netif_wake_queue ( card - > dev ) ;
card - > state = DEV_STATE_UP ;
2005-05-12 22:35:57 +04:00
} else {
2005-04-17 02:20:36 +04:00
lcs_stopcard ( card ) ;
2005-05-12 22:35:57 +04:00
}
2005-04-17 02:20:36 +04:00
2005-05-12 22:35:57 +04:00
if ( lcs_register_netdev ( ccwgdev ) ! = 0 )
goto out ;
/* Print out supported assists: IPv6 */
2008-12-25 15:39:33 +03:00
pr_info ( " LCS device %s %s IPv6 support \n " , card - > dev - > name ,
( card - > ip_assists_supported & LCS_IPASS_IPV6_SUPPORT ) ?
" with " : " without " ) ;
2005-05-12 22:35:57 +04:00
/* Print out supported assist: Multicast */
2008-12-25 15:39:33 +03:00
pr_info ( " LCS device %s %s Multicast support \n " , card - > dev - > name ,
( card - > ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT ) ?
" with " : " without " ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
out :
ccw_device_set_offline ( card - > read . ccwdev ) ;
ccw_device_set_offline ( card - > write . ccwdev ) ;
return - ENODEV ;
}
/**
* lcs_shutdown_device , called when setting the group device offline .
*/
static int
2006-05-24 11:51:17 +04:00
__lcs_shutdown_device ( struct ccwgroup_device * ccwgdev , int recovery_mode )
2005-04-17 02:20:36 +04:00
{
struct lcs_card * card ;
enum lcs_dev_states recover_state ;
int ret ;
LCS_DBF_TEXT ( 3 , setup , " shtdndev " ) ;
card = ( struct lcs_card * ) ccwgdev - > dev . driver_data ;
if ( ! card )
return - ENODEV ;
2006-05-24 11:51:17 +04:00
if ( recovery_mode = = 0 ) {
lcs_set_allowed_threads ( card , 0 ) ;
if ( lcs_wait_for_threads ( card , LCS_SET_MC_THREAD ) )
return - ERESTARTSYS ;
}
2005-04-17 02:20:36 +04:00
LCS_DBF_HEX ( 3 , setup , & card , sizeof ( void * ) ) ;
recover_state = card - > state ;
ret = lcs_stop_device ( card - > dev ) ;
ret = ccw_device_set_offline ( card - > read . ccwdev ) ;
ret = ccw_device_set_offline ( card - > write . ccwdev ) ;
if ( recover_state = = DEV_STATE_UP ) {
card - > state = DEV_STATE_RECOVER ;
}
if ( ret )
return ret ;
return 0 ;
}
2006-05-24 11:51:17 +04:00
static int
lcs_shutdown_device ( struct ccwgroup_device * ccwgdev )
{
return __lcs_shutdown_device ( ccwgdev , 0 ) ;
}
/**
* drive lcs recovery after startup and startlan initiated by Lan Gateway
*/
static int
lcs_recovery ( void * ptr )
{
struct lcs_card * card ;
struct ccwgroup_device * gdev ;
int rc ;
card = ( struct lcs_card * ) ptr ;
LCS_DBF_TEXT ( 4 , trace , " recover1 " ) ;
if ( ! lcs_do_run_thread ( card , LCS_RECOVERY_THREAD ) )
return 0 ;
LCS_DBF_TEXT ( 4 , trace , " recover2 " ) ;
gdev = card - > gdev ;
2008-12-25 15:39:33 +03:00
dev_warn ( & gdev - > dev ,
" A recovery process has been started for the LCS device \n " ) ;
2006-05-24 11:51:17 +04:00
rc = __lcs_shutdown_device ( gdev , 1 ) ;
rc = lcs_new_device ( gdev ) ;
if ( ! rc )
2008-12-25 15:39:33 +03:00
pr_info ( " Device %s successfully recovered! \n " ,
card - > dev - > name ) ;
2006-05-24 11:51:17 +04:00
else
2008-12-25 15:39:33 +03:00
pr_info ( " Device %s could not be recovered! \n " ,
card - > dev - > name ) ;
2006-05-24 11:51:17 +04:00
lcs_clear_thread_running_bit ( card , LCS_RECOVERY_THREAD ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
/**
* lcs_remove_device , free buffers and card
*/
static void
lcs_remove_device ( struct ccwgroup_device * ccwgdev )
{
struct lcs_card * card ;
card = ( struct lcs_card * ) ccwgdev - > dev . driver_data ;
if ( ! card )
return ;
LCS_DBF_TEXT ( 3 , setup , " remdev " ) ;
LCS_DBF_HEX ( 3 , setup , & card , sizeof ( void * ) ) ;
if ( ccwgdev - > state = = CCWGROUP_ONLINE ) {
lcs_shutdown_device ( ccwgdev ) ;
}
if ( card - > dev )
unregister_netdev ( card - > dev ) ;
sysfs_remove_group ( & ccwgdev - > dev . kobj , & lcs_attr_group ) ;
lcs_cleanup_card ( card ) ;
lcs_free_card ( card ) ;
put_device ( & ccwgdev - > dev ) ;
}
/**
* LCS ccwgroup driver registration
*/
static struct ccwgroup_driver lcs_group_driver = {
. owner = THIS_MODULE ,
. name = " lcs " ,
. max_slaves = 2 ,
. driver_id = 0xD3C3E2 ,
. probe = lcs_probe_device ,
. remove = lcs_remove_device ,
. set_online = lcs_new_device ,
. set_offline = lcs_shutdown_device ,
} ;
/**
* LCS Module / Kernel initialization function
*/
static int
__init lcs_init_module ( void )
{
int rc ;
2008-12-25 15:39:33 +03:00
pr_info ( " Loading %s \n " , version ) ;
2005-04-17 02:20:36 +04:00
rc = lcs_register_debug_facility ( ) ;
LCS_DBF_TEXT ( 0 , setup , " lcsinit " ) ;
if ( rc ) {
2008-12-25 15:39:33 +03:00
pr_err ( " Initialization failed \n " ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
rc = register_cu3088_discipline ( & lcs_group_driver ) ;
if ( rc ) {
2008-12-25 15:39:33 +03:00
pr_err ( " Initialization failed \n " ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
return 0 ;
}
/**
* LCS module cleanup function
*/
static void
__exit lcs_cleanup_module ( void )
{
2008-12-25 15:39:33 +03:00
pr_info ( " Terminating lcs module. \n " ) ;
2005-04-17 02:20:36 +04:00
LCS_DBF_TEXT ( 0 , trace , " cleanup " ) ;
unregister_cu3088_discipline ( & lcs_group_driver ) ;
lcs_unregister_debug_facility ( ) ;
}
module_init ( lcs_init_module ) ;
module_exit ( lcs_cleanup_module ) ;
2005-11-10 15:51:42 +03:00
MODULE_AUTHOR ( " Frank Pavlic <fpavlic@de.ibm.com> " ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;