2005-04-17 02:20:36 +04:00
/*
2006-02-01 14:06:31 +03:00
* linux / drivers / s390 / net / qeth_main . c
2005-04-17 02:20:36 +04:00
*
* Linux on zSeries OSA Express and HiperSockets support
*
* Copyright 2000 , 2003 IBM Corporation
*
* Author ( s ) : Original Code written by
* Utz Bacher ( utz . bacher @ de . ibm . 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
* Thomas Spatzier < tspat @ 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 .
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/string.h>
# include <linux/errno.h>
# include <linux/mm.h>
# include <linux/ip.h>
# include <linux/inetdevice.h>
# include <linux/netdevice.h>
# include <linux/sched.h>
# include <linux/workqueue.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/interrupt.h>
# include <linux/tcp.h>
# include <linux/icmp.h>
# include <linux/skbuff.h>
# include <linux/in.h>
# include <linux/igmp.h>
# include <linux/init.h>
# include <linux/reboot.h>
# include <linux/mii.h>
# include <linux/rcupdate.h>
# include <linux/ethtool.h>
# include <net/arp.h>
# include <net/ip.h>
# include <net/route.h>
# include <asm/ebcdic.h>
# include <asm/io.h>
# include <asm/qeth.h>
# include <asm/timex.h>
# include <asm/semaphore.h>
# include <asm/uaccess.h>
2006-01-06 11:19:14 +03:00
# include <asm/s390_rdev.h>
2005-04-17 02:20:36 +04:00
# include "qeth.h"
# include "qeth_mpc.h"
# include "qeth_fs.h"
# include "qeth_eddp.h"
# include "qeth_tso.h"
static const char * version = " qeth S/390 OSA-Express driver " ;
/**
* Debug Facility Stuff
*/
static debug_info_t * qeth_dbf_setup = NULL ;
static debug_info_t * qeth_dbf_data = NULL ;
static debug_info_t * qeth_dbf_misc = NULL ;
static debug_info_t * qeth_dbf_control = NULL ;
debug_info_t * qeth_dbf_trace = NULL ;
static debug_info_t * qeth_dbf_sense = NULL ;
static debug_info_t * qeth_dbf_qerr = NULL ;
DEFINE_PER_CPU ( char [ 256 ] , qeth_dbf_txt_buf ) ;
2006-07-03 11:25:26 +04:00
static struct lock_class_key qdio_out_skb_queue_key ;
2005-04-17 02:20:36 +04:00
/**
* some more definitions and declarations
*/
static unsigned int known_devices [ ] [ 10 ] = QETH_MODELLIST_ARRAY ;
/* list of our cards */
struct qeth_card_list_struct qeth_card_list ;
/*process list want to be notified*/
spinlock_t qeth_notify_lock ;
struct list_head qeth_notify_list ;
static void qeth_send_control_data_cb ( struct qeth_channel * ,
struct qeth_cmd_buffer * ) ;
/**
* here we go with function implementation
*/
static void
qeth_init_qdio_info ( struct qeth_card * card ) ;
static int
qeth_init_qdio_queues ( struct qeth_card * card ) ;
static int
qeth_alloc_qdio_buffers ( struct qeth_card * card ) ;
static void
qeth_free_qdio_buffers ( struct qeth_card * ) ;
static void
qeth_clear_qdio_buffers ( struct qeth_card * ) ;
static void
qeth_clear_ip_list ( struct qeth_card * , int , int ) ;
static void
qeth_clear_ipacmd_list ( struct qeth_card * ) ;
static int
qeth_qdio_clear_card ( struct qeth_card * , int ) ;
static void
qeth_clear_working_pool_list ( struct qeth_card * ) ;
static void
qeth_clear_cmd_buffers ( struct qeth_channel * ) ;
static int
qeth_stop ( struct net_device * ) ;
static void
qeth_clear_ipato_list ( struct qeth_card * ) ;
static int
qeth_is_addr_covered_by_ipato ( struct qeth_card * , struct qeth_ipaddr * ) ;
static void
qeth_irq_tasklet ( unsigned long ) ;
static int
qeth_set_online ( struct ccwgroup_device * ) ;
2005-05-12 22:39:09 +04:00
static int
__qeth_set_online ( struct ccwgroup_device * gdev , int recovery_mode ) ;
2005-04-17 02:20:36 +04:00
static struct qeth_ipaddr *
qeth_get_addr_buffer ( enum qeth_prot_versions ) ;
static void
qeth_set_multicast_list ( struct net_device * ) ;
2005-11-10 15:51:25 +03:00
static void
qeth_setadp_promisc_mode ( struct qeth_card * ) ;
2005-04-17 02:20:36 +04:00
static void
qeth_notify_processes ( void )
{
/*notify all registered processes */
struct qeth_notify_list_struct * n_entry ;
QETH_DBF_TEXT ( trace , 3 , " procnoti " ) ;
spin_lock ( & qeth_notify_lock ) ;
list_for_each_entry ( n_entry , & qeth_notify_list , list ) {
send_sig ( n_entry - > signum , n_entry - > task , 1 ) ;
}
spin_unlock ( & qeth_notify_lock ) ;
}
int
qeth_notifier_unregister ( struct task_struct * p )
{
struct qeth_notify_list_struct * n_entry , * tmp ;
QETH_DBF_TEXT ( trace , 2 , " notunreg " ) ;
spin_lock ( & qeth_notify_lock ) ;
list_for_each_entry_safe ( n_entry , tmp , & qeth_notify_list , list ) {
if ( n_entry - > task = = p ) {
list_del ( & n_entry - > list ) ;
kfree ( n_entry ) ;
goto out ;
}
}
out :
spin_unlock ( & qeth_notify_lock ) ;
return 0 ;
}
int
qeth_notifier_register ( struct task_struct * p , int signum )
{
struct qeth_notify_list_struct * n_entry ;
/*check first if entry already exists*/
spin_lock ( & qeth_notify_lock ) ;
list_for_each_entry ( n_entry , & qeth_notify_list , list ) {
if ( n_entry - > task = = p ) {
n_entry - > signum = signum ;
spin_unlock ( & qeth_notify_lock ) ;
return 0 ;
}
}
spin_unlock ( & qeth_notify_lock ) ;
n_entry = ( struct qeth_notify_list_struct * )
kmalloc ( sizeof ( struct qeth_notify_list_struct ) , GFP_KERNEL ) ;
if ( ! n_entry )
return - ENOMEM ;
n_entry - > task = p ;
n_entry - > signum = signum ;
spin_lock ( & qeth_notify_lock ) ;
list_add ( & n_entry - > list , & qeth_notify_list ) ;
spin_unlock ( & qeth_notify_lock ) ;
return 0 ;
}
/**
* free channel command buffers
*/
static void
qeth_clean_channel ( struct qeth_channel * channel )
{
int cnt ;
QETH_DBF_TEXT ( setup , 2 , " freech " ) ;
for ( cnt = 0 ; cnt < QETH_CMD_BUFFER_NO ; cnt + + )
kfree ( channel - > iob [ cnt ] . data ) ;
}
/**
* free card
*/
static void
qeth_free_card ( struct qeth_card * card )
{
QETH_DBF_TEXT ( setup , 2 , " freecrd " ) ;
QETH_DBF_HEX ( setup , 2 , & card , sizeof ( void * ) ) ;
qeth_clean_channel ( & card - > read ) ;
qeth_clean_channel ( & card - > write ) ;
if ( card - > dev )
free_netdev ( card - > dev ) ;
qeth_clear_ip_list ( card , 0 , 0 ) ;
qeth_clear_ipato_list ( card ) ;
kfree ( card - > ip_tbd_list ) ;
qeth_free_qdio_buffers ( card ) ;
kfree ( card ) ;
}
/**
* alloc memory for command buffer per channel
*/
static int
qeth_setup_channel ( struct qeth_channel * channel )
{
int cnt ;
QETH_DBF_TEXT ( setup , 2 , " setupch " ) ;
for ( cnt = 0 ; cnt < QETH_CMD_BUFFER_NO ; cnt + + ) {
channel - > iob [ cnt ] . data = ( char * )
kmalloc ( QETH_BUFSIZE , GFP_DMA | GFP_KERNEL ) ;
if ( channel - > iob [ cnt ] . data = = NULL )
break ;
channel - > iob [ cnt ] . state = BUF_STATE_FREE ;
channel - > iob [ cnt ] . channel = channel ;
channel - > iob [ cnt ] . callback = qeth_send_control_data_cb ;
channel - > iob [ cnt ] . rc = 0 ;
}
if ( cnt < QETH_CMD_BUFFER_NO ) {
while ( cnt - - > 0 )
kfree ( channel - > iob [ cnt ] . data ) ;
return - ENOMEM ;
}
channel - > buf_no = 0 ;
channel - > io_buf_no = 0 ;
atomic_set ( & channel - > irq_pending , 0 ) ;
spin_lock_init ( & channel - > iob_lock ) ;
init_waitqueue_head ( & channel - > wait_q ) ;
channel - > irq_tasklet . data = ( unsigned long ) channel ;
channel - > irq_tasklet . func = qeth_irq_tasklet ;
return 0 ;
}
/**
* alloc memory for card structure
*/
static struct qeth_card *
qeth_alloc_card ( void )
{
struct qeth_card * card ;
QETH_DBF_TEXT ( setup , 2 , " alloccrd " ) ;
2006-03-24 14:15:31 +03:00
card = kzalloc ( sizeof ( struct qeth_card ) , GFP_DMA | GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! card )
return NULL ;
QETH_DBF_HEX ( setup , 2 , & card , sizeof ( void * ) ) ;
if ( qeth_setup_channel ( & card - > read ) ) {
kfree ( card ) ;
return NULL ;
}
if ( qeth_setup_channel ( & card - > write ) ) {
qeth_clean_channel ( & card - > read ) ;
kfree ( card ) ;
return NULL ;
}
return card ;
}
static long
2007-05-04 20:47:52 +04:00
__qeth_check_irb_error ( struct ccw_device * cdev , unsigned long intparm ,
struct irb * irb )
2005-04-17 02:20:36 +04:00
{
if ( ! IS_ERR ( irb ) )
return 0 ;
switch ( PTR_ERR ( irb ) ) {
case - EIO :
PRINT_WARN ( " i/o-error on device %s \n " , cdev - > dev . bus_id ) ;
QETH_DBF_TEXT ( trace , 2 , " ckirberr " ) ;
QETH_DBF_TEXT_ ( trace , 2 , " rc%d " , - EIO ) ;
break ;
case - ETIMEDOUT :
PRINT_WARN ( " timeout on device %s \n " , cdev - > dev . bus_id ) ;
QETH_DBF_TEXT ( trace , 2 , " ckirberr " ) ;
QETH_DBF_TEXT_ ( trace , 2 , " rc%d " , - ETIMEDOUT ) ;
2007-05-04 20:47:52 +04:00
if ( intparm = = QETH_RCD_PARM ) {
struct qeth_card * card = CARD_FROM_CDEV ( cdev ) ;
if ( card & & ( card - > data . ccwdev = = cdev ) ) {
card - > data . state = CH_STATE_DOWN ;
wake_up ( & card - > wait_q ) ;
}
}
2005-04-17 02:20:36 +04:00
break ;
default :
PRINT_WARN ( " unknown error %ld on device %s \n " , PTR_ERR ( irb ) ,
cdev - > dev . bus_id ) ;
QETH_DBF_TEXT ( trace , 2 , " ckirberr " ) ;
QETH_DBF_TEXT ( trace , 2 , " rc??? " ) ;
}
return PTR_ERR ( irb ) ;
}
static int
qeth_get_problem ( struct ccw_device * cdev , struct irb * irb )
{
int dstat , cstat ;
char * sense ;
sense = ( char * ) irb - > ecw ;
cstat = irb - > scsw . cstat ;
dstat = irb - > scsw . dstat ;
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 ) ) {
QETH_DBF_TEXT ( trace , 2 , " CGENCHK " ) ;
PRINT_WARN ( " check on device %s, dstat=x%x, cstat=x%x " ,
cdev - > dev . bus_id , dstat , cstat ) ;
HEXDUMP16 ( WARN , " irb: " , irb ) ;
HEXDUMP16 ( WARN , " irb: " , ( ( char * ) irb ) + 32 ) ;
return 1 ;
}
if ( dstat & DEV_STAT_UNIT_CHECK ) {
if ( sense [ SENSE_RESETTING_EVENT_BYTE ] &
SENSE_RESETTING_EVENT_FLAG ) {
QETH_DBF_TEXT ( trace , 2 , " REVIND " ) ;
return 1 ;
}
if ( sense [ SENSE_COMMAND_REJECT_BYTE ] &
SENSE_COMMAND_REJECT_FLAG ) {
QETH_DBF_TEXT ( trace , 2 , " CMDREJi " ) ;
return 0 ;
}
if ( ( sense [ 2 ] = = 0xaf ) & & ( sense [ 3 ] = = 0xfe ) ) {
QETH_DBF_TEXT ( trace , 2 , " AFFE " ) ;
return 1 ;
}
if ( ( ! sense [ 0 ] ) & & ( ! sense [ 1 ] ) & & ( ! sense [ 2 ] ) & & ( ! sense [ 3 ] ) ) {
QETH_DBF_TEXT ( trace , 2 , " ZEROSEN " ) ;
return 0 ;
}
QETH_DBF_TEXT ( trace , 2 , " DGENCHK " ) ;
return 1 ;
}
return 0 ;
}
static int qeth_issue_next_read ( struct qeth_card * ) ;
/**
* interrupt handler
*/
static void
qeth_irq ( struct ccw_device * cdev , unsigned long intparm , struct irb * irb )
{
int rc ;
int cstat , dstat ;
struct qeth_cmd_buffer * buffer ;
struct qeth_channel * channel ;
struct qeth_card * card ;
QETH_DBF_TEXT ( trace , 5 , " irq " ) ;
2007-05-04 20:47:52 +04:00
if ( __qeth_check_irb_error ( cdev , intparm , irb ) )
2005-04-17 02:20:36 +04:00
return ;
cstat = irb - > scsw . cstat ;
dstat = irb - > scsw . dstat ;
card = CARD_FROM_CDEV ( cdev ) ;
if ( ! card )
return ;
if ( card - > read . ccwdev = = cdev ) {
channel = & card - > read ;
QETH_DBF_TEXT ( trace , 5 , " read " ) ;
} else if ( card - > write . ccwdev = = cdev ) {
channel = & card - > write ;
QETH_DBF_TEXT ( trace , 5 , " write " ) ;
} else {
channel = & card - > data ;
QETH_DBF_TEXT ( trace , 5 , " data " ) ;
}
atomic_set ( & channel - > irq_pending , 0 ) ;
if ( irb - > scsw . fctl & ( SCSW_FCTL_CLEAR_FUNC ) )
channel - > state = CH_STATE_STOPPED ;
if ( irb - > scsw . fctl & ( SCSW_FCTL_HALT_FUNC ) )
channel - > state = CH_STATE_HALTED ;
/*let's wake up immediately on data channel*/
2007-05-04 20:47:52 +04:00
if ( ( channel = = & card - > data ) & & ( intparm ! = 0 ) & &
( intparm ! = QETH_RCD_PARM ) )
2005-04-17 02:20:36 +04:00
goto out ;
if ( intparm = = QETH_CLEAR_CHANNEL_PARM ) {
QETH_DBF_TEXT ( trace , 6 , " clrchpar " ) ;
/* we don't have to handle this further */
intparm = 0 ;
}
if ( intparm = = QETH_HALT_CHANNEL_PARM ) {
QETH_DBF_TEXT ( trace , 6 , " hltchpar " ) ;
/* we don't have to handle this further */
intparm = 0 ;
}
if ( ( dstat & DEV_STAT_UNIT_EXCEP ) | |
( dstat & DEV_STAT_UNIT_CHECK ) | |
( cstat ) ) {
if ( irb - > esw . esw0 . erw . cons ) {
/* TODO: we should make this s390dbf */
PRINT_WARN ( " sense data available on channel %s. \n " ,
CHANNEL_ID ( channel ) ) ;
PRINT_WARN ( " cstat 0x%X \n dstat 0x%X \n " , cstat , dstat ) ;
HEXDUMP16 ( WARN , " irb: " , irb ) ;
HEXDUMP16 ( WARN , " sense data: " , irb - > ecw ) ;
}
2007-05-04 20:47:52 +04:00
if ( intparm = = QETH_RCD_PARM ) {
channel - > state = CH_STATE_DOWN ;
goto out ;
}
2005-04-17 02:20:36 +04:00
rc = qeth_get_problem ( cdev , irb ) ;
if ( rc ) {
qeth_schedule_recovery ( card ) ;
goto out ;
}
}
2007-05-04 20:47:52 +04:00
if ( intparm = = QETH_RCD_PARM ) {
channel - > state = CH_STATE_RCD_DONE ;
goto out ;
}
2005-04-17 02:20:36 +04:00
if ( intparm ) {
buffer = ( struct qeth_cmd_buffer * ) __va ( ( addr_t ) intparm ) ;
buffer - > state = BUF_STATE_PROCESSED ;
}
if ( channel = = & card - > data )
return ;
if ( channel = = & card - > read & &
channel - > state = = CH_STATE_UP )
qeth_issue_next_read ( card ) ;
2007-01-08 19:30:11 +03:00
qeth_irq_tasklet ( ( unsigned long ) channel ) ;
2005-04-17 02:20:36 +04:00
return ;
out :
wake_up ( & card - > wait_q ) ;
}
/**
* tasklet function scheduled from irq handler
*/
static void
qeth_irq_tasklet ( unsigned long data )
{
struct qeth_card * card ;
struct qeth_channel * channel ;
struct qeth_cmd_buffer * iob ;
__u8 index ;
QETH_DBF_TEXT ( trace , 5 , " irqtlet " ) ;
channel = ( struct qeth_channel * ) data ;
iob = channel - > iob ;
index = channel - > buf_no ;
card = CARD_FROM_CDEV ( channel - > ccwdev ) ;
while ( iob [ index ] . state = = BUF_STATE_PROCESSED ) {
if ( iob [ index ] . callback ! = NULL ) {
iob [ index ] . callback ( channel , iob + index ) ;
}
index = ( index + 1 ) % QETH_CMD_BUFFER_NO ;
}
channel - > buf_no = index ;
wake_up ( & card - > wait_q ) ;
}
2005-05-12 22:39:09 +04:00
static int qeth_stop_card ( struct qeth_card * , int ) ;
2005-04-17 02:20:36 +04:00
static int
2005-05-12 22:39:09 +04:00
__qeth_set_offline ( struct ccwgroup_device * cgdev , int recovery_mode )
2005-04-17 02:20:36 +04:00
{
struct qeth_card * card = ( struct qeth_card * ) cgdev - > dev . driver_data ;
2005-09-30 12:17:24 +04:00
int rc = 0 , rc2 = 0 , rc3 = 0 ;
2005-04-17 02:20:36 +04:00
enum qeth_card_states recover_flag ;
QETH_DBF_TEXT ( setup , 3 , " setoffl " ) ;
QETH_DBF_HEX ( setup , 3 , & card , sizeof ( void * ) ) ;
2006-05-27 05:58:38 +04:00
2006-02-07 19:04:38 +03:00
if ( card - > dev & & netif_carrier_ok ( card - > dev ) )
netif_carrier_off ( card - > dev ) ;
2005-04-17 02:20:36 +04:00
recover_flag = card - > state ;
2005-05-12 22:39:09 +04:00
if ( qeth_stop_card ( card , recovery_mode ) = = - ERESTARTSYS ) {
2005-04-17 02:20:36 +04:00
PRINT_WARN ( " Stopping card %s interrupted by user! \n " ,
CARD_BUS_ID ( card ) ) ;
return - ERESTARTSYS ;
}
2005-09-30 12:17:24 +04:00
rc = ccw_device_set_offline ( CARD_DDEV ( card ) ) ;
rc2 = ccw_device_set_offline ( CARD_WDEV ( card ) ) ;
rc3 = ccw_device_set_offline ( CARD_RDEV ( card ) ) ;
if ( ! rc )
rc = ( rc2 ) ? rc2 : rc3 ;
if ( rc )
2005-04-17 02:20:36 +04:00
QETH_DBF_TEXT_ ( setup , 2 , " 1err%d " , rc ) ;
if ( recover_flag = = CARD_STATE_UP )
card - > state = CARD_STATE_RECOVER ;
qeth_notify_processes ( ) ;
return 0 ;
}
2005-05-12 22:39:09 +04:00
static int
qeth_set_offline ( struct ccwgroup_device * cgdev )
{
return __qeth_set_offline ( cgdev , 0 ) ;
}
2005-04-17 02:20:36 +04:00
static int
qeth_wait_for_threads ( struct qeth_card * card , unsigned long threads ) ;
static void
qeth_remove_device ( struct ccwgroup_device * cgdev )
{
struct qeth_card * card = ( struct qeth_card * ) cgdev - > dev . driver_data ;
unsigned long flags ;
QETH_DBF_TEXT ( setup , 3 , " rmdev " ) ;
QETH_DBF_HEX ( setup , 3 , & card , sizeof ( void * ) ) ;
if ( ! card )
return ;
if ( qeth_wait_for_threads ( card , 0xffffffff ) )
return ;
if ( cgdev - > state = = CCWGROUP_ONLINE ) {
card - > use_hard_stop = 1 ;
qeth_set_offline ( cgdev ) ;
}
/* remove form our internal list */
write_lock_irqsave ( & qeth_card_list . rwlock , flags ) ;
list_del ( & card - > list ) ;
write_unlock_irqrestore ( & qeth_card_list . rwlock , flags ) ;
if ( card - > dev )
unregister_netdev ( card - > dev ) ;
qeth_remove_device_attributes ( & cgdev - > dev ) ;
qeth_free_card ( card ) ;
cgdev - > dev . driver_data = NULL ;
put_device ( & cgdev - > dev ) ;
}
static int
qeth_register_addr_entry ( struct qeth_card * , struct qeth_ipaddr * ) ;
static int
qeth_deregister_addr_entry ( struct qeth_card * , struct qeth_ipaddr * ) ;
/**
* Add / remove address to / from card ' s ip list , i . e . try to add or remove
* reference to / from an IP address that is already registered on the card .
* Returns :
* 0 address was on card and its reference count has been adjusted ,
* but is still > 0 , so nothing has to be done
* also returns 0 if card was not on card and the todo was to delete
* the address - > there is also nothing to be done
* 1 address was not on card and the todo is to add it to the card ' s ip
* list
* - 1 address was on card and its reference count has been decremented
* to < = 0 by the todo - > address must be removed from card
*/
static int
__qeth_ref_ip_on_card ( struct qeth_card * card , struct qeth_ipaddr * todo ,
struct qeth_ipaddr * * __addr )
{
struct qeth_ipaddr * addr ;
int found = 0 ;
list_for_each_entry ( addr , & card - > ip_list , entry ) {
2005-11-10 15:49:28 +03:00
if ( card - > options . layer2 ) {
if ( ( addr - > type = = todo - > type ) & &
2006-05-27 05:58:38 +04:00
( memcmp ( & addr - > mac , & todo - > mac ,
2005-11-10 15:49:28 +03:00
OSA_ADDR_LEN ) = = 0 ) ) {
found = 1 ;
break ;
}
continue ;
2006-05-27 05:58:38 +04:00
}
2005-04-17 02:20:36 +04:00
if ( ( addr - > proto = = QETH_PROT_IPV4 ) & &
( todo - > proto = = QETH_PROT_IPV4 ) & &
( addr - > type = = todo - > type ) & &
( addr - > u . a4 . addr = = todo - > u . a4 . addr ) & &
2005-11-10 15:49:28 +03:00
( addr - > u . a4 . mask = = todo - > u . a4 . mask ) ) {
2005-04-17 02:20:36 +04:00
found = 1 ;
break ;
}
if ( ( addr - > proto = = QETH_PROT_IPV6 ) & &
( todo - > proto = = QETH_PROT_IPV6 ) & &
( addr - > type = = todo - > type ) & &
( addr - > u . a6 . pfxlen = = todo - > u . a6 . pfxlen ) & &
( memcmp ( & addr - > u . a6 . addr , & todo - > u . a6 . addr ,
2005-11-10 15:49:28 +03:00
sizeof ( struct in6_addr ) ) = = 0 ) ) {
2005-04-17 02:20:36 +04:00
found = 1 ;
break ;
}
}
2005-11-10 15:49:28 +03:00
if ( found ) {
2005-04-17 02:20:36 +04:00
addr - > users + = todo - > users ;
if ( addr - > users < = 0 ) {
* __addr = addr ;
return - 1 ;
} else {
/* for VIPA and RXIP limit refcount to 1 */
if ( addr - > type ! = QETH_IP_TYPE_NORMAL )
addr - > users = 1 ;
return 0 ;
}
}
2005-11-10 15:49:28 +03:00
if ( todo - > users > 0 ) {
2005-04-17 02:20:36 +04:00
/* for VIPA and RXIP limit refcount to 1 */
if ( todo - > type ! = QETH_IP_TYPE_NORMAL )
todo - > users = 1 ;
return 1 ;
} else
return 0 ;
}
2007-02-05 23:18:53 +03:00
static int
2005-04-17 02:20:36 +04:00
__qeth_address_exists_in_list ( struct list_head * list , struct qeth_ipaddr * addr ,
int same_type )
{
struct qeth_ipaddr * tmp ;
list_for_each_entry ( tmp , list , entry ) {
if ( ( tmp - > proto = = QETH_PROT_IPV4 ) & &
( addr - > proto = = QETH_PROT_IPV4 ) & &
( ( same_type & & ( tmp - > type = = addr - > type ) ) | |
( ! same_type & & ( tmp - > type ! = addr - > type ) ) ) & &
( tmp - > u . a4 . addr = = addr - > u . a4 . addr ) ) {
return 1 ;
}
if ( ( tmp - > proto = = QETH_PROT_IPV6 ) & &
( addr - > proto = = QETH_PROT_IPV6 ) & &
( ( same_type & & ( tmp - > type = = addr - > type ) ) | |
( ! same_type & & ( tmp - > type ! = addr - > type ) ) ) & &
( memcmp ( & tmp - > u . a6 . addr , & addr - > u . a6 . addr ,
sizeof ( struct in6_addr ) ) = = 0 ) ) {
return 1 ;
}
}
return 0 ;
}
/*
* Add IP to be added to todo list . If there is already an " add todo "
* in this list we just incremenent the reference count .
* Returns 0 if we just incremented reference count .
*/
static int
__qeth_insert_ip_todo ( struct qeth_card * card , struct qeth_ipaddr * addr , int add )
{
struct qeth_ipaddr * tmp , * t ;
int found = 0 ;
list_for_each_entry_safe ( tmp , t , card - > ip_tbd_list , entry ) {
if ( ( addr - > type = = QETH_IP_TYPE_DEL_ALL_MC ) & &
( tmp - > type = = QETH_IP_TYPE_DEL_ALL_MC ) )
return 0 ;
2005-11-10 15:49:28 +03:00
if ( card - > options . layer2 ) {
if ( ( tmp - > type = = addr - > type ) & &
( tmp - > is_multicast = = addr - > is_multicast ) & &
2006-05-27 05:58:38 +04:00
( memcmp ( & tmp - > mac , & addr - > mac ,
2005-11-10 15:49:28 +03:00
OSA_ADDR_LEN ) = = 0 ) ) {
found = 1 ;
break ;
}
continue ;
2006-05-27 05:58:38 +04:00
}
2005-04-17 02:20:36 +04:00
if ( ( tmp - > proto = = QETH_PROT_IPV4 ) & &
( addr - > proto = = QETH_PROT_IPV4 ) & &
( tmp - > type = = addr - > type ) & &
( tmp - > is_multicast = = addr - > is_multicast ) & &
( tmp - > u . a4 . addr = = addr - > u . a4 . addr ) & &
2005-11-10 15:49:28 +03:00
( tmp - > u . a4 . mask = = addr - > u . a4 . mask ) ) {
2005-04-17 02:20:36 +04:00
found = 1 ;
break ;
}
if ( ( tmp - > proto = = QETH_PROT_IPV6 ) & &
( addr - > proto = = QETH_PROT_IPV6 ) & &
( tmp - > type = = addr - > type ) & &
( tmp - > is_multicast = = addr - > is_multicast ) & &
( tmp - > u . a6 . pfxlen = = addr - > u . a6 . pfxlen ) & &
( memcmp ( & tmp - > u . a6 . addr , & addr - > u . a6 . addr ,
2005-11-10 15:49:28 +03:00
sizeof ( struct in6_addr ) ) = = 0 ) ) {
2005-04-17 02:20:36 +04:00
found = 1 ;
break ;
}
}
if ( found ) {
if ( addr - > users ! = 0 )
tmp - > users + = addr - > users ;
else
tmp - > users + = add ? 1 : - 1 ;
2005-11-10 15:49:28 +03:00
if ( tmp - > users = = 0 ) {
2005-04-17 02:20:36 +04:00
list_del ( & tmp - > entry ) ;
kfree ( tmp ) ;
}
return 0 ;
} else {
if ( addr - > type = = QETH_IP_TYPE_DEL_ALL_MC )
list_add ( & addr - > entry , card - > ip_tbd_list ) ;
else {
if ( addr - > users = = 0 )
addr - > users + = add ? 1 : - 1 ;
if ( add & & ( addr - > type = = QETH_IP_TYPE_NORMAL ) & &
qeth_is_addr_covered_by_ipato ( card , addr ) ) {
QETH_DBF_TEXT ( trace , 2 , " tkovaddr " ) ;
addr - > set_flags | = QETH_IPA_SETIP_TAKEOVER_FLAG ;
}
list_add_tail ( & addr - > entry , card - > ip_tbd_list ) ;
}
return 1 ;
}
}
/**
* Remove IP address from list
*/
static int
qeth_delete_ip ( struct qeth_card * card , struct qeth_ipaddr * addr )
{
unsigned long flags ;
int rc = 0 ;
2005-11-10 15:49:28 +03:00
QETH_DBF_TEXT ( trace , 4 , " delip " ) ;
if ( card - > options . layer2 )
QETH_DBF_HEX ( trace , 4 , & addr - > mac , 6 ) ;
else if ( addr - > proto = = QETH_PROT_IPV4 )
QETH_DBF_HEX ( trace , 4 , & addr - > u . a4 . addr , 4 ) ;
2005-04-17 02:20:36 +04:00
else {
2005-11-10 15:49:28 +03:00
QETH_DBF_HEX ( trace , 4 , & addr - > u . a6 . addr , 8 ) ;
QETH_DBF_HEX ( trace , 4 , ( ( char * ) & addr - > u . a6 . addr ) + 8 , 8 ) ;
2005-04-17 02:20:36 +04:00
}
spin_lock_irqsave ( & card - > ip_lock , flags ) ;
rc = __qeth_insert_ip_todo ( card , addr , 0 ) ;
spin_unlock_irqrestore ( & card - > ip_lock , flags ) ;
return rc ;
}
static int
qeth_add_ip ( struct qeth_card * card , struct qeth_ipaddr * addr )
{
unsigned long flags ;
int rc = 0 ;
2005-11-10 15:49:28 +03:00
QETH_DBF_TEXT ( trace , 4 , " addip " ) ;
if ( card - > options . layer2 )
QETH_DBF_HEX ( trace , 4 , & addr - > mac , 6 ) ;
else if ( addr - > proto = = QETH_PROT_IPV4 )
QETH_DBF_HEX ( trace , 4 , & addr - > u . a4 . addr , 4 ) ;
2005-04-17 02:20:36 +04:00
else {
2005-11-10 15:49:28 +03:00
QETH_DBF_HEX ( trace , 4 , & addr - > u . a6 . addr , 8 ) ;
QETH_DBF_HEX ( trace , 4 , ( ( char * ) & addr - > u . a6 . addr ) + 8 , 8 ) ;
2005-04-17 02:20:36 +04:00
}
spin_lock_irqsave ( & card - > ip_lock , flags ) ;
rc = __qeth_insert_ip_todo ( card , addr , 1 ) ;
spin_unlock_irqrestore ( & card - > ip_lock , flags ) ;
return rc ;
}
2007-02-05 23:18:53 +03:00
static void
2005-04-17 02:20:36 +04:00
__qeth_delete_all_mc ( struct qeth_card * card , unsigned long * flags )
{
struct qeth_ipaddr * addr , * tmp ;
int rc ;
2005-11-10 15:50:58 +03:00
again :
2005-04-17 02:20:36 +04:00
list_for_each_entry_safe ( addr , tmp , & card - > ip_list , entry ) {
if ( addr - > is_multicast ) {
spin_unlock_irqrestore ( & card - > ip_lock , * flags ) ;
rc = qeth_deregister_addr_entry ( card , addr ) ;
spin_lock_irqsave ( & card - > ip_lock , * flags ) ;
if ( ! rc ) {
list_del ( & addr - > entry ) ;
kfree ( addr ) ;
2005-11-10 15:50:58 +03:00
goto again ;
2005-04-17 02:20:36 +04:00
}
}
}
}
static void
qeth_set_ip_addr_list ( struct qeth_card * card )
{
struct list_head * tbd_list ;
struct qeth_ipaddr * todo , * addr ;
unsigned long flags ;
int rc ;
QETH_DBF_TEXT ( trace , 2 , " sdiplist " ) ;
QETH_DBF_HEX ( trace , 2 , & card , sizeof ( void * ) ) ;
spin_lock_irqsave ( & card - > ip_lock , flags ) ;
tbd_list = card - > ip_tbd_list ;
card - > ip_tbd_list = kmalloc ( sizeof ( struct list_head ) , GFP_ATOMIC ) ;
if ( ! card - > ip_tbd_list ) {
QETH_DBF_TEXT ( trace , 0 , " silnomem " ) ;
card - > ip_tbd_list = tbd_list ;
spin_unlock_irqrestore ( & card - > ip_lock , flags ) ;
return ;
} else
INIT_LIST_HEAD ( card - > ip_tbd_list ) ;
while ( ! list_empty ( tbd_list ) ) {
todo = list_entry ( tbd_list - > next , struct qeth_ipaddr , entry ) ;
list_del ( & todo - > entry ) ;
if ( todo - > type = = QETH_IP_TYPE_DEL_ALL_MC ) {
__qeth_delete_all_mc ( card , & flags ) ;
kfree ( todo ) ;
continue ;
}
rc = __qeth_ref_ip_on_card ( card , todo , & addr ) ;
if ( rc = = 0 ) {
/* nothing to be done; only adjusted refcount */
kfree ( todo ) ;
} else if ( rc = = 1 ) {
/* new entry to be added to on-card list */
spin_unlock_irqrestore ( & card - > ip_lock , flags ) ;
rc = qeth_register_addr_entry ( card , todo ) ;
spin_lock_irqsave ( & card - > ip_lock , flags ) ;
if ( ! rc )
list_add_tail ( & todo - > entry , & card - > ip_list ) ;
else
kfree ( todo ) ;
} else if ( rc = = - 1 ) {
/* on-card entry to be removed */
list_del_init ( & addr - > entry ) ;
spin_unlock_irqrestore ( & card - > ip_lock , flags ) ;
rc = qeth_deregister_addr_entry ( card , addr ) ;
spin_lock_irqsave ( & card - > ip_lock , flags ) ;
if ( ! rc )
kfree ( addr ) ;
else
list_add_tail ( & addr - > entry , & card - > ip_list ) ;
kfree ( todo ) ;
}
}
spin_unlock_irqrestore ( & card - > ip_lock , flags ) ;
kfree ( tbd_list ) ;
}
static void qeth_delete_mc_addresses ( struct qeth_card * ) ;
static void qeth_add_multicast_ipv4 ( struct qeth_card * ) ;
2005-11-10 15:49:28 +03:00
static void qeth_layer2_add_multicast ( struct qeth_card * ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_QETH_IPV6
static void qeth_add_multicast_ipv6 ( struct qeth_card * ) ;
# endif
2007-02-05 23:18:53 +03:00
static int
2005-04-17 02:20:36 +04:00
qeth_set_thread_start_bit ( struct qeth_card * card , unsigned long thread )
{
unsigned long flags ;
spin_lock_irqsave ( & card - > thread_mask_lock , flags ) ;
if ( ! ( card - > thread_allowed_mask & thread ) | |
( card - > thread_start_mask & thread ) ) {
spin_unlock_irqrestore ( & card - > thread_mask_lock , flags ) ;
return - EPERM ;
}
card - > thread_start_mask | = thread ;
spin_unlock_irqrestore ( & card - > thread_mask_lock , flags ) ;
return 0 ;
}
static void
qeth_clear_thread_start_bit ( struct qeth_card * card , unsigned long thread )
{
unsigned long flags ;
spin_lock_irqsave ( & card - > thread_mask_lock , flags ) ;
card - > thread_start_mask & = ~ thread ;
spin_unlock_irqrestore ( & card - > thread_mask_lock , flags ) ;
wake_up ( & card - > wait_q ) ;
}
static void
qeth_clear_thread_running_bit ( struct qeth_card * card , unsigned long thread )
{
unsigned long flags ;
spin_lock_irqsave ( & card - > thread_mask_lock , flags ) ;
card - > thread_running_mask & = ~ thread ;
spin_unlock_irqrestore ( & card - > thread_mask_lock , flags ) ;
wake_up ( & card - > wait_q ) ;
}
2007-02-05 23:18:53 +03:00
static int
2005-04-17 02:20:36 +04:00
__qeth_do_run_thread ( struct qeth_card * card , unsigned long thread )
{
unsigned long flags ;
int rc = 0 ;
spin_lock_irqsave ( & card - > thread_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 - > thread_mask_lock , flags ) ;
return rc ;
}
static int
qeth_do_run_thread ( struct qeth_card * card , unsigned long thread )
{
int rc = 0 ;
wait_event ( card - > wait_q ,
( rc = __qeth_do_run_thread ( card , thread ) ) > = 0 ) ;
return rc ;
}
static int
qeth_recover ( void * ptr )
{
struct qeth_card * card ;
int rc = 0 ;
card = ( struct qeth_card * ) ptr ;
daemonize ( " qeth_recover " ) ;
QETH_DBF_TEXT ( trace , 2 , " recover1 " ) ;
QETH_DBF_HEX ( trace , 2 , & card , sizeof ( void * ) ) ;
if ( ! qeth_do_run_thread ( card , QETH_RECOVER_THREAD ) )
return 0 ;
QETH_DBF_TEXT ( trace , 2 , " recover2 " ) ;
PRINT_WARN ( " Recovery of device %s started ... \n " ,
CARD_BUS_ID ( card ) ) ;
card - > use_hard_stop = 1 ;
2005-05-12 22:39:09 +04:00
__qeth_set_offline ( card - > gdev , 1 ) ;
rc = __qeth_set_online ( card - > gdev , 1 ) ;
2005-04-17 02:20:36 +04:00
if ( ! rc )
PRINT_INFO ( " Device %s successfully recovered! \n " ,
CARD_BUS_ID ( card ) ) ;
else
PRINT_INFO ( " Device %s could not be recovered! \n " ,
CARD_BUS_ID ( card ) ) ;
/* don't run another scheduled recovery */
qeth_clear_thread_start_bit ( card , QETH_RECOVER_THREAD ) ;
qeth_clear_thread_running_bit ( card , QETH_RECOVER_THREAD ) ;
return 0 ;
}
void
qeth_schedule_recovery ( struct qeth_card * card )
{
QETH_DBF_TEXT ( trace , 2 , " startrec " ) ;
if ( qeth_set_thread_start_bit ( card , QETH_RECOVER_THREAD ) = = 0 )
schedule_work ( & card - > kernel_thread_starter ) ;
}
static int
qeth_do_start_thread ( struct qeth_card * card , unsigned long thread )
{
unsigned long flags ;
int rc = 0 ;
spin_lock_irqsave ( & card - > thread_mask_lock , flags ) ;
QETH_DBF_TEXT_ ( trace , 4 , " %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 - > thread_mask_lock , flags ) ;
return rc ;
}
static void
2006-12-06 22:18:20 +03:00
qeth_start_kernel_thread ( struct work_struct * work )
2005-04-17 02:20:36 +04:00
{
2006-12-06 22:18:20 +03:00
struct qeth_card * card = container_of ( work , struct qeth_card , kernel_thread_starter ) ;
2005-04-17 02:20:36 +04:00
QETH_DBF_TEXT ( trace , 2 , " strthrd " ) ;
if ( card - > read . state ! = CH_STATE_UP & &
card - > write . state ! = CH_STATE_UP )
return ;
if ( qeth_do_start_thread ( card , QETH_RECOVER_THREAD ) )
kernel_thread ( qeth_recover , ( void * ) card , SIGCHLD ) ;
}
static void
qeth_set_intial_options ( struct qeth_card * card )
{
card - > options . route4 . type = NO_ROUTER ;
# ifdef CONFIG_QETH_IPV6
card - > options . route6 . type = NO_ROUTER ;
# endif /* QETH_IPV6 */
card - > options . checksum_type = QETH_CHECKSUM_DEFAULT ;
card - > options . broadcast_mode = QETH_TR_BROADCAST_ALLRINGS ;
card - > options . macaddr_mode = QETH_TR_MACADDR_NONCANONICAL ;
card - > options . fake_broadcast = 0 ;
card - > options . add_hhlen = DEFAULT_ADD_HHLEN ;
card - > options . fake_ll = 0 ;
2005-09-30 12:19:19 +04:00
if ( card - > info . type = = QETH_CARD_TYPE_OSN )
card - > options . layer2 = 1 ;
else
card - > options . layer2 = 0 ;
2007-01-08 19:29:34 +03:00
card - > options . performance_stats = 0 ;
2005-04-17 02:20:36 +04:00
}
/**
* initialize channels , card and all state machines
*/
static int
qeth_setup_card ( struct qeth_card * card )
{
QETH_DBF_TEXT ( setup , 2 , " setupcrd " ) ;
QETH_DBF_HEX ( setup , 2 , & card , sizeof ( void * ) ) ;
card - > read . state = CH_STATE_DOWN ;
card - > write . state = CH_STATE_DOWN ;
card - > data . state = CH_STATE_DOWN ;
card - > state = CARD_STATE_DOWN ;
card - > lan_online = 0 ;
card - > use_hard_stop = 0 ;
card - > dev = NULL ;
# ifdef CONFIG_QETH_VLAN
spin_lock_init ( & card - > vlanlock ) ;
card - > vlangrp = NULL ;
# endif
2005-09-30 12:17:24 +04:00
spin_lock_init ( & card - > lock ) ;
2005-04-17 02:20:36 +04:00
spin_lock_init ( & card - > ip_lock ) ;
spin_lock_init ( & card - > thread_mask_lock ) ;
card - > thread_start_mask = 0 ;
card - > thread_allowed_mask = 0 ;
card - > thread_running_mask = 0 ;
2006-12-06 22:18:20 +03:00
INIT_WORK ( & card - > kernel_thread_starter , qeth_start_kernel_thread ) ;
2005-04-17 02:20:36 +04:00
INIT_LIST_HEAD ( & card - > ip_list ) ;
card - > ip_tbd_list = kmalloc ( sizeof ( struct list_head ) , GFP_KERNEL ) ;
if ( ! card - > ip_tbd_list ) {
QETH_DBF_TEXT ( setup , 0 , " iptbdnom " ) ;
return - ENOMEM ;
}
INIT_LIST_HEAD ( card - > ip_tbd_list ) ;
INIT_LIST_HEAD ( & card - > cmd_waiter_list ) ;
init_waitqueue_head ( & card - > wait_q ) ;
/* intial options */
qeth_set_intial_options ( card ) ;
/* IP address takeover */
INIT_LIST_HEAD ( & card - > ipato . entries ) ;
card - > ipato . enabled = 0 ;
card - > ipato . invert4 = 0 ;
card - > ipato . invert6 = 0 ;
/* init QDIO stuff */
qeth_init_qdio_info ( card ) ;
return 0 ;
}
static int
is_1920_device ( struct qeth_card * card )
{
int single_queue = 0 ;
struct ccw_device * ccwdev ;
struct channelPath_dsc {
u8 flags ;
u8 lsn ;
u8 desc ;
u8 chpid ;
u8 swla ;
u8 zeroes ;
u8 chla ;
u8 chpp ;
} * chp_dsc ;
QETH_DBF_TEXT ( setup , 2 , " chk_1920 " ) ;
ccwdev = card - > data . ccwdev ;
chp_dsc = ( struct channelPath_dsc * ) ccw_device_get_chp_desc ( ccwdev , 0 ) ;
if ( chp_dsc ! = NULL ) {
/* CHPP field bit 6 == 1 -> single queue */
single_queue = ( ( chp_dsc - > chpp & 0x02 ) = = 0x02 ) ;
kfree ( chp_dsc ) ;
}
QETH_DBF_TEXT_ ( setup , 2 , " rc:%x " , single_queue ) ;
return single_queue ;
}
static int
qeth_determine_card_type ( struct qeth_card * card )
{
int i = 0 ;
QETH_DBF_TEXT ( setup , 2 , " detcdtyp " ) ;
2005-09-30 12:19:19 +04:00
card - > qdio . do_prio_queueing = QETH_PRIOQ_DEFAULT ;
card - > qdio . default_out_queue = QETH_DEFAULT_QUEUE ;
2005-04-17 02:20:36 +04:00
while ( known_devices [ i ] [ 4 ] ) {
if ( ( CARD_RDEV ( card ) - > id . dev_type = = known_devices [ i ] [ 2 ] ) & &
( CARD_RDEV ( card ) - > id . dev_model = = known_devices [ i ] [ 3 ] ) ) {
card - > info . type = known_devices [ i ] [ 4 ] ;
2005-09-30 12:19:19 +04:00
card - > qdio . no_out_queues = known_devices [ i ] [ 8 ] ;
card - > info . is_multicast_different = known_devices [ i ] [ 9 ] ;
2005-04-17 02:20:36 +04:00
if ( is_1920_device ( card ) ) {
PRINT_INFO ( " Priority Queueing not able "
" due to hardware limitations! \n " ) ;
card - > qdio . no_out_queues = 1 ;
card - > qdio . default_out_queue = 0 ;
2006-05-27 05:58:38 +04:00
}
2005-04-17 02:20:36 +04:00
return 0 ;
}
i + + ;
}
card - > info . type = QETH_CARD_TYPE_UNKNOWN ;
PRINT_ERR ( " unknown card type on device %s \n " , CARD_BUS_ID ( card ) ) ;
return - ENOENT ;
}
static int
qeth_probe_device ( struct ccwgroup_device * gdev )
{
struct qeth_card * card ;
struct device * dev ;
unsigned long flags ;
int rc ;
QETH_DBF_TEXT ( setup , 2 , " probedev " ) ;
dev = & gdev - > dev ;
if ( ! get_device ( dev ) )
return - ENODEV ;
2005-09-30 12:19:19 +04:00
QETH_DBF_TEXT_ ( setup , 2 , " %s " , gdev - > dev . bus_id ) ;
2006-05-27 05:58:38 +04:00
2005-04-17 02:20:36 +04:00
card = qeth_alloc_card ( ) ;
if ( ! card ) {
put_device ( dev ) ;
QETH_DBF_TEXT_ ( setup , 2 , " 1err%d " , - ENOMEM ) ;
return - ENOMEM ;
}
card - > read . ccwdev = gdev - > cdev [ 0 ] ;
card - > write . ccwdev = gdev - > cdev [ 1 ] ;
card - > data . ccwdev = gdev - > cdev [ 2 ] ;
gdev - > dev . driver_data = card ;
card - > gdev = gdev ;
gdev - > cdev [ 0 ] - > handler = qeth_irq ;
gdev - > cdev [ 1 ] - > handler = qeth_irq ;
gdev - > cdev [ 2 ] - > handler = qeth_irq ;
2005-09-30 12:19:19 +04:00
if ( ( rc = qeth_determine_card_type ( card ) ) ) {
PRINT_WARN ( " %s: not a valid card type \n " , __func__ ) ;
QETH_DBF_TEXT_ ( setup , 2 , " 3err%d " , rc ) ;
put_device ( dev ) ;
qeth_free_card ( card ) ;
return rc ;
2006-05-27 05:58:38 +04:00
}
2005-09-30 12:19:19 +04:00
if ( ( rc = qeth_setup_card ( card ) ) ) {
QETH_DBF_TEXT_ ( setup , 2 , " 2err%d " , rc ) ;
2005-04-17 02:20:36 +04:00
put_device ( dev ) ;
qeth_free_card ( card ) ;
return rc ;
}
2005-09-30 12:19:19 +04:00
rc = qeth_create_device_attributes ( dev ) ;
if ( rc ) {
2005-04-17 02:20:36 +04:00
put_device ( dev ) ;
qeth_free_card ( card ) ;
return rc ;
}
/* insert into our internal list */
write_lock_irqsave ( & qeth_card_list . rwlock , flags ) ;
list_add_tail ( & card - > list , & qeth_card_list . list ) ;
write_unlock_irqrestore ( & qeth_card_list . rwlock , flags ) ;
return rc ;
}
2007-05-04 20:47:52 +04:00
static int qeth_read_conf_data ( struct qeth_card * card , void * * buffer ,
int * length )
{
struct ciw * ciw ;
char * rcd_buf ;
int ret ;
struct qeth_channel * channel = & card - > data ;
unsigned long flags ;
/*
* scan for RCD command in extended SenseID data
*/
ciw = ccw_device_get_ciw ( channel - > ccwdev , CIW_TYPE_RCD ) ;
if ( ! ciw | | ciw - > cmd = = 0 )
return - EOPNOTSUPP ;
rcd_buf = kzalloc ( ciw - > count , GFP_KERNEL | GFP_DMA ) ;
if ( ! rcd_buf )
return - ENOMEM ;
channel - > ccw . cmd_code = ciw - > cmd ;
channel - > ccw . cda = ( __u32 ) __pa ( rcd_buf ) ;
channel - > ccw . count = ciw - > count ;
channel - > ccw . flags = CCW_FLAG_SLI ;
channel - > state = CH_STATE_RCD ;
spin_lock_irqsave ( get_ccwdev_lock ( channel - > ccwdev ) , flags ) ;
ret = ccw_device_start_timeout ( channel - > ccwdev , & channel - > ccw ,
QETH_RCD_PARM , LPM_ANYPATH , 0 ,
QETH_RCD_TIMEOUT ) ;
spin_unlock_irqrestore ( get_ccwdev_lock ( channel - > ccwdev ) , flags ) ;
if ( ! ret )
wait_event ( card - > wait_q ,
( channel - > state = = CH_STATE_RCD_DONE | |
channel - > state = = CH_STATE_DOWN ) ) ;
if ( channel - > state = = CH_STATE_DOWN )
ret = - EIO ;
else
channel - > state = CH_STATE_DOWN ;
if ( ret ) {
kfree ( rcd_buf ) ;
* buffer = NULL ;
* length = 0 ;
} else {
* length = ciw - > count ;
* buffer = rcd_buf ;
}
return ret ;
}
2005-04-17 02:20:36 +04:00
static int
qeth_get_unitaddr ( struct qeth_card * card )
{
int length ;
char * prcd ;
int rc ;
QETH_DBF_TEXT ( setup , 2 , " getunit " ) ;
2007-05-04 20:47:52 +04:00
rc = qeth_read_conf_data ( card , ( void * * ) & prcd , & length ) ;
2005-04-17 02:20:36 +04:00
if ( rc ) {
2007-05-04 20:47:52 +04:00
PRINT_ERR ( " qeth_read_conf_data for device %s returned %i \n " ,
2005-04-17 02:20:36 +04:00
CARD_DDEV_ID ( card ) , rc ) ;
return rc ;
}
card - > info . chpid = prcd [ 30 ] ;
card - > info . unit_addr2 = prcd [ 31 ] ;
card - > info . cula = prcd [ 63 ] ;
card - > info . guestlan = ( ( prcd [ 0x10 ] = = _ascebc [ ' V ' ] ) & &
( prcd [ 0x11 ] = = _ascebc [ ' M ' ] ) ) ;
2007-05-04 20:47:52 +04:00
kfree ( prcd ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static void
qeth_init_tokens ( struct qeth_card * card )
{
card - > token . issuer_rm_w = 0x00010103UL ;
card - > token . cm_filter_w = 0x00010108UL ;
card - > token . cm_connection_w = 0x0001010aUL ;
card - > token . ulp_filter_w = 0x0001010bUL ;
card - > token . ulp_connection_w = 0x0001010dUL ;
}
static inline __u16
raw_devno_from_bus_id ( char * id )
{
id + = ( strlen ( id ) - 4 ) ;
return ( __u16 ) simple_strtoul ( id , & id , 16 ) ;
}
/**
* setup channel
*/
static void
qeth_setup_ccw ( struct qeth_channel * channel , unsigned char * iob , __u32 len )
{
struct qeth_card * card ;
QETH_DBF_TEXT ( trace , 4 , " setupccw " ) ;
card = CARD_FROM_CDEV ( channel - > ccwdev ) ;
if ( channel = = & card - > read )
memcpy ( & channel - > ccw , READ_CCW , sizeof ( struct ccw1 ) ) ;
else
memcpy ( & channel - > ccw , WRITE_CCW , sizeof ( struct ccw1 ) ) ;
channel - > ccw . count = len ;
channel - > ccw . cda = ( __u32 ) __pa ( iob ) ;
}
/**
* get free buffer for ccws ( IDX activation , lancmds , ipassists . . . )
*/
static struct qeth_cmd_buffer *
__qeth_get_buffer ( struct qeth_channel * channel )
{
__u8 index ;
QETH_DBF_TEXT ( trace , 6 , " getbuff " ) ;
index = channel - > io_buf_no ;
do {
if ( channel - > iob [ index ] . state = = BUF_STATE_FREE ) {
channel - > iob [ index ] . state = BUF_STATE_LOCKED ;
channel - > io_buf_no = ( channel - > io_buf_no + 1 ) %
QETH_CMD_BUFFER_NO ;
memset ( channel - > iob [ index ] . data , 0 , QETH_BUFSIZE ) ;
return channel - > iob + index ;
}
index = ( index + 1 ) % QETH_CMD_BUFFER_NO ;
} while ( index ! = channel - > io_buf_no ) ;
return NULL ;
}
/**
* release command buffer
*/
static void
qeth_release_buffer ( struct qeth_channel * channel , struct qeth_cmd_buffer * iob )
{
unsigned long flags ;
QETH_DBF_TEXT ( trace , 6 , " relbuff " ) ;
spin_lock_irqsave ( & channel - > iob_lock , flags ) ;
memset ( iob - > data , 0 , QETH_BUFSIZE ) ;
iob - > state = BUF_STATE_FREE ;
iob - > callback = qeth_send_control_data_cb ;
iob - > rc = 0 ;
spin_unlock_irqrestore ( & channel - > iob_lock , flags ) ;
}
static struct qeth_cmd_buffer *
qeth_get_buffer ( struct qeth_channel * channel )
{
struct qeth_cmd_buffer * buffer = NULL ;
unsigned long flags ;
spin_lock_irqsave ( & channel - > iob_lock , flags ) ;
buffer = __qeth_get_buffer ( channel ) ;
spin_unlock_irqrestore ( & channel - > iob_lock , flags ) ;
return buffer ;
}
static struct qeth_cmd_buffer *
qeth_wait_for_buffer ( struct qeth_channel * channel )
{
struct qeth_cmd_buffer * buffer ;
wait_event ( channel - > wait_q ,
( ( buffer = qeth_get_buffer ( channel ) ) ! = NULL ) ) ;
return buffer ;
}
static void
qeth_clear_cmd_buffers ( struct qeth_channel * channel )
{
2006-03-22 18:03:41 +03:00
int cnt ;
2005-04-17 02:20:36 +04:00
for ( cnt = 0 ; cnt < QETH_CMD_BUFFER_NO ; cnt + + )
qeth_release_buffer ( channel , & channel - > iob [ cnt ] ) ;
channel - > buf_no = 0 ;
channel - > io_buf_no = 0 ;
}
/**
* start IDX for read and write channel
*/
static int
qeth_idx_activate_get_answer ( struct qeth_channel * channel ,
void ( * idx_reply_cb ) ( struct qeth_channel * ,
struct qeth_cmd_buffer * ) )
{
struct qeth_cmd_buffer * iob ;
unsigned long flags ;
int rc ;
struct qeth_card * card ;
QETH_DBF_TEXT ( setup , 2 , " idxanswr " ) ;
card = CARD_FROM_CDEV ( channel - > ccwdev ) ;
iob = qeth_get_buffer ( channel ) ;
iob - > callback = idx_reply_cb ;
memcpy ( & channel - > ccw , READ_CCW , sizeof ( struct ccw1 ) ) ;
channel - > ccw . count = QETH_BUFSIZE ;
channel - > ccw . cda = ( __u32 ) __pa ( iob - > data ) ;
wait_event ( card - > wait_q ,
2006-01-06 11:19:07 +03:00
atomic_cmpxchg ( & channel - > irq_pending , 0 , 1 ) = = 0 ) ;
2005-04-17 02:20:36 +04:00
QETH_DBF_TEXT ( setup , 6 , " noirqpnd " ) ;
spin_lock_irqsave ( get_ccwdev_lock ( channel - > ccwdev ) , flags ) ;
rc = ccw_device_start ( channel - > ccwdev ,
& channel - > ccw , ( addr_t ) iob , 0 , 0 ) ;
spin_unlock_irqrestore ( get_ccwdev_lock ( channel - > ccwdev ) , flags ) ;
if ( rc ) {
PRINT_ERR ( " qeth: Error2 in activating channel rc=%d \n " , rc ) ;
QETH_DBF_TEXT_ ( setup , 2 , " 2err%d " , rc ) ;
atomic_set ( & channel - > irq_pending , 0 ) ;
wake_up ( & card - > wait_q ) ;
return rc ;
}
rc = wait_event_interruptible_timeout ( card - > wait_q ,
channel - > state = = CH_STATE_UP , QETH_TIMEOUT ) ;
if ( rc = = - ERESTARTSYS )
return rc ;
if ( channel - > state ! = CH_STATE_UP ) {
rc = - ETIME ;
QETH_DBF_TEXT_ ( setup , 2 , " 3err%d " , rc ) ;
qeth_clear_cmd_buffers ( channel ) ;
} else
rc = 0 ;
return rc ;
}
static int
qeth_idx_activate_channel ( struct qeth_channel * channel ,
void ( * idx_reply_cb ) ( struct qeth_channel * ,
struct qeth_cmd_buffer * ) )
{
struct qeth_card * card ;
struct qeth_cmd_buffer * iob ;
unsigned long flags ;
__u16 temp ;
int rc ;
card = CARD_FROM_CDEV ( channel - > ccwdev ) ;
QETH_DBF_TEXT ( setup , 2 , " idxactch " ) ;
iob = qeth_get_buffer ( channel ) ;
iob - > callback = idx_reply_cb ;
memcpy ( & channel - > ccw , WRITE_CCW , sizeof ( struct ccw1 ) ) ;
channel - > ccw . count = IDX_ACTIVATE_SIZE ;
channel - > ccw . cda = ( __u32 ) __pa ( iob - > data ) ;
if ( channel = = & card - > write ) {
memcpy ( iob - > data , IDX_ACTIVATE_WRITE , IDX_ACTIVATE_SIZE ) ;
memcpy ( QETH_TRANSPORT_HEADER_SEQ_NO ( iob - > data ) ,
& card - > seqno . trans_hdr , QETH_SEQ_NO_LENGTH ) ;
card - > seqno . trans_hdr + + ;
} else {
memcpy ( iob - > data , IDX_ACTIVATE_READ , IDX_ACTIVATE_SIZE ) ;
memcpy ( QETH_TRANSPORT_HEADER_SEQ_NO ( iob - > data ) ,
& card - > seqno . trans_hdr , QETH_SEQ_NO_LENGTH ) ;
}
memcpy ( QETH_IDX_ACT_ISSUER_RM_TOKEN ( iob - > data ) ,
& card - > token . issuer_rm_w , QETH_MPC_TOKEN_LENGTH ) ;
memcpy ( QETH_IDX_ACT_FUNC_LEVEL ( iob - > data ) ,
& card - > info . func_level , sizeof ( __u16 ) ) ;
temp = raw_devno_from_bus_id ( CARD_DDEV_ID ( card ) ) ;
memcpy ( QETH_IDX_ACT_QDIO_DEV_CUA ( iob - > data ) , & temp , 2 ) ;
temp = ( card - > info . cula < < 8 ) + card - > info . unit_addr2 ;
memcpy ( QETH_IDX_ACT_QDIO_DEV_REALADDR ( iob - > data ) , & temp , 2 ) ;
wait_event ( card - > wait_q ,
2006-01-06 11:19:07 +03:00
atomic_cmpxchg ( & channel - > irq_pending , 0 , 1 ) = = 0 ) ;
2005-04-17 02:20:36 +04:00
QETH_DBF_TEXT ( setup , 6 , " noirqpnd " ) ;
spin_lock_irqsave ( get_ccwdev_lock ( channel - > ccwdev ) , flags ) ;
rc = ccw_device_start ( channel - > ccwdev ,
& channel - > ccw , ( addr_t ) iob , 0 , 0 ) ;
spin_unlock_irqrestore ( get_ccwdev_lock ( channel - > ccwdev ) , flags ) ;
if ( rc ) {
PRINT_ERR ( " qeth: Error1 in activating channel. rc=%d \n " , rc ) ;
QETH_DBF_TEXT_ ( setup , 2 , " 1err%d " , rc ) ;
atomic_set ( & channel - > irq_pending , 0 ) ;
wake_up ( & card - > wait_q ) ;
return rc ;
}
rc = wait_event_interruptible_timeout ( card - > wait_q ,
channel - > state = = CH_STATE_ACTIVATING , QETH_TIMEOUT ) ;
if ( rc = = - ERESTARTSYS )
return rc ;
if ( channel - > state ! = CH_STATE_ACTIVATING ) {
PRINT_WARN ( " qeth: IDX activate timed out! \n " ) ;
QETH_DBF_TEXT_ ( setup , 2 , " 2err%d " , - ETIME ) ;
qeth_clear_cmd_buffers ( channel ) ;
return - ETIME ;
}
return qeth_idx_activate_get_answer ( channel , idx_reply_cb ) ;
}
static int
qeth_peer_func_level ( int level )
{
if ( ( level & 0xff ) = = 8 )
return ( level & 0xff ) + 0x400 ;
if ( ( ( level > > 8 ) & 3 ) = = 1 )
return ( level & 0xff ) + 0x200 ;
return level ;
}
static void
qeth_idx_write_cb ( struct qeth_channel * channel , struct qeth_cmd_buffer * iob )
{
struct qeth_card * card ;
__u16 temp ;
QETH_DBF_TEXT ( setup , 2 , " idxwrcb " ) ;
if ( channel - > state = = CH_STATE_DOWN ) {
channel - > state = CH_STATE_ACTIVATING ;
goto out ;
}
card = CARD_FROM_CDEV ( channel - > ccwdev ) ;
if ( ! ( QETH_IS_IDX_ACT_POS_REPLY ( iob - > data ) ) ) {
PRINT_ERR ( " IDX_ACTIVATE on write channel device %s: negative "
" reply \n " , CARD_WDEV_ID ( card ) ) ;
goto out ;
}
memcpy ( & temp , QETH_IDX_ACT_FUNC_LEVEL ( iob - > data ) , 2 ) ;
if ( ( temp & ~ 0x0100 ) ! = qeth_peer_func_level ( card - > info . func_level ) ) {
PRINT_WARN ( " IDX_ACTIVATE on write channel device %s: "
" function level mismatch "
" (sent: 0x%x, received: 0x%x) \n " ,
CARD_WDEV_ID ( card ) , card - > info . func_level , temp ) ;
goto out ;
}
channel - > state = CH_STATE_UP ;
out :
qeth_release_buffer ( channel , iob ) ;
}
static int
qeth_check_idx_response ( unsigned char * buffer )
{
if ( ! buffer )
return 0 ;
QETH_DBF_HEX ( control , 2 , buffer , QETH_DBF_CONTROL_LEN ) ;
if ( ( buffer [ 2 ] & 0xc0 ) = = 0xc0 ) {
PRINT_WARN ( " received an IDX TERMINATE "
" with cause code 0x%02x%s \n " ,
buffer [ 4 ] ,
( ( buffer [ 4 ] = = 0x22 ) ?
" -- try another portname " : " " ) ) ;
QETH_DBF_TEXT ( trace , 2 , " ckidxres " ) ;
QETH_DBF_TEXT ( trace , 2 , " idxterm " ) ;
QETH_DBF_TEXT_ ( trace , 2 , " rc%d " , - EIO ) ;
return - EIO ;
}
return 0 ;
}
static void
qeth_idx_read_cb ( struct qeth_channel * channel , struct qeth_cmd_buffer * iob )
{
struct qeth_card * card ;
__u16 temp ;
QETH_DBF_TEXT ( setup , 2 , " idxrdcb " ) ;
if ( channel - > state = = CH_STATE_DOWN ) {
channel - > state = CH_STATE_ACTIVATING ;
goto out ;
}
card = CARD_FROM_CDEV ( channel - > ccwdev ) ;
if ( qeth_check_idx_response ( iob - > data ) ) {
goto out ;
}
if ( ! ( QETH_IS_IDX_ACT_POS_REPLY ( iob - > data ) ) ) {
PRINT_ERR ( " IDX_ACTIVATE on read channel device %s: negative "
" reply \n " , CARD_RDEV_ID ( card ) ) ;
goto out ;
}
/**
* temporary fix for microcode bug
* to revert it , replace OR by AND
*/
if ( ( ! QETH_IDX_NO_PORTNAME_REQUIRED ( iob - > data ) ) | |
( card - > info . type = = QETH_CARD_TYPE_OSAE ) )
card - > info . portname_required = 1 ;
memcpy ( & temp , QETH_IDX_ACT_FUNC_LEVEL ( iob - > data ) , 2 ) ;
if ( temp ! = qeth_peer_func_level ( card - > info . func_level ) ) {
PRINT_WARN ( " IDX_ACTIVATE on read channel device %s: function "
" level mismatch (sent: 0x%x, received: 0x%x) \n " ,
CARD_RDEV_ID ( card ) , card - > info . func_level , temp ) ;
goto out ;
}
memcpy ( & card - > token . issuer_rm_r ,
QETH_IDX_ACT_ISSUER_RM_TOKEN ( iob - > data ) ,
QETH_MPC_TOKEN_LENGTH ) ;
memcpy ( & card - > info . mcl_level [ 0 ] ,
QETH_IDX_REPLY_LEVEL ( iob - > data ) , QETH_MCL_LENGTH ) ;
channel - > state = CH_STATE_UP ;
out :
qeth_release_buffer ( channel , iob ) ;
}
static int
qeth_issue_next_read ( struct qeth_card * card )
{
int rc ;
struct qeth_cmd_buffer * iob ;
QETH_DBF_TEXT ( trace , 5 , " issnxrd " ) ;
if ( card - > read . state ! = CH_STATE_UP )
return - EIO ;
iob = qeth_get_buffer ( & card - > read ) ;
if ( ! iob ) {
PRINT_WARN ( " issue_next_read failed: no iob available! \n " ) ;
return - ENOMEM ;
}
qeth_setup_ccw ( & card - > read , iob - > data , QETH_BUFSIZE ) ;
QETH_DBF_TEXT ( trace , 6 , " noirqpnd " ) ;
rc = ccw_device_start ( card - > read . ccwdev , & card - > read . ccw ,
( addr_t ) iob , 0 , 0 ) ;
if ( rc ) {
PRINT_ERR ( " Error in starting next read ccw! rc=%i \n " , rc ) ;
atomic_set ( & card - > read . irq_pending , 0 ) ;
qeth_schedule_recovery ( card ) ;
wake_up ( & card - > wait_q ) ;
}
return rc ;
}
static struct qeth_reply *
qeth_alloc_reply ( struct qeth_card * card )
{
struct qeth_reply * reply ;
2006-03-24 14:15:31 +03:00
reply = kzalloc ( sizeof ( struct qeth_reply ) , GFP_ATOMIC ) ;
2005-04-17 02:20:36 +04:00
if ( reply ) {
atomic_set ( & reply - > refcnt , 1 ) ;
2007-01-08 19:30:11 +03:00
atomic_set ( & reply - > received , 0 ) ;
2005-04-17 02:20:36 +04:00
reply - > card = card ;
} ;
return reply ;
}
static void
qeth_get_reply ( struct qeth_reply * reply )
{
WARN_ON ( atomic_read ( & reply - > refcnt ) < = 0 ) ;
atomic_inc ( & reply - > refcnt ) ;
}
static void
qeth_put_reply ( struct qeth_reply * reply )
{
WARN_ON ( atomic_read ( & reply - > refcnt ) < = 0 ) ;
if ( atomic_dec_and_test ( & reply - > refcnt ) )
kfree ( reply ) ;
}
2007-05-02 17:19:35 +04:00
static void
qeth_issue_ipa_msg ( struct qeth_ipa_cmd * cmd , struct qeth_card * card )
{
int rc ;
int com ;
char * ipa_name ;
com = cmd - > hdr . command ;
rc = cmd - > hdr . return_code ;
ipa_name = qeth_get_ipa_cmd_name ( com ) ;
PRINT_ERR ( " %s(x%X) for %s returned x%X \" %s \" \n " , ipa_name , com ,
QETH_CARD_IFNAME ( card ) , rc , qeth_get_ipa_msg ( rc ) ) ;
}
2005-04-17 02:20:36 +04:00
static struct qeth_ipa_cmd *
qeth_check_ipa_data ( struct qeth_card * card , struct qeth_cmd_buffer * iob )
{
struct qeth_ipa_cmd * cmd = NULL ;
QETH_DBF_TEXT ( trace , 5 , " chkipad " ) ;
if ( IS_IPA ( iob - > data ) ) {
cmd = ( struct qeth_ipa_cmd * ) PDU_ENCAPSULATION ( iob - > data ) ;
2007-05-02 17:19:35 +04:00
if ( IS_IPA_REPLY ( cmd ) ) {
if ( cmd - > hdr . return_code )
qeth_issue_ipa_msg ( cmd , card ) ;
2005-04-17 02:20:36 +04:00
return cmd ;
2007-05-02 17:19:35 +04:00
}
2005-04-17 02:20:36 +04:00
else {
switch ( cmd - > hdr . command ) {
case IPA_CMD_STOPLAN :
PRINT_WARN ( " Link failure on %s (CHPID 0x%X) - "
" there is a network problem or "
" someone pulled the cable or "
" disabled the port. \n " ,
QETH_CARD_IFNAME ( card ) ,
card - > info . chpid ) ;
card - > lan_online = 0 ;
2006-02-07 19:04:38 +03:00
if ( card - > dev & & netif_carrier_ok ( card - > dev ) )
netif_carrier_off ( card - > dev ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
case IPA_CMD_STARTLAN :
PRINT_INFO ( " Link reestablished on %s "
" (CHPID 0x%X). Scheduling "
" IP address reset. \n " ,
QETH_CARD_IFNAME ( card ) ,
card - > info . chpid ) ;
2006-09-15 18:25:56 +04:00
netif_carrier_on ( card - > dev ) ;
2005-09-30 12:17:24 +04:00
qeth_schedule_recovery ( card ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
2005-09-30 12:19:19 +04:00
case IPA_CMD_MODCCID :
return cmd ;
2005-04-17 02:20:36 +04:00
case IPA_CMD_REGISTER_LOCAL_ADDR :
QETH_DBF_TEXT ( trace , 3 , " irla " ) ;
break ;
case IPA_CMD_UNREGISTER_LOCAL_ADDR :
QETH_DBF_TEXT ( trace , 3 , " urla " ) ;
break ;
default :
PRINT_WARN ( " Received data is IPA "
" but not a reply! \n " ) ;
break ;
}
}
}
return cmd ;
}
/**
* wake all waiting ipa commands
*/
static void
qeth_clear_ipacmd_list ( struct qeth_card * card )
{
struct qeth_reply * reply , * r ;
unsigned long flags ;
QETH_DBF_TEXT ( trace , 4 , " clipalst " ) ;
spin_lock_irqsave ( & card - > lock , flags ) ;
list_for_each_entry_safe ( reply , r , & card - > cmd_waiter_list , list ) {
qeth_get_reply ( reply ) ;
reply - > rc = - EIO ;
2007-01-08 19:30:11 +03:00
atomic_inc ( & reply - > received ) ;
2005-04-17 02:20:36 +04:00
list_del_init ( & reply - > list ) ;
wake_up ( & reply - > wait_q ) ;
qeth_put_reply ( reply ) ;
}
spin_unlock_irqrestore ( & card - > lock , flags ) ;
}
static void
qeth_send_control_data_cb ( struct qeth_channel * channel ,
struct qeth_cmd_buffer * iob )
{
struct qeth_card * card ;
struct qeth_reply * reply , * r ;
struct qeth_ipa_cmd * cmd ;
unsigned long flags ;
int keep_reply ;
QETH_DBF_TEXT ( trace , 4 , " sndctlcb " ) ;
card = CARD_FROM_CDEV ( channel - > ccwdev ) ;
if ( qeth_check_idx_response ( iob - > data ) ) {
qeth_clear_ipacmd_list ( card ) ;
qeth_schedule_recovery ( card ) ;
goto out ;
}
cmd = qeth_check_ipa_data ( card , iob ) ;
if ( ( cmd = = NULL ) & & ( card - > state ! = CARD_STATE_DOWN ) )
goto out ;
2005-09-30 12:19:19 +04:00
/*in case of OSN : check if cmd is set */
if ( card - > info . type = = QETH_CARD_TYPE_OSN & &
cmd & &
cmd - > hdr . command ! = IPA_CMD_STARTLAN & &
card - > osn_info . assist_cb ! = NULL ) {
card - > osn_info . assist_cb ( card - > dev , cmd ) ;
goto out ;
}
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & card - > lock , flags ) ;
list_for_each_entry_safe ( reply , r , & card - > cmd_waiter_list , list ) {
if ( ( reply - > seqno = = QETH_IDX_COMMAND_SEQNO ) | |
( ( cmd ) & & ( reply - > seqno = = cmd - > hdr . seqno ) ) ) {
qeth_get_reply ( reply ) ;
list_del_init ( & reply - > list ) ;
spin_unlock_irqrestore ( & card - > lock , flags ) ;
keep_reply = 0 ;
if ( reply - > callback ! = NULL ) {
if ( cmd ) {
reply - > offset = ( __u16 ) ( ( char * ) cmd -
( char * ) iob - > data ) ;
keep_reply = reply - > callback ( card ,
reply ,
( unsigned long ) cmd ) ;
2005-09-30 12:19:19 +04:00
} else
2005-04-17 02:20:36 +04:00
keep_reply = reply - > callback ( card ,
reply ,
( unsigned long ) iob ) ;
}
if ( cmd )
reply - > rc = ( u16 ) cmd - > hdr . return_code ;
else if ( iob - > rc )
reply - > rc = iob - > rc ;
if ( keep_reply ) {
spin_lock_irqsave ( & card - > lock , flags ) ;
list_add_tail ( & reply - > list ,
& card - > cmd_waiter_list ) ;
spin_unlock_irqrestore ( & card - > lock , flags ) ;
} else {
2007-01-08 19:30:11 +03:00
atomic_inc ( & reply - > received ) ;
2005-04-17 02:20:36 +04:00
wake_up ( & reply - > wait_q ) ;
}
qeth_put_reply ( reply ) ;
goto out ;
}
}
spin_unlock_irqrestore ( & card - > lock , flags ) ;
out :
memcpy ( & card - > seqno . pdu_hdr_ack ,
QETH_PDU_HEADER_SEQ_NO ( iob - > data ) ,
QETH_SEQ_NO_LENGTH ) ;
qeth_release_buffer ( channel , iob ) ;
}
2007-02-05 23:18:53 +03:00
static void
2005-09-30 12:19:19 +04:00
qeth_prepare_control_data ( struct qeth_card * card , int len ,
2007-02-05 23:18:53 +03:00
struct qeth_cmd_buffer * iob )
2005-09-30 12:19:19 +04:00
{
qeth_setup_ccw ( & card - > write , iob - > data , len ) ;
iob - > callback = qeth_release_buffer ;
memcpy ( QETH_TRANSPORT_HEADER_SEQ_NO ( iob - > data ) ,
& card - > seqno . trans_hdr , QETH_SEQ_NO_LENGTH ) ;
card - > seqno . trans_hdr + + ;
memcpy ( QETH_PDU_HEADER_SEQ_NO ( iob - > data ) ,
& card - > seqno . pdu_hdr , QETH_SEQ_NO_LENGTH ) ;
card - > seqno . pdu_hdr + + ;
memcpy ( QETH_PDU_HEADER_ACK_SEQ_NO ( iob - > data ) ,
& card - > seqno . pdu_hdr_ack , QETH_SEQ_NO_LENGTH ) ;
QETH_DBF_HEX ( control , 2 , iob - > data , QETH_DBF_CONTROL_LEN ) ;
}
2006-05-27 05:58:38 +04:00
2005-04-17 02:20:36 +04:00
static int
qeth_send_control_data ( struct qeth_card * card , int len ,
struct qeth_cmd_buffer * iob ,
int ( * reply_cb )
( struct qeth_card * , struct qeth_reply * , unsigned long ) ,
void * reply_param )
{
int rc ;
unsigned long flags ;
2005-09-30 12:19:19 +04:00
struct qeth_reply * reply = NULL ;
2007-01-08 19:30:11 +03:00
unsigned long timeout ;
2005-04-17 02:20:36 +04:00
QETH_DBF_TEXT ( trace , 2 , " sendctl " ) ;
reply = qeth_alloc_reply ( card ) ;
if ( ! reply ) {
PRINT_WARN ( " Could no alloc qeth_reply! \n " ) ;
return - ENOMEM ;
}
reply - > callback = reply_cb ;
reply - > param = reply_param ;
if ( card - > state = = CARD_STATE_DOWN )
reply - > seqno = QETH_IDX_COMMAND_SEQNO ;
else
reply - > seqno = card - > seqno . ipa + + ;
init_waitqueue_head ( & reply - > wait_q ) ;
spin_lock_irqsave ( & card - > lock , flags ) ;
list_add_tail ( & reply - > list , & card - > cmd_waiter_list ) ;
spin_unlock_irqrestore ( & card - > lock , flags ) ;
QETH_DBF_HEX ( control , 2 , iob - > data , QETH_DBF_CONTROL_LEN ) ;
2007-01-08 19:30:11 +03:00
while ( atomic_cmpxchg ( & card - > write . irq_pending , 0 , 1 ) ) ;
2005-09-30 12:19:19 +04:00
qeth_prepare_control_data ( card , len , iob ) ;
2007-01-08 19:30:11 +03:00
2005-09-30 12:19:19 +04:00
if ( IS_IPA ( iob - > data ) )
2007-01-08 19:30:11 +03:00
timeout = jiffies + QETH_IPA_TIMEOUT ;
2005-09-30 12:19:19 +04:00
else
2007-01-08 19:30:11 +03:00
timeout = jiffies + QETH_TIMEOUT ;
2005-04-17 02:20:36 +04:00
QETH_DBF_TEXT ( trace , 6 , " noirqpnd " ) ;
spin_lock_irqsave ( get_ccwdev_lock ( card - > write . ccwdev ) , flags ) ;
rc = ccw_device_start ( card - > write . ccwdev , & card - > write . ccw ,
( addr_t ) iob , 0 , 0 ) ;
spin_unlock_irqrestore ( get_ccwdev_lock ( card - > write . ccwdev ) , flags ) ;
if ( rc ) {
PRINT_WARN ( " qeth_send_control_data: "
" ccw_device_start rc = %i \n " , rc ) ;
QETH_DBF_TEXT_ ( trace , 2 , " err%d " , rc ) ;
spin_lock_irqsave ( & card - > lock , flags ) ;
list_del_init ( & reply - > list ) ;
qeth_put_reply ( reply ) ;
spin_unlock_irqrestore ( & card - > lock , flags ) ;
qeth_release_buffer ( iob - > channel , iob ) ;
atomic_set ( & card - > write . irq_pending , 0 ) ;
wake_up ( & card - > wait_q ) ;
return rc ;
}
2007-01-08 19:30:11 +03:00
while ( ! atomic_read ( & reply - > received ) ) {
if ( time_after ( jiffies , timeout ) ) {
spin_lock_irqsave ( & reply - > card - > lock , flags ) ;
list_del_init ( & reply - > list ) ;
spin_unlock_irqrestore ( & reply - > card - > lock , flags ) ;
reply - > rc = - ETIME ;
atomic_inc ( & reply - > received ) ;
wake_up ( & reply - > wait_q ) ;
}
} ;
2005-04-17 02:20:36 +04:00
rc = reply - > rc ;
qeth_put_reply ( reply ) ;
return rc ;
}
2005-09-30 12:19:19 +04:00
static int
qeth_osn_send_control_data ( struct qeth_card * card , int len ,
struct qeth_cmd_buffer * iob )
{
unsigned long flags ;
int rc = 0 ;
QETH_DBF_TEXT ( trace , 5 , " osndctrd " ) ;
wait_event ( card - > wait_q ,
2006-01-06 11:19:07 +03:00
atomic_cmpxchg ( & card - > write . irq_pending , 0 , 1 ) = = 0 ) ;
2005-09-30 12:19:19 +04:00
qeth_prepare_control_data ( card , len , iob ) ;
QETH_DBF_TEXT ( trace , 6 , " osnoirqp " ) ;
spin_lock_irqsave ( get_ccwdev_lock ( card - > write . ccwdev ) , flags ) ;
rc = ccw_device_start ( card - > write . ccwdev , & card - > write . ccw ,
( addr_t ) iob , 0 , 0 ) ;
spin_unlock_irqrestore ( get_ccwdev_lock ( card - > write . ccwdev ) , flags ) ;
if ( rc ) {
PRINT_WARN ( " qeth_osn_send_control_data: "
" ccw_device_start rc = %i \n " , rc ) ;
QETH_DBF_TEXT_ ( trace , 2 , " err%d " , rc ) ;
qeth_release_buffer ( iob - > channel , iob ) ;
atomic_set ( & card - > write . irq_pending , 0 ) ;
wake_up ( & card - > wait_q ) ;
}
return rc ;
2006-05-27 05:58:38 +04:00
}
2005-09-30 12:19:19 +04:00
static inline void
qeth_prepare_ipa_cmd ( struct qeth_card * card , struct qeth_cmd_buffer * iob ,
char prot_type )
{
memcpy ( iob - > data , IPA_PDU_HEADER , IPA_PDU_HEADER_SIZE ) ;
memcpy ( QETH_IPA_CMD_PROT_TYPE ( iob - > data ) , & prot_type , 1 ) ;
memcpy ( QETH_IPA_CMD_DEST_ADDR ( iob - > data ) ,
& card - > token . ulp_connection_r , QETH_MPC_TOKEN_LENGTH ) ;
}
static int
qeth_osn_send_ipa_cmd ( struct qeth_card * card , struct qeth_cmd_buffer * iob ,
int data_len )
{
u16 s1 , s2 ;
2005-12-13 10:22:30 +03:00
QETH_DBF_TEXT ( trace , 4 , " osndipa " ) ;
2005-09-30 12:19:19 +04:00
qeth_prepare_ipa_cmd ( card , iob , QETH_PROT_OSN2 ) ;
s1 = ( u16 ) ( IPA_PDU_HEADER_SIZE + data_len ) ;
s2 = ( u16 ) data_len ;
memcpy ( QETH_IPA_PDU_LEN_TOTAL ( iob - > data ) , & s1 , 2 ) ;
memcpy ( QETH_IPA_PDU_LEN_PDU1 ( iob - > data ) , & s2 , 2 ) ;
memcpy ( QETH_IPA_PDU_LEN_PDU2 ( iob - > data ) , & s2 , 2 ) ;
memcpy ( QETH_IPA_PDU_LEN_PDU3 ( iob - > data ) , & s2 , 2 ) ;
return qeth_osn_send_control_data ( card , s1 , iob ) ;
}
2006-05-27 05:58:38 +04:00
2005-04-17 02:20:36 +04:00
static int
qeth_send_ipa_cmd ( struct qeth_card * card , struct qeth_cmd_buffer * iob ,
int ( * reply_cb )
( struct qeth_card * , struct qeth_reply * , unsigned long ) ,
void * reply_param )
{
int rc ;
char prot_type ;
QETH_DBF_TEXT ( trace , 4 , " sendipa " ) ;
if ( card - > options . layer2 )
2005-09-30 12:19:19 +04:00
if ( card - > info . type = = QETH_CARD_TYPE_OSN )
prot_type = QETH_PROT_OSN2 ;
else
prot_type = QETH_PROT_LAYER2 ;
2005-04-17 02:20:36 +04:00
else
prot_type = QETH_PROT_TCPIP ;
2005-09-30 12:19:19 +04:00
qeth_prepare_ipa_cmd ( card , iob , prot_type ) ;
2005-04-17 02:20:36 +04:00
rc = qeth_send_control_data ( card , IPA_CMD_LENGTH , iob ,
reply_cb , reply_param ) ;
return rc ;
}
static int
qeth_cm_enable_cb ( struct qeth_card * card , struct qeth_reply * reply ,
unsigned long data )
{
struct qeth_cmd_buffer * iob ;
QETH_DBF_TEXT ( setup , 2 , " cmenblcb " ) ;
iob = ( struct qeth_cmd_buffer * ) data ;
memcpy ( & card - > token . cm_filter_r ,
QETH_CM_ENABLE_RESP_FILTER_TOKEN ( iob - > data ) ,
QETH_MPC_TOKEN_LENGTH ) ;
QETH_DBF_TEXT_ ( setup , 2 , " rc%d " , iob - > rc ) ;
return 0 ;
}
static int
qeth_cm_enable ( struct qeth_card * card )
{
int rc ;
struct qeth_cmd_buffer * iob ;
QETH_DBF_TEXT ( setup , 2 , " cmenable " ) ;
iob = qeth_wait_for_buffer ( & card - > write ) ;
memcpy ( iob - > data , CM_ENABLE , CM_ENABLE_SIZE ) ;
memcpy ( QETH_CM_ENABLE_ISSUER_RM_TOKEN ( iob - > data ) ,
& card - > token . issuer_rm_r , QETH_MPC_TOKEN_LENGTH ) ;
memcpy ( QETH_CM_ENABLE_FILTER_TOKEN ( iob - > data ) ,
& card - > token . cm_filter_w , QETH_MPC_TOKEN_LENGTH ) ;
rc = qeth_send_control_data ( card , CM_ENABLE_SIZE , iob ,
qeth_cm_enable_cb , NULL ) ;
return rc ;
}
static int
qeth_cm_setup_cb ( struct qeth_card * card , struct qeth_reply * reply ,
unsigned long data )
{
struct qeth_cmd_buffer * iob ;
QETH_DBF_TEXT ( setup , 2 , " cmsetpcb " ) ;
iob = ( struct qeth_cmd_buffer * ) data ;
memcpy ( & card - > token . cm_connection_r ,
QETH_CM_SETUP_RESP_DEST_ADDR ( iob - > data ) ,
QETH_MPC_TOKEN_LENGTH ) ;
QETH_DBF_TEXT_ ( setup , 2 , " rc%d " , iob - > rc ) ;
return 0 ;
}
static int
qeth_cm_setup ( struct qeth_card * card )
{
int rc ;
struct qeth_cmd_buffer * iob ;
QETH_DBF_TEXT ( setup , 2 , " cmsetup " ) ;
iob = qeth_wait_for_buffer ( & card - > write ) ;
memcpy ( iob - > data , CM_SETUP , CM_SETUP_SIZE ) ;
memcpy ( QETH_CM_SETUP_DEST_ADDR ( iob - > data ) ,
& card - > token . issuer_rm_r , QETH_MPC_TOKEN_LENGTH ) ;
memcpy ( QETH_CM_SETUP_CONNECTION_TOKEN ( iob - > data ) ,
& card - > token . cm_connection_w , QETH_MPC_TOKEN_LENGTH ) ;
memcpy ( QETH_CM_SETUP_FILTER_TOKEN ( iob - > data ) ,
& card - > token . cm_filter_r , QETH_MPC_TOKEN_LENGTH ) ;
rc = qeth_send_control_data ( card , CM_SETUP_SIZE , iob ,
qeth_cm_setup_cb , NULL ) ;
return rc ;
}
static int
qeth_ulp_enable_cb ( struct qeth_card * card , struct qeth_reply * reply ,
unsigned long data )
{
__u16 mtu , framesize ;
__u16 len ;
__u8 link_type ;
struct qeth_cmd_buffer * iob ;
QETH_DBF_TEXT ( setup , 2 , " ulpenacb " ) ;
iob = ( struct qeth_cmd_buffer * ) data ;
memcpy ( & card - > token . ulp_filter_r ,
QETH_ULP_ENABLE_RESP_FILTER_TOKEN ( iob - > data ) ,
QETH_MPC_TOKEN_LENGTH ) ;
if ( qeth_get_mtu_out_of_mpc ( card - > info . type ) ) {
memcpy ( & framesize , QETH_ULP_ENABLE_RESP_MAX_MTU ( iob - > data ) , 2 ) ;
mtu = qeth_get_mtu_outof_framesize ( framesize ) ;
if ( ! mtu ) {
iob - > rc = - EINVAL ;
QETH_DBF_TEXT_ ( setup , 2 , " rc%d " , iob - > rc ) ;
return 0 ;
}
card - > info . max_mtu = mtu ;
card - > info . initial_mtu = mtu ;
card - > qdio . in_buf_size = mtu + 2 * PAGE_SIZE ;
} else {
card - > info . initial_mtu = qeth_get_initial_mtu_for_card ( card ) ;
card - > info . max_mtu = qeth_get_max_mtu_for_card ( card - > info . type ) ;
card - > qdio . in_buf_size = QETH_IN_BUF_SIZE_DEFAULT ;
}
memcpy ( & len , QETH_ULP_ENABLE_RESP_DIFINFO_LEN ( iob - > data ) , 2 ) ;
if ( len > = QETH_MPC_DIFINFO_LEN_INDICATES_LINK_TYPE ) {
memcpy ( & link_type ,
QETH_ULP_ENABLE_RESP_LINK_TYPE ( iob - > data ) , 1 ) ;
card - > info . link_type = link_type ;
} else
card - > info . link_type = 0 ;
QETH_DBF_TEXT_ ( setup , 2 , " rc%d " , iob - > rc ) ;
return 0 ;
}
static int
qeth_ulp_enable ( struct qeth_card * card )
{
int rc ;
char prot_type ;
struct qeth_cmd_buffer * iob ;
/*FIXME: trace view callbacks*/
QETH_DBF_TEXT ( setup , 2 , " ulpenabl " ) ;
iob = qeth_wait_for_buffer ( & card - > write ) ;
memcpy ( iob - > data , ULP_ENABLE , ULP_ENABLE_SIZE ) ;
* ( QETH_ULP_ENABLE_LINKNUM ( iob - > data ) ) =
( __u8 ) card - > info . portno ;
if ( card - > options . layer2 )
2005-09-30 12:19:19 +04:00
if ( card - > info . type = = QETH_CARD_TYPE_OSN )
prot_type = QETH_PROT_OSN2 ;
else
prot_type = QETH_PROT_LAYER2 ;
2005-04-17 02:20:36 +04:00
else
prot_type = QETH_PROT_TCPIP ;
memcpy ( QETH_ULP_ENABLE_PROT_TYPE ( iob - > data ) , & prot_type , 1 ) ;
memcpy ( QETH_ULP_ENABLE_DEST_ADDR ( iob - > data ) ,
& card - > token . cm_connection_r , QETH_MPC_TOKEN_LENGTH ) ;
memcpy ( QETH_ULP_ENABLE_FILTER_TOKEN ( iob - > data ) ,
& card - > token . ulp_filter_w , QETH_MPC_TOKEN_LENGTH ) ;
memcpy ( QETH_ULP_ENABLE_PORTNAME_AND_LL ( iob - > data ) ,
card - > info . portname , 9 ) ;
rc = qeth_send_control_data ( card , ULP_ENABLE_SIZE , iob ,
qeth_ulp_enable_cb , NULL ) ;
return rc ;
}
static inline __u16
__raw_devno_from_bus_id ( char * id )
{
id + = ( strlen ( id ) - 4 ) ;
return ( __u16 ) simple_strtoul ( id , & id , 16 ) ;
}
static int
qeth_ulp_setup_cb ( struct qeth_card * card , struct qeth_reply * reply ,
unsigned long data )
{
struct qeth_cmd_buffer * iob ;
QETH_DBF_TEXT ( setup , 2 , " ulpstpcb " ) ;
iob = ( struct qeth_cmd_buffer * ) data ;
memcpy ( & card - > token . ulp_connection_r ,
QETH_ULP_SETUP_RESP_CONNECTION_TOKEN ( iob - > data ) ,
QETH_MPC_TOKEN_LENGTH ) ;
QETH_DBF_TEXT_ ( setup , 2 , " rc%d " , iob - > rc ) ;
return 0 ;
}
static int
qeth_ulp_setup ( struct qeth_card * card )
{
int rc ;
__u16 temp ;
struct qeth_cmd_buffer * iob ;
QETH_DBF_TEXT ( setup , 2 , " ulpsetup " ) ;
iob = qeth_wait_for_buffer ( & card - > write ) ;
memcpy ( iob - > data , ULP_SETUP , ULP_SETUP_SIZE ) ;
memcpy ( QETH_ULP_SETUP_DEST_ADDR ( iob - > data ) ,
& card - > token . cm_connection_r , QETH_MPC_TOKEN_LENGTH ) ;
memcpy ( QETH_ULP_SETUP_CONNECTION_TOKEN ( iob - > data ) ,
& card - > token . ulp_connection_w , QETH_MPC_TOKEN_LENGTH ) ;
memcpy ( QETH_ULP_SETUP_FILTER_TOKEN ( iob - > data ) ,
& card - > token . ulp_filter_r , QETH_MPC_TOKEN_LENGTH ) ;
temp = __raw_devno_from_bus_id ( CARD_DDEV_ID ( card ) ) ;
memcpy ( QETH_ULP_SETUP_CUA ( iob - > data ) , & temp , 2 ) ;
temp = ( card - > info . cula < < 8 ) + card - > info . unit_addr2 ;
memcpy ( QETH_ULP_SETUP_REAL_DEVADDR ( iob - > data ) , & temp , 2 ) ;
rc = qeth_send_control_data ( card , ULP_SETUP_SIZE , iob ,
qeth_ulp_setup_cb , NULL ) ;
return rc ;
}
static inline int
2005-12-13 10:21:47 +03:00
qeth_check_qdio_errors ( struct qdio_buffer * buf , unsigned int qdio_error ,
unsigned int siga_error , const char * dbftext )
2005-04-17 02:20:36 +04:00
{
if ( qdio_error | | siga_error ) {
2005-12-13 10:21:47 +03:00
QETH_DBF_TEXT ( trace , 2 , dbftext ) ;
QETH_DBF_TEXT ( qerr , 2 , dbftext ) ;
2005-04-17 02:20:36 +04:00
QETH_DBF_TEXT_ ( qerr , 2 , " F15=%02X " ,
2005-12-13 10:21:47 +03:00
buf - > element [ 15 ] . flags & 0xff ) ;
2005-04-17 02:20:36 +04:00
QETH_DBF_TEXT_ ( qerr , 2 , " F14=%02X " ,
2005-12-13 10:21:47 +03:00
buf - > element [ 14 ] . flags & 0xff ) ;
2005-04-17 02:20:36 +04:00
QETH_DBF_TEXT_ ( qerr , 2 , " qerr=%X " , qdio_error ) ;
QETH_DBF_TEXT_ ( qerr , 2 , " serr=%X " , siga_error ) ;
2005-12-13 10:21:47 +03:00
return 1 ;
2005-04-17 02:20:36 +04:00
}
2005-12-13 10:21:47 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-02-05 23:18:53 +03:00
static struct sk_buff *
2005-09-30 12:19:19 +04:00
qeth_get_skb ( unsigned int length , struct qeth_hdr * hdr )
2005-04-17 02:20:36 +04:00
{
struct sk_buff * skb ;
2005-09-30 12:19:19 +04:00
int add_len ;
add_len = 0 ;
if ( hdr - > hdr . osn . id = = QETH_HEADER_TYPE_OSN )
add_len = sizeof ( struct qeth_hdr ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_QETH_VLAN
2005-09-30 12:19:19 +04:00
else
add_len = VLAN_HLEN ;
2005-04-17 02:20:36 +04:00
# endif
2005-09-30 12:19:19 +04:00
skb = dev_alloc_skb ( length + add_len ) ;
if ( skb & & add_len )
skb_reserve ( skb , add_len ) ;
2005-04-17 02:20:36 +04:00
return skb ;
}
2007-02-05 23:18:53 +03:00
static struct sk_buff *
2005-04-17 02:20:36 +04:00
qeth_get_next_skb ( struct qeth_card * card , struct qdio_buffer * buffer ,
struct qdio_buffer_element * * __element , int * __offset ,
struct qeth_hdr * * hdr )
{
struct qdio_buffer_element * element = * __element ;
int offset = * __offset ;
struct sk_buff * skb = NULL ;
int skb_len ;
void * data_ptr ;
int data_len ;
QETH_DBF_TEXT ( trace , 6 , " nextskb " ) ;
/* qeth_hdr must not cross element boundaries */
if ( element - > length < offset + sizeof ( struct qeth_hdr ) ) {
if ( qeth_is_last_sbale ( element ) )
return NULL ;
element + + ;
offset = 0 ;
if ( element - > length < sizeof ( struct qeth_hdr ) )
return NULL ;
}
* hdr = element - > addr + offset ;
offset + = sizeof ( struct qeth_hdr ) ;
if ( card - > options . layer2 )
2005-09-30 12:19:19 +04:00
if ( card - > info . type = = QETH_CARD_TYPE_OSN )
skb_len = ( * hdr ) - > hdr . osn . pdu_length ;
else
skb_len = ( * hdr ) - > hdr . l2 . pkt_length ;
2005-04-17 02:20:36 +04:00
else
skb_len = ( * hdr ) - > hdr . l3 . length ;
if ( ! skb_len )
return NULL ;
if ( card - > options . fake_ll ) {
2005-05-12 22:38:11 +04:00
if ( card - > dev - > type = = ARPHRD_IEEE802_TR ) {
2005-09-30 12:19:19 +04:00
if ( ! ( skb = qeth_get_skb ( skb_len + QETH_FAKE_LL_LEN_TR , * hdr ) ) )
2005-05-12 22:38:11 +04:00
goto no_mem ;
skb_reserve ( skb , QETH_FAKE_LL_LEN_TR ) ;
} else {
2005-09-30 12:19:19 +04:00
if ( ! ( skb = qeth_get_skb ( skb_len + QETH_FAKE_LL_LEN_ETH , * hdr ) ) )
2005-05-12 22:38:11 +04:00
goto no_mem ;
skb_reserve ( skb , QETH_FAKE_LL_LEN_ETH ) ;
}
2005-09-30 12:19:19 +04:00
} else if ( ! ( skb = qeth_get_skb ( skb_len , * hdr ) ) )
2005-04-17 02:20:36 +04:00
goto no_mem ;
data_ptr = element - > addr + offset ;
while ( skb_len ) {
data_len = min ( skb_len , ( int ) ( element - > length - offset ) ) ;
if ( data_len )
memcpy ( skb_put ( skb , data_len ) , data_ptr , data_len ) ;
skb_len - = data_len ;
if ( skb_len ) {
if ( qeth_is_last_sbale ( element ) ) {
QETH_DBF_TEXT ( trace , 4 , " unexeob " ) ;
QETH_DBF_TEXT_ ( trace , 4 , " %s " , CARD_BUS_ID ( card ) ) ;
QETH_DBF_TEXT ( qerr , 2 , " unexeob " ) ;
QETH_DBF_TEXT_ ( qerr , 2 , " %s " , CARD_BUS_ID ( card ) ) ;
QETH_DBF_HEX ( misc , 4 , buffer , sizeof ( * buffer ) ) ;
dev_kfree_skb_any ( skb ) ;
card - > stats . rx_errors + + ;
return NULL ;
}
element + + ;
offset = 0 ;
data_ptr = element - > addr ;
} else {
offset + = data_len ;
}
}
* __element = element ;
* __offset = offset ;
return skb ;
no_mem :
if ( net_ratelimit ( ) ) {
PRINT_WARN ( " No memory for packet received on %s. \n " ,
QETH_CARD_IFNAME ( card ) ) ;
QETH_DBF_TEXT ( trace , 2 , " noskbmem " ) ;
QETH_DBF_TEXT_ ( trace , 2 , " %s " , CARD_BUS_ID ( card ) ) ;
}
card - > stats . rx_dropped + + ;
return NULL ;
}
2007-02-05 23:18:53 +03:00
static __be16
2005-04-17 02:20:36 +04:00
qeth_type_trans ( struct sk_buff * skb , struct net_device * dev )
{
struct qeth_card * card ;
struct ethhdr * eth ;
QETH_DBF_TEXT ( trace , 6 , " typtrans " ) ;
card = ( struct qeth_card * ) dev - > priv ;
# ifdef CONFIG_TR
if ( ( card - > info . link_type = = QETH_LINK_TYPE_HSTR ) | |
( card - > info . link_type = = QETH_LINK_TYPE_LANE_TR ) )
return tr_type_trans ( skb , dev ) ;
# endif /* CONFIG_TR */
2007-03-20 01:30:44 +03:00
skb_reset_mac_header ( skb ) ;
2005-04-17 02:20:36 +04:00
skb_pull ( skb , ETH_HLEN ) ;
eth = eth_hdr ( skb ) ;
if ( * eth - > h_dest & 1 ) {
if ( memcmp ( eth - > h_dest , dev - > broadcast , ETH_ALEN ) = = 0 )
skb - > pkt_type = PACKET_BROADCAST ;
else
skb - > pkt_type = PACKET_MULTICAST ;
} else if ( memcmp ( eth - > h_dest , dev - > dev_addr , ETH_ALEN ) )
skb - > pkt_type = PACKET_OTHERHOST ;
if ( ntohs ( eth - > h_proto ) > = 1536 )
return eth - > h_proto ;
if ( * ( unsigned short * ) ( skb - > data ) = = 0xFFFF )
return htons ( ETH_P_802_3 ) ;
return htons ( ETH_P_802_2 ) ;
}
2007-02-05 23:18:53 +03:00
static void
2005-05-12 22:38:11 +04:00
qeth_rebuild_skb_fake_ll_tr ( struct qeth_card * card , struct sk_buff * skb ,
struct qeth_hdr * hdr )
{
struct trh_hdr * fake_hdr ;
struct trllc * fake_llc ;
struct iphdr * ip_hdr ;
QETH_DBF_TEXT ( trace , 5 , " skbfktr " ) ;
2007-03-10 18:30:58 +03:00
skb_set_mac_header ( skb , - QETH_FAKE_LL_LEN_TR ) ;
2005-05-12 22:38:11 +04:00
/* this is a fake ethernet header */
2007-03-20 01:27:07 +03:00
fake_hdr = tr_hdr ( skb ) ;
2005-05-12 22:38:11 +04:00
/* the destination MAC address */
switch ( skb - > pkt_type ) {
case PACKET_MULTICAST :
switch ( skb - > protocol ) {
# ifdef CONFIG_QETH_IPV6
case __constant_htons ( ETH_P_IPV6 ) :
ndisc_mc_map ( ( struct in6_addr * )
skb - > data + QETH_FAKE_LL_V6_ADDR_POS ,
fake_hdr - > daddr , card - > dev , 0 ) ;
break ;
# endif /* CONFIG_QETH_IPV6 */
case __constant_htons ( ETH_P_IP ) :
ip_hdr = ( struct iphdr * ) skb - > data ;
ip_tr_mc_map ( ip_hdr - > daddr , fake_hdr - > daddr ) ;
break ;
default :
memcpy ( fake_hdr - > daddr , card - > dev - > dev_addr , TR_ALEN ) ;
}
break ;
case PACKET_BROADCAST :
memset ( fake_hdr - > daddr , 0xff , TR_ALEN ) ;
break ;
default :
memcpy ( fake_hdr - > daddr , card - > dev - > dev_addr , TR_ALEN ) ;
}
/* the source MAC address */
if ( hdr - > hdr . l3 . ext_flags & QETH_HDR_EXT_SRC_MAC_ADDR )
memcpy ( fake_hdr - > saddr , & hdr - > hdr . l3 . dest_addr [ 2 ] , TR_ALEN ) ;
else
memset ( fake_hdr - > saddr , 0 , TR_ALEN ) ;
fake_hdr - > rcf = 0 ;
fake_llc = ( struct trllc * ) & ( fake_hdr - > rcf ) ;
fake_llc - > dsap = EXTENDED_SAP ;
fake_llc - > ssap = EXTENDED_SAP ;
fake_llc - > llc = UI_CMD ;
fake_llc - > protid [ 0 ] = 0 ;
fake_llc - > protid [ 1 ] = 0 ;
fake_llc - > protid [ 2 ] = 0 ;
fake_llc - > ethertype = ETH_P_IP ;
}
2007-02-05 23:18:53 +03:00
static void
2005-05-12 22:38:11 +04:00
qeth_rebuild_skb_fake_ll_eth ( struct qeth_card * card , struct sk_buff * skb ,
2005-04-17 02:20:36 +04:00
struct qeth_hdr * hdr )
{
struct ethhdr * fake_hdr ;
struct iphdr * ip_hdr ;
2005-05-12 22:38:11 +04:00
QETH_DBF_TEXT ( trace , 5 , " skbfketh " ) ;
2007-03-10 18:30:58 +03:00
skb_set_mac_header ( skb , - QETH_FAKE_LL_LEN_ETH ) ;
2005-04-17 02:20:36 +04:00
/* this is a fake ethernet header */
2007-03-10 17:13:59 +03:00
fake_hdr = eth_hdr ( skb ) ;
2005-04-17 02:20:36 +04:00
/* the destination MAC address */
switch ( skb - > pkt_type ) {
case PACKET_MULTICAST :
switch ( skb - > protocol ) {
# ifdef CONFIG_QETH_IPV6
case __constant_htons ( ETH_P_IPV6 ) :
ndisc_mc_map ( ( struct in6_addr * )
skb - > data + QETH_FAKE_LL_V6_ADDR_POS ,
fake_hdr - > h_dest , card - > dev , 0 ) ;
break ;
# endif /* CONFIG_QETH_IPV6 */
case __constant_htons ( ETH_P_IP ) :
ip_hdr = ( struct iphdr * ) skb - > data ;
2005-05-12 22:38:11 +04:00
ip_eth_mc_map ( ip_hdr - > daddr , fake_hdr - > h_dest ) ;
2005-04-17 02:20:36 +04:00
break ;
default :
memcpy ( fake_hdr - > h_dest , card - > dev - > dev_addr , ETH_ALEN ) ;
}
break ;
case PACKET_BROADCAST :
memset ( fake_hdr - > h_dest , 0xff , ETH_ALEN ) ;
break ;
default :
memcpy ( fake_hdr - > h_dest , card - > dev - > dev_addr , ETH_ALEN ) ;
}
/* the source MAC address */
if ( hdr - > hdr . l3 . ext_flags & QETH_HDR_EXT_SRC_MAC_ADDR )
memcpy ( fake_hdr - > h_source , & hdr - > hdr . l3 . dest_addr [ 2 ] , ETH_ALEN ) ;
else
memset ( fake_hdr - > h_source , 0 , ETH_ALEN ) ;
/* the protocol */
fake_hdr - > h_proto = skb - > protocol ;
}
2005-05-12 22:38:11 +04:00
static inline void
qeth_rebuild_skb_fake_ll ( struct qeth_card * card , struct sk_buff * skb ,
struct qeth_hdr * hdr )
{
if ( card - > dev - > type = = ARPHRD_IEEE802_TR )
qeth_rebuild_skb_fake_ll_tr ( card , skb , hdr ) ;
else
qeth_rebuild_skb_fake_ll_eth ( card , skb , hdr ) ;
}
2007-01-08 19:29:34 +03:00
static inline void
2005-04-17 02:20:36 +04:00
qeth_layer2_rebuild_skb ( struct qeth_card * card , struct sk_buff * skb ,
struct qeth_hdr * hdr )
{
skb - > pkt_type = PACKET_HOST ;
skb - > protocol = qeth_type_trans ( skb , skb - > dev ) ;
if ( card - > options . checksum_type = = NO_CHECKSUMMING )
skb - > ip_summed = CHECKSUM_UNNECESSARY ;
else
skb - > ip_summed = CHECKSUM_NONE ;
2005-09-30 12:17:24 +04:00
* ( ( __u32 * ) skb - > cb ) = + + card - > seqno . pkt_seqno ;
2005-04-17 02:20:36 +04:00
}
2007-02-05 23:18:53 +03:00
static __u16
2005-04-17 02:20:36 +04:00
qeth_rebuild_skb ( struct qeth_card * card , struct sk_buff * skb ,
struct qeth_hdr * hdr )
{
2006-09-15 18:25:56 +04:00
unsigned short vlan_id = 0 ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_QETH_IPV6
if ( hdr - > hdr . l3 . flags & QETH_HDR_PASSTHRU ) {
skb - > pkt_type = PACKET_HOST ;
skb - > protocol = qeth_type_trans ( skb , card - > dev ) ;
2006-09-15 18:25:56 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
# endif /* CONFIG_QETH_IPV6 */
skb - > protocol = htons ( ( hdr - > hdr . l3 . flags & QETH_HDR_IPV6 ) ? ETH_P_IPV6 :
ETH_P_IP ) ;
switch ( hdr - > hdr . l3 . flags & QETH_HDR_CAST_MASK ) {
case QETH_CAST_UNICAST :
skb - > pkt_type = PACKET_HOST ;
break ;
case QETH_CAST_MULTICAST :
skb - > pkt_type = PACKET_MULTICAST ;
card - > stats . multicast + + ;
break ;
case QETH_CAST_BROADCAST :
skb - > pkt_type = PACKET_BROADCAST ;
card - > stats . multicast + + ;
break ;
case QETH_CAST_ANYCAST :
case QETH_CAST_NOCAST :
default :
skb - > pkt_type = PACKET_HOST ;
}
2006-09-15 18:25:56 +04:00
if ( hdr - > hdr . l3 . ext_flags &
( QETH_HDR_EXT_VLAN_FRAME | QETH_HDR_EXT_INCLUDE_VLAN_TAG ) ) {
vlan_id = ( hdr - > hdr . l3 . ext_flags & QETH_HDR_EXT_VLAN_FRAME ) ?
hdr - > hdr . l3 . vlan_id : * ( ( u16 * ) & hdr - > hdr . l3 . dest_addr [ 12 ] ) ;
}
2005-04-17 02:20:36 +04:00
if ( card - > options . fake_ll )
qeth_rebuild_skb_fake_ll ( card , skb , hdr ) ;
else
2007-03-20 01:30:44 +03:00
skb_reset_mac_header ( skb ) ;
2005-04-17 02:20:36 +04:00
skb - > ip_summed = card - > options . checksum_type ;
if ( card - > options . checksum_type = = HW_CHECKSUMMING ) {
if ( ( hdr - > hdr . l3 . ext_flags &
( QETH_HDR_EXT_CSUM_HDR_REQ |
QETH_HDR_EXT_CSUM_TRANSP_REQ ) ) = =
( QETH_HDR_EXT_CSUM_HDR_REQ |
QETH_HDR_EXT_CSUM_TRANSP_REQ ) )
skb - > ip_summed = CHECKSUM_UNNECESSARY ;
else
skb - > ip_summed = SW_CHECKSUMMING ;
}
2006-09-15 18:25:56 +04:00
return vlan_id ;
2005-04-17 02:20:36 +04:00
}
2007-02-05 23:18:53 +03:00
static void
2005-04-17 02:20:36 +04:00
qeth_process_inbound_buffer ( struct qeth_card * card ,
struct qeth_qdio_buffer * buf , int index )
{
struct qdio_buffer_element * element ;
struct sk_buff * skb ;
struct qeth_hdr * hdr ;
int offset ;
int rxrc ;
__u16 vlan_tag = 0 ;
/* get first element of current buffer */
element = ( struct qdio_buffer_element * ) & buf - > buffer - > element [ 0 ] ;
offset = 0 ;
2006-09-15 18:26:34 +04:00
if ( card - > options . performance_stats )
card - > perf_stats . bufs_rec + + ;
2005-04-17 02:20:36 +04:00
while ( ( skb = qeth_get_next_skb ( card , buf - > buffer , & element ,
& offset , & hdr ) ) ) {
skb - > dev = card - > dev ;
if ( hdr - > hdr . l2 . id = = QETH_HEADER_TYPE_LAYER2 )
2007-01-08 19:29:34 +03:00
qeth_layer2_rebuild_skb ( card , skb , hdr ) ;
2006-05-27 05:58:38 +04:00
else if ( hdr - > hdr . l3 . id = = QETH_HEADER_TYPE_LAYER3 )
2006-09-15 18:25:56 +04:00
vlan_tag = qeth_rebuild_skb ( card , skb , hdr ) ;
2005-09-30 12:19:19 +04:00
else { /*in case of OSN*/
skb_push ( skb , sizeof ( struct qeth_hdr ) ) ;
2007-03-31 18:55:19 +04:00
skb_copy_to_linear_data ( skb , hdr ,
sizeof ( struct qeth_hdr ) ) ;
2005-09-30 12:19:19 +04:00
}
2005-04-17 02:20:36 +04:00
/* is device UP ? */
if ( ! ( card - > dev - > flags & IFF_UP ) ) {
dev_kfree_skb_any ( skb ) ;
continue ;
}
2006-09-15 18:25:56 +04:00
if ( card - > info . type = = QETH_CARD_TYPE_OSN )
rxrc = card - > osn_info . data_cb ( skb ) ;
else
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_QETH_VLAN
if ( vlan_tag )
2006-09-15 18:25:56 +04:00
if ( card - > vlangrp )
vlan_hwaccel_rx ( skb , card - > vlangrp , vlan_tag ) ;
else {
dev_kfree_skb_any ( skb ) ;
continue ;
}
2005-04-17 02:20:36 +04:00
else
# endif
2005-09-30 12:19:19 +04:00
rxrc = netif_rx ( skb ) ;
2005-04-17 02:20:36 +04:00
card - > dev - > last_rx = jiffies ;
card - > stats . rx_packets + + ;
card - > stats . rx_bytes + = skb - > len ;
}
}
2007-02-05 23:18:53 +03:00
static struct qeth_buffer_pool_entry *
2005-04-17 02:20:36 +04:00
qeth_get_buffer_pool_entry ( struct qeth_card * card )
{
struct qeth_buffer_pool_entry * entry ;
QETH_DBF_TEXT ( trace , 6 , " gtbfplen " ) ;
if ( ! list_empty ( & card - > qdio . in_buf_pool . entry_list ) ) {
entry = list_entry ( card - > qdio . in_buf_pool . entry_list . next ,
struct qeth_buffer_pool_entry , list ) ;
list_del_init ( & entry - > list ) ;
return entry ;
}
return NULL ;
}
2007-02-05 23:18:53 +03:00
static void
2005-04-17 02:20:36 +04:00
qeth_init_input_buffer ( struct qeth_card * card , struct qeth_qdio_buffer * buf )
{
struct qeth_buffer_pool_entry * pool_entry ;
int i ;
2006-09-15 18:26:34 +04:00
2005-04-17 02:20:36 +04:00
pool_entry = qeth_get_buffer_pool_entry ( card ) ;
/*
* since the buffer is accessed only from the input_tasklet
* there shouldn ' t be a need to synchronize ; also , since we use
* the QETH_IN_BUF_REQUEUE_THRESHOLD we should never run out off
* buffers
*/
BUG_ON ( ! pool_entry ) ;
buf - > pool_entry = pool_entry ;
for ( i = 0 ; i < QETH_MAX_BUFFER_ELEMENTS ( card ) ; + + i ) {
buf - > buffer - > element [ i ] . length = PAGE_SIZE ;
buf - > buffer - > element [ i ] . addr = pool_entry - > elements [ i ] ;
if ( i = = QETH_MAX_BUFFER_ELEMENTS ( card ) - 1 )
buf - > buffer - > element [ i ] . flags = SBAL_FLAGS_LAST_ENTRY ;
else
buf - > buffer - > element [ i ] . flags = 0 ;
}
buf - > state = QETH_QDIO_BUF_EMPTY ;
}
2007-02-05 23:18:53 +03:00
static void
2005-04-17 02:20:36 +04:00
qeth_clear_output_buffer ( struct qeth_qdio_out_q * queue ,
struct qeth_qdio_out_buffer * buf )
{
int i ;
struct sk_buff * skb ;
/* is PCI flag set on buffer? */
if ( buf - > buffer - > element [ 0 ] . flags & 0x40 )
atomic_dec ( & queue - > set_pci_flags_count ) ;
while ( ( skb = skb_dequeue ( & buf - > skb_list ) ) ) {
atomic_dec ( & skb - > users ) ;
dev_kfree_skb_any ( skb ) ;
}
qeth_eddp_buf_release_contexts ( buf ) ;
for ( i = 0 ; i < QETH_MAX_BUFFER_ELEMENTS ( queue - > card ) ; + + i ) {
buf - > buffer - > element [ i ] . length = 0 ;
buf - > buffer - > element [ i ] . addr = NULL ;
buf - > buffer - > element [ i ] . flags = 0 ;
}
buf - > next_element_to_fill = 0 ;
atomic_set ( & buf - > state , QETH_QDIO_BUF_EMPTY ) ;
}
2007-02-05 23:18:53 +03:00
static void
2005-04-17 02:20:36 +04:00
qeth_queue_input_buffer ( struct qeth_card * card , int index )
{
struct qeth_qdio_q * queue = card - > qdio . in_q ;
int count ;
int i ;
int rc ;
QETH_DBF_TEXT ( trace , 6 , " queinbuf " ) ;
count = ( index < queue - > next_buf_to_init ) ?
card - > qdio . in_buf_pool . buf_count -
( queue - > next_buf_to_init - index ) :
card - > qdio . in_buf_pool . buf_count -
( queue - > next_buf_to_init + QDIO_MAX_BUFFERS_PER_Q - index ) ;
/* only requeue at a certain threshold to avoid SIGAs */
if ( count > = QETH_IN_BUF_REQUEUE_THRESHOLD ( card ) ) {
for ( i = queue - > next_buf_to_init ;
i < queue - > next_buf_to_init + count ; + + i )
qeth_init_input_buffer ( card ,
& queue - > bufs [ i % QDIO_MAX_BUFFERS_PER_Q ] ) ;
/*
* according to old code it should be avoided to requeue all
* 128 buffers in order to benefit from PCI avoidance .
* this function keeps at least one buffer ( the buffer at
* ' index ' ) un - requeued - > this buffer is the first buffer that
* will be requeued the next time
*/
2006-09-15 18:26:34 +04:00
if ( card - > options . performance_stats ) {
card - > perf_stats . inbound_do_qdio_cnt + + ;
card - > perf_stats . inbound_do_qdio_start_time =
qeth_get_micros ( ) ;
}
2005-04-17 02:20:36 +04:00
rc = do_QDIO ( CARD_DDEV ( card ) ,
QDIO_FLAG_SYNC_INPUT | QDIO_FLAG_UNDER_INTERRUPT ,
0 , queue - > next_buf_to_init , count , NULL ) ;
2006-09-15 18:26:34 +04:00
if ( card - > options . performance_stats )
card - > perf_stats . inbound_do_qdio_time + =
qeth_get_micros ( ) -
card - > perf_stats . inbound_do_qdio_start_time ;
2005-04-17 02:20:36 +04:00
if ( rc ) {
PRINT_WARN ( " qeth_queue_input_buffer's do_QDIO "
" return %i (device %s). \n " ,
rc , CARD_DDEV_ID ( card ) ) ;
QETH_DBF_TEXT ( trace , 2 , " qinberr " ) ;
QETH_DBF_TEXT_ ( trace , 2 , " %s " , CARD_BUS_ID ( card ) ) ;
}
queue - > next_buf_to_init = ( queue - > next_buf_to_init + count ) %
QDIO_MAX_BUFFERS_PER_Q ;
}
}
static inline void
qeth_put_buffer_pool_entry ( struct qeth_card * card ,
struct qeth_buffer_pool_entry * entry )
{
QETH_DBF_TEXT ( trace , 6 , " ptbfplen " ) ;
list_add_tail ( & entry - > list , & card - > qdio . in_buf_pool . entry_list ) ;
}
static void
qeth_qdio_input_handler ( struct ccw_device * ccwdev , unsigned int status ,
unsigned int qdio_err , unsigned int siga_err ,
unsigned int queue , int first_element , int count ,
unsigned long card_ptr )
{
struct net_device * net_dev ;
struct qeth_card * card ;
struct qeth_qdio_buffer * buffer ;
int index ;
int i ;
QETH_DBF_TEXT ( trace , 6 , " qdinput " ) ;
card = ( struct qeth_card * ) card_ptr ;
net_dev = card - > dev ;
2006-09-15 18:26:34 +04:00
if ( card - > options . performance_stats ) {
card - > perf_stats . inbound_cnt + + ;
card - > perf_stats . inbound_start_time = qeth_get_micros ( ) ;
}
2005-04-17 02:20:36 +04:00
if ( status & QDIO_STATUS_LOOK_FOR_ERROR ) {
if ( status & QDIO_STATUS_ACTIVATE_CHECK_CONDITION ) {
QETH_DBF_TEXT ( trace , 1 , " qdinchk " ) ;
QETH_DBF_TEXT_ ( trace , 1 , " %s " , CARD_BUS_ID ( card ) ) ;
QETH_DBF_TEXT_ ( trace , 1 , " %04X%04X " , first_element , count ) ;
QETH_DBF_TEXT_ ( trace , 1 , " %04X%04X " , queue , status ) ;
qeth_schedule_recovery ( card ) ;
return ;
}
}
for ( i = first_element ; i < ( first_element + count ) ; + + i ) {
index = i % QDIO_MAX_BUFFERS_PER_Q ;
buffer = & card - > qdio . in_q - > bufs [ index ] ;
2005-12-13 10:21:47 +03:00
if ( ! ( ( status & QDIO_STATUS_LOOK_FOR_ERROR ) & &
2006-05-27 05:58:38 +04:00
qeth_check_qdio_errors ( buffer - > buffer ,
2005-12-13 10:21:47 +03:00
qdio_err , siga_err , " qinerr " ) ) )
2005-04-17 02:20:36 +04:00
qeth_process_inbound_buffer ( card , buffer , index ) ;
/* clear buffer and give back to hardware */
qeth_put_buffer_pool_entry ( card , buffer - > pool_entry ) ;
qeth_queue_input_buffer ( card , index ) ;
}
2006-09-15 18:26:34 +04:00
if ( card - > options . performance_stats )
card - > perf_stats . inbound_time + = qeth_get_micros ( ) -
card - > perf_stats . inbound_start_time ;
2005-04-17 02:20:36 +04:00
}
2007-02-05 23:18:53 +03:00
static int
2005-04-17 02:20:36 +04:00
qeth_handle_send_error ( struct qeth_card * card ,
struct qeth_qdio_out_buffer * buffer ,
2005-12-13 10:21:47 +03:00
unsigned int qdio_err , unsigned int siga_err )
2005-04-17 02:20:36 +04:00
{
int sbalf15 = buffer - > buffer - > element [ 15 ] . flags & 0xff ;
int cc = siga_err & 3 ;
QETH_DBF_TEXT ( trace , 6 , " hdsnderr " ) ;
2005-12-13 10:21:47 +03:00
qeth_check_qdio_errors ( buffer - > buffer , qdio_err , siga_err , " qouterr " ) ;
2005-04-17 02:20:36 +04:00
switch ( cc ) {
case 0 :
if ( qdio_err ) {
QETH_DBF_TEXT ( trace , 1 , " lnkfail " ) ;
QETH_DBF_TEXT_ ( trace , 1 , " %s " , CARD_BUS_ID ( card ) ) ;
QETH_DBF_TEXT_ ( trace , 1 , " %04x %02x " ,
( u16 ) qdio_err , ( u8 ) sbalf15 ) ;
return QETH_SEND_ERROR_LINK_FAILURE ;
}
return QETH_SEND_ERROR_NONE ;
case 2 :
if ( siga_err & QDIO_SIGA_ERROR_B_BIT_SET ) {
QETH_DBF_TEXT ( trace , 1 , " SIGAcc2B " ) ;
QETH_DBF_TEXT_ ( trace , 1 , " %s " , CARD_BUS_ID ( card ) ) ;
return QETH_SEND_ERROR_KICK_IT ;
}
if ( ( sbalf15 > = 15 ) & & ( sbalf15 < = 31 ) )
return QETH_SEND_ERROR_RETRY ;
return QETH_SEND_ERROR_LINK_FAILURE ;
/* look at qdio_error and sbalf 15 */
case 1 :
QETH_DBF_TEXT ( trace , 1 , " SIGAcc1 " ) ;
QETH_DBF_TEXT_ ( trace , 1 , " %s " , CARD_BUS_ID ( card ) ) ;
return QETH_SEND_ERROR_LINK_FAILURE ;
case 3 :
2006-03-22 18:03:41 +03:00
default :
2005-04-17 02:20:36 +04:00
QETH_DBF_TEXT ( trace , 1 , " SIGAcc3 " ) ;
QETH_DBF_TEXT_ ( trace , 1 , " %s " , CARD_BUS_ID ( card ) ) ;
return QETH_SEND_ERROR_KICK_IT ;
}
}
void
qeth_flush_buffers ( struct qeth_qdio_out_q * queue , int under_int ,
int index , int count )
{
struct qeth_qdio_out_buffer * buf ;
int rc ;
int i ;
2007-05-02 17:18:07 +04:00
unsigned int qdio_flags ;
2005-04-17 02:20:36 +04:00
QETH_DBF_TEXT ( trace , 6 , " flushbuf " ) ;
for ( i = index ; i < index + count ; + + i ) {
buf = & queue - > bufs [ i % QDIO_MAX_BUFFERS_PER_Q ] ;
buf - > buffer - > element [ buf - > next_element_to_fill - 1 ] . flags | =
SBAL_FLAGS_LAST_ENTRY ;
if ( queue - > card - > info . type = = QETH_CARD_TYPE_IQD )
continue ;
if ( ! queue - > do_pack ) {
if ( ( atomic_read ( & queue - > used_buffers ) > =
( QETH_HIGH_WATERMARK_PACK -
QETH_WATERMARK_PACK_FUZZ ) ) & &
! atomic_read ( & queue - > set_pci_flags_count ) ) {
/* it's likely that we'll go to packing
* mode soon */
atomic_inc ( & queue - > set_pci_flags_count ) ;
buf - > buffer - > element [ 0 ] . flags | = 0x40 ;
}
} else {
if ( ! atomic_read ( & queue - > set_pci_flags_count ) ) {
/*
* there ' s no outstanding PCI any more , so we
2007-05-09 10:57:56 +04:00
* have to request a PCI to be sure that the PCI
2005-04-17 02:20:36 +04:00
* will wake at some time in the future then we
* can flush packed buffers that might still be
* hanging around , which can happen if no
* further send was requested by the stack
*/
atomic_inc ( & queue - > set_pci_flags_count ) ;
buf - > buffer - > element [ 0 ] . flags | = 0x40 ;
}
}
}
queue - > card - > dev - > trans_start = jiffies ;
2006-09-15 18:26:34 +04:00
if ( queue - > card - > options . performance_stats ) {
queue - > card - > perf_stats . outbound_do_qdio_cnt + + ;
queue - > card - > perf_stats . outbound_do_qdio_start_time =
qeth_get_micros ( ) ;
}
2007-05-02 17:18:07 +04:00
qdio_flags = QDIO_FLAG_SYNC_OUTPUT ;
2005-04-17 02:20:36 +04:00
if ( under_int )
2007-05-02 17:18:07 +04:00
qdio_flags | = QDIO_FLAG_UNDER_INTERRUPT ;
if ( atomic_read ( & queue - > set_pci_flags_count ) )
qdio_flags | = QDIO_FLAG_PCI_OUT ;
rc = do_QDIO ( CARD_DDEV ( queue - > card ) , qdio_flags ,
queue - > queue_no , index , count , NULL ) ;
2006-09-15 18:26:34 +04:00
if ( queue - > card - > options . performance_stats )
queue - > card - > perf_stats . outbound_do_qdio_time + =
qeth_get_micros ( ) -
queue - > card - > perf_stats . outbound_do_qdio_start_time ;
2005-04-17 02:20:36 +04:00
if ( rc ) {
QETH_DBF_TEXT ( trace , 2 , " flushbuf " ) ;
QETH_DBF_TEXT_ ( trace , 2 , " err%d " , rc ) ;
2005-09-14 20:05:31 +04:00
QETH_DBF_TEXT_ ( trace , 2 , " %s " , CARD_DDEV_ID ( queue - > card ) ) ;
2005-04-17 02:20:36 +04:00
queue - > card - > stats . tx_errors + = count ;
/* this must not happen under normal circumstances. if it
* happens something is really wrong - > recover */
qeth_schedule_recovery ( queue - > card ) ;
return ;
}
atomic_add ( count , & queue - > used_buffers ) ;
2006-09-15 18:26:34 +04:00
if ( queue - > card - > options . performance_stats )
queue - > card - > perf_stats . bufs_sent + = count ;
2005-04-17 02:20:36 +04:00
}
/*
* Switched to packing state if the number of used buffers on a queue
* reaches a certain limit .
*/
2007-02-05 23:18:53 +03:00
static void
2005-04-17 02:20:36 +04:00
qeth_switch_to_packing_if_needed ( struct qeth_qdio_out_q * queue )
{
if ( ! queue - > do_pack ) {
if ( atomic_read ( & queue - > used_buffers )
> = QETH_HIGH_WATERMARK_PACK ) {
/* switch non-PACKING -> PACKING */
QETH_DBF_TEXT ( trace , 6 , " np->pack " ) ;
2006-09-15 18:26:34 +04:00
if ( queue - > card - > options . performance_stats )
queue - > card - > perf_stats . sc_dp_p + + ;
2005-04-17 02:20:36 +04:00
queue - > do_pack = 1 ;
}
}
}
/*
* Switches from packing to non - packing mode . If there is a packing
* buffer on the queue this buffer will be prepared to be flushed .
* In that case 1 is returned to inform the caller . If no buffer
* has to be flushed , zero is returned .
*/
2007-02-05 23:18:53 +03:00
static int
2005-04-17 02:20:36 +04:00
qeth_switch_to_nonpacking_if_needed ( struct qeth_qdio_out_q * queue )
{
struct qeth_qdio_out_buffer * buffer ;
int flush_count = 0 ;
if ( queue - > do_pack ) {
if ( atomic_read ( & queue - > used_buffers )
< = QETH_LOW_WATERMARK_PACK ) {
/* switch PACKING -> non-PACKING */
QETH_DBF_TEXT ( trace , 6 , " pack->np " ) ;
2006-09-15 18:26:34 +04:00
if ( queue - > card - > options . performance_stats )
queue - > card - > perf_stats . sc_p_dp + + ;
2005-04-17 02:20:36 +04:00
queue - > do_pack = 0 ;
/* flush packing buffers */
buffer = & queue - > bufs [ queue - > next_buf_to_fill ] ;
if ( ( atomic_read ( & buffer - > state ) = =
QETH_QDIO_BUF_EMPTY ) & &
( buffer - > next_element_to_fill > 0 ) ) {
atomic_set ( & buffer - > state , QETH_QDIO_BUF_PRIMED ) ;
flush_count + + ;
queue - > next_buf_to_fill =
( queue - > next_buf_to_fill + 1 ) %
QDIO_MAX_BUFFERS_PER_Q ;
2006-09-15 18:26:34 +04:00
}
2005-04-17 02:20:36 +04:00
}
}
return flush_count ;
}
/*
* Called to flush a packing buffer if no more pci flags are on the queue .
* Checks if there is a packing buffer and prepares it to be flushed .
* In that case returns 1 , otherwise zero .
*/
2007-02-05 23:18:53 +03:00
static int
2005-04-17 02:20:36 +04:00
qeth_flush_buffers_on_no_pci ( struct qeth_qdio_out_q * queue )
{
struct qeth_qdio_out_buffer * buffer ;
buffer = & queue - > bufs [ queue - > next_buf_to_fill ] ;
if ( ( atomic_read ( & buffer - > state ) = = QETH_QDIO_BUF_EMPTY ) & &
( buffer - > next_element_to_fill > 0 ) ) {
/* it's a packing buffer */
atomic_set ( & buffer - > state , QETH_QDIO_BUF_PRIMED ) ;
queue - > next_buf_to_fill =
( queue - > next_buf_to_fill + 1 ) % QDIO_MAX_BUFFERS_PER_Q ;
return 1 ;
}
return 0 ;
}
2007-02-05 23:18:53 +03:00
static void
2005-04-17 02:20:36 +04:00
qeth_check_outbound_queue ( struct qeth_qdio_out_q * queue )
{
int index ;
int flush_cnt = 0 ;
int q_was_packing = 0 ;
/*
* check if weed have to switch to non - packing mode or if
* we have to get a pci flag out on the queue
*/
if ( ( atomic_read ( & queue - > used_buffers ) < = QETH_LOW_WATERMARK_PACK ) | |
! atomic_read ( & queue - > set_pci_flags_count ) ) {
2006-12-04 17:40:59 +03:00
if ( atomic_xchg ( & queue - > state , QETH_OUT_Q_LOCKED_FLUSH ) = =
2005-04-17 02:20:36 +04:00
QETH_OUT_Q_UNLOCKED ) {
/*
* If we get in here , there was no action in
* do_send_packet . So , we check if there is a
* packing buffer to be flushed here .
*/
netif_stop_queue ( queue - > card - > dev ) ;
index = queue - > next_buf_to_fill ;
q_was_packing = queue - > do_pack ;
flush_cnt + = qeth_switch_to_nonpacking_if_needed ( queue ) ;
if ( ! flush_cnt & &
! atomic_read ( & queue - > set_pci_flags_count ) )
flush_cnt + =
qeth_flush_buffers_on_no_pci ( queue ) ;
2006-09-15 18:26:34 +04:00
if ( queue - > card - > options . performance_stats & &
q_was_packing )
2005-04-17 02:20:36 +04:00
queue - > card - > perf_stats . bufs_sent_pack + =
flush_cnt ;
if ( flush_cnt )
qeth_flush_buffers ( queue , 1 , index , flush_cnt ) ;
atomic_set ( & queue - > state , QETH_OUT_Q_UNLOCKED ) ;
}
}
}
static void
qeth_qdio_output_handler ( struct ccw_device * ccwdev , unsigned int status ,
unsigned int qdio_error , unsigned int siga_error ,
unsigned int __queue , int first_element , int count ,
unsigned long card_ptr )
{
struct qeth_card * card = ( struct qeth_card * ) card_ptr ;
struct qeth_qdio_out_q * queue = card - > qdio . out_qs [ __queue ] ;
struct qeth_qdio_out_buffer * buffer ;
int i ;
QETH_DBF_TEXT ( trace , 6 , " qdouhdl " ) ;
if ( status & QDIO_STATUS_LOOK_FOR_ERROR ) {
if ( status & QDIO_STATUS_ACTIVATE_CHECK_CONDITION ) {
2005-09-14 20:05:31 +04:00
QETH_DBF_TEXT ( trace , 2 , " achkcond " ) ;
QETH_DBF_TEXT_ ( trace , 2 , " %s " , CARD_BUS_ID ( card ) ) ;
2005-04-17 02:20:36 +04:00
QETH_DBF_TEXT_ ( trace , 2 , " %08x " , status ) ;
netif_stop_queue ( card - > dev ) ;
qeth_schedule_recovery ( card ) ;
return ;
}
}
2006-09-15 18:26:34 +04:00
if ( card - > options . performance_stats ) {
card - > perf_stats . outbound_handler_cnt + + ;
card - > perf_stats . outbound_handler_start_time =
qeth_get_micros ( ) ;
}
2005-04-17 02:20:36 +04:00
for ( i = first_element ; i < ( first_element + count ) ; + + i ) {
buffer = & queue - > bufs [ i % QDIO_MAX_BUFFERS_PER_Q ] ;
/*we only handle the KICK_IT error by doing a recovery */
2005-12-13 10:21:47 +03:00
if ( qeth_handle_send_error ( card , buffer ,
qdio_error , siga_error )
2005-04-17 02:20:36 +04:00
= = QETH_SEND_ERROR_KICK_IT ) {
netif_stop_queue ( card - > dev ) ;
qeth_schedule_recovery ( card ) ;
return ;
}
qeth_clear_output_buffer ( queue , buffer ) ;
}
atomic_sub ( count , & queue - > used_buffers ) ;
/* check if we need to do something on this outbound queue */
if ( card - > info . type ! = QETH_CARD_TYPE_IQD )
qeth_check_outbound_queue ( queue ) ;
netif_wake_queue ( queue - > card - > dev ) ;
2006-09-15 18:26:34 +04:00
if ( card - > options . performance_stats )
card - > perf_stats . outbound_handler_time + = qeth_get_micros ( ) -
card - > perf_stats . outbound_handler_start_time ;
2005-04-17 02:20:36 +04:00
}
static void
qeth_create_qib_param_field ( struct qeth_card * card , char * param_field )
{
param_field [ 0 ] = _ascebc [ ' P ' ] ;
param_field [ 1 ] = _ascebc [ ' C ' ] ;
param_field [ 2 ] = _ascebc [ ' I ' ] ;
param_field [ 3 ] = _ascebc [ ' T ' ] ;
* ( ( unsigned int * ) ( & param_field [ 4 ] ) ) = QETH_PCI_THRESHOLD_A ( card ) ;
* ( ( unsigned int * ) ( & param_field [ 8 ] ) ) = QETH_PCI_THRESHOLD_B ( card ) ;
* ( ( unsigned int * ) ( & param_field [ 12 ] ) ) = QETH_PCI_TIMER_VALUE ( card ) ;
}
static void
qeth_create_qib_param_field_blkt ( struct qeth_card * card , char * param_field )
{
param_field [ 16 ] = _ascebc [ ' B ' ] ;
param_field [ 17 ] = _ascebc [ ' L ' ] ;
param_field [ 18 ] = _ascebc [ ' K ' ] ;
param_field [ 19 ] = _ascebc [ ' T ' ] ;
* ( ( unsigned int * ) ( & param_field [ 20 ] ) ) = card - > info . blkt . time_total ;
* ( ( unsigned int * ) ( & param_field [ 24 ] ) ) = card - > info . blkt . inter_packet ;
* ( ( unsigned int * ) ( & param_field [ 28 ] ) ) = card - > info . blkt . inter_packet_jumbo ;
}
static void
qeth_initialize_working_pool_list ( struct qeth_card * card )
{
struct qeth_buffer_pool_entry * entry ;
QETH_DBF_TEXT ( trace , 5 , " inwrklst " ) ;
list_for_each_entry ( entry ,
& card - > qdio . init_pool . entry_list , init_list ) {
qeth_put_buffer_pool_entry ( card , entry ) ;
}
}
static void
qeth_clear_working_pool_list ( struct qeth_card * card )
{
struct qeth_buffer_pool_entry * pool_entry , * tmp ;
QETH_DBF_TEXT ( trace , 5 , " clwrklst " ) ;
list_for_each_entry_safe ( pool_entry , tmp ,
& card - > qdio . in_buf_pool . entry_list , list ) {
list_del ( & pool_entry - > list ) ;
}
}
static void
qeth_free_buffer_pool ( struct qeth_card * card )
{
struct qeth_buffer_pool_entry * pool_entry , * tmp ;
int i = 0 ;
QETH_DBF_TEXT ( trace , 5 , " freepool " ) ;
list_for_each_entry_safe ( pool_entry , tmp ,
& card - > qdio . init_pool . entry_list , init_list ) {
for ( i = 0 ; i < QETH_MAX_BUFFER_ELEMENTS ( card ) ; + + i )
free_page ( ( unsigned long ) pool_entry - > elements [ i ] ) ;
list_del ( & pool_entry - > init_list ) ;
kfree ( pool_entry ) ;
}
}
static int
qeth_alloc_buffer_pool ( struct qeth_card * card )
{
struct qeth_buffer_pool_entry * pool_entry ;
void * ptr ;
int i , j ;
QETH_DBF_TEXT ( trace , 5 , " alocpool " ) ;
for ( i = 0 ; i < card - > qdio . init_pool . buf_count ; + + i ) {
pool_entry = kmalloc ( sizeof ( * pool_entry ) , GFP_KERNEL ) ;
if ( ! pool_entry ) {
qeth_free_buffer_pool ( card ) ;
return - ENOMEM ;
}
for ( j = 0 ; j < QETH_MAX_BUFFER_ELEMENTS ( card ) ; + + j ) {
2005-09-30 12:17:24 +04:00
ptr = ( void * ) __get_free_page ( GFP_KERNEL | GFP_DMA ) ;
2005-04-17 02:20:36 +04:00
if ( ! ptr ) {
while ( j > 0 )
free_page ( ( unsigned long )
pool_entry - > elements [ - - j ] ) ;
kfree ( pool_entry ) ;
qeth_free_buffer_pool ( card ) ;
return - ENOMEM ;
}
pool_entry - > elements [ j ] = ptr ;
}
list_add ( & pool_entry - > init_list ,
& card - > qdio . init_pool . entry_list ) ;
}
return 0 ;
}
int
qeth_realloc_buffer_pool ( struct qeth_card * card , int bufcnt )
{
QETH_DBF_TEXT ( trace , 2 , " realcbp " ) ;
if ( ( card - > state ! = CARD_STATE_DOWN ) & &
( card - > state ! = CARD_STATE_RECOVER ) )
return - EPERM ;
/* TODO: steel/add buffers from/to a running card's buffer pool (?) */
qeth_clear_working_pool_list ( card ) ;
qeth_free_buffer_pool ( card ) ;
card - > qdio . in_buf_pool . buf_count = bufcnt ;
card - > qdio . init_pool . buf_count = bufcnt ;
return qeth_alloc_buffer_pool ( card ) ;
}
static int
qeth_alloc_qdio_buffers ( struct qeth_card * card )
{
int i , j ;
QETH_DBF_TEXT ( setup , 2 , " allcqdbf " ) ;
2006-09-15 18:26:52 +04:00
if ( atomic_cmpxchg ( & card - > qdio . state , QETH_QDIO_UNINITIALIZED ,
QETH_QDIO_ALLOCATED ) ! = QETH_QDIO_UNINITIALIZED )
2005-04-17 02:20:36 +04:00
return 0 ;
2006-05-27 05:58:38 +04:00
card - > qdio . in_q = kmalloc ( sizeof ( struct qeth_qdio_q ) ,
2005-09-30 12:17:24 +04:00
GFP_KERNEL | GFP_DMA ) ;
2005-04-17 02:20:36 +04:00
if ( ! card - > qdio . in_q )
2006-09-15 18:26:52 +04:00
goto out_nomem ;
2005-04-17 02:20:36 +04:00
QETH_DBF_TEXT ( setup , 2 , " inq " ) ;
QETH_DBF_HEX ( setup , 2 , & card - > qdio . in_q , sizeof ( void * ) ) ;
memset ( card - > qdio . in_q , 0 , sizeof ( struct qeth_qdio_q ) ) ;
/* give inbound qeth_qdio_buffers their qdio_buffers */
for ( i = 0 ; i < QDIO_MAX_BUFFERS_PER_Q ; + + i )
card - > qdio . in_q - > bufs [ i ] . buffer =
& card - > qdio . in_q - > qdio_bufs [ i ] ;
/* inbound buffer pool */
2006-09-15 18:26:52 +04:00
if ( qeth_alloc_buffer_pool ( card ) )
goto out_freeinq ;
2005-04-17 02:20:36 +04:00
/* outbound */
card - > qdio . out_qs =
kmalloc ( card - > qdio . no_out_queues *
sizeof ( struct qeth_qdio_out_q * ) , GFP_KERNEL ) ;
2006-09-15 18:26:52 +04:00
if ( ! card - > qdio . out_qs )
goto out_freepool ;
for ( i = 0 ; i < card - > qdio . no_out_queues ; + + i ) {
2005-04-17 02:20:36 +04:00
card - > qdio . out_qs [ i ] = kmalloc ( sizeof ( struct qeth_qdio_out_q ) ,
2005-09-30 12:17:24 +04:00
GFP_KERNEL | GFP_DMA ) ;
2006-09-15 18:26:52 +04:00
if ( ! card - > qdio . out_qs [ i ] )
goto out_freeoutq ;
2005-04-17 02:20:36 +04:00
QETH_DBF_TEXT_ ( setup , 2 , " outq %i " , i ) ;
QETH_DBF_HEX ( setup , 2 , & card - > qdio . out_qs [ i ] , sizeof ( void * ) ) ;
memset ( card - > qdio . out_qs [ i ] , 0 , sizeof ( struct qeth_qdio_out_q ) ) ;
card - > qdio . out_qs [ i ] - > queue_no = i ;
/* give outbound qeth_qdio_buffers their qdio_buffers */
for ( j = 0 ; j < QDIO_MAX_BUFFERS_PER_Q ; + + j ) {
card - > qdio . out_qs [ i ] - > bufs [ j ] . buffer =
& card - > qdio . out_qs [ i ] - > qdio_bufs [ j ] ;
skb_queue_head_init ( & card - > qdio . out_qs [ i ] - > bufs [ j ] .
skb_list ) ;
2006-07-03 11:25:26 +04:00
lockdep_set_class (
& card - > qdio . out_qs [ i ] - > bufs [ j ] . skb_list . lock ,
& qdio_out_skb_queue_key ) ;
2005-04-17 02:20:36 +04:00
INIT_LIST_HEAD ( & card - > qdio . out_qs [ i ] - > bufs [ j ] . ctx_list ) ;
}
}
return 0 ;
2006-09-15 18:26:52 +04:00
out_freeoutq :
while ( i > 0 )
kfree ( card - > qdio . out_qs [ - - i ] ) ;
kfree ( card - > qdio . out_qs ) ;
out_freepool :
qeth_free_buffer_pool ( card ) ;
out_freeinq :
kfree ( card - > qdio . in_q ) ;
out_nomem :
atomic_set ( & card - > qdio . state , QETH_QDIO_UNINITIALIZED ) ;
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
}
static void
qeth_free_qdio_buffers ( struct qeth_card * card )
{
int i , j ;
QETH_DBF_TEXT ( trace , 2 , " freeqdbf " ) ;
2006-12-04 17:40:59 +03:00
if ( atomic_xchg ( & card - > qdio . state , QETH_QDIO_UNINITIALIZED ) = =
2006-09-15 18:26:52 +04:00
QETH_QDIO_UNINITIALIZED )
2005-04-17 02:20:36 +04:00
return ;
kfree ( card - > qdio . in_q ) ;
/* inbound buffer pool */
qeth_free_buffer_pool ( card ) ;
/* free outbound qdio_qs */
for ( i = 0 ; i < card - > qdio . no_out_queues ; + + i ) {
for ( j = 0 ; j < QDIO_MAX_BUFFERS_PER_Q ; + + j )
qeth_clear_output_buffer ( card - > qdio . out_qs [ i ] ,
& card - > qdio . out_qs [ i ] - > bufs [ j ] ) ;
kfree ( card - > qdio . out_qs [ i ] ) ;
}
kfree ( card - > qdio . out_qs ) ;
}
static void
qeth_clear_qdio_buffers ( struct qeth_card * card )
{
int i , j ;
QETH_DBF_TEXT ( trace , 2 , " clearqdbf " ) ;
/* clear outbound buffers to free skbs */
for ( i = 0 ; i < card - > qdio . no_out_queues ; + + i )
if ( card - > qdio . out_qs [ i ] ) {
for ( j = 0 ; j < QDIO_MAX_BUFFERS_PER_Q ; + + j )
qeth_clear_output_buffer ( card - > qdio . out_qs [ i ] ,
& card - > qdio . out_qs [ i ] - > bufs [ j ] ) ;
}
}
static void
qeth_init_qdio_info ( struct qeth_card * card )
{
QETH_DBF_TEXT ( setup , 4 , " intqdinf " ) ;
2006-09-15 18:26:52 +04:00
atomic_set ( & card - > qdio . state , QETH_QDIO_UNINITIALIZED ) ;
2005-04-17 02:20:36 +04:00
/* inbound */
card - > qdio . in_buf_size = QETH_IN_BUF_SIZE_DEFAULT ;
card - > qdio . init_pool . buf_count = QETH_IN_BUF_COUNT_DEFAULT ;
card - > qdio . in_buf_pool . buf_count = card - > qdio . init_pool . buf_count ;
INIT_LIST_HEAD ( & card - > qdio . in_buf_pool . entry_list ) ;
INIT_LIST_HEAD ( & card - > qdio . init_pool . entry_list ) ;
}
static int
qeth_init_qdio_queues ( struct qeth_card * card )
{
int i , j ;
int rc ;
QETH_DBF_TEXT ( setup , 2 , " initqdqs " ) ;
/* inbound queue */
memset ( card - > qdio . in_q - > qdio_bufs , 0 ,
QDIO_MAX_BUFFERS_PER_Q * sizeof ( struct qdio_buffer ) ) ;
qeth_initialize_working_pool_list ( card ) ;
/*give only as many buffers to hardware as we have buffer pool entries*/
for ( i = 0 ; i < card - > qdio . in_buf_pool . buf_count - 1 ; + + i )
qeth_init_input_buffer ( card , & card - > qdio . in_q - > bufs [ i ] ) ;
card - > qdio . in_q - > next_buf_to_init = card - > qdio . in_buf_pool . buf_count - 1 ;
rc = do_QDIO ( CARD_DDEV ( card ) , QDIO_FLAG_SYNC_INPUT , 0 , 0 ,
card - > qdio . in_buf_pool . buf_count - 1 , NULL ) ;
if ( rc ) {
QETH_DBF_TEXT_ ( setup , 2 , " 1err%d " , rc ) ;
return rc ;
}
rc = qdio_synchronize ( CARD_DDEV ( card ) , QDIO_FLAG_SYNC_INPUT , 0 ) ;
if ( rc ) {
QETH_DBF_TEXT_ ( setup , 2 , " 2err%d " , rc ) ;
return rc ;
}
/* outbound queue */
for ( i = 0 ; i < card - > qdio . no_out_queues ; + + i ) {
memset ( card - > qdio . out_qs [ i ] - > qdio_bufs , 0 ,
QDIO_MAX_BUFFERS_PER_Q * sizeof ( struct qdio_buffer ) ) ;
for ( j = 0 ; j < QDIO_MAX_BUFFERS_PER_Q ; + + j ) {
qeth_clear_output_buffer ( card - > qdio . out_qs [ i ] ,
& card - > qdio . out_qs [ i ] - > bufs [ j ] ) ;
}
card - > qdio . out_qs [ i ] - > card = card ;
card - > qdio . out_qs [ i ] - > next_buf_to_fill = 0 ;
card - > qdio . out_qs [ i ] - > do_pack = 0 ;
atomic_set ( & card - > qdio . out_qs [ i ] - > used_buffers , 0 ) ;
atomic_set ( & card - > qdio . out_qs [ i ] - > set_pci_flags_count , 0 ) ;
atomic_set ( & card - > qdio . out_qs [ i ] - > state ,
QETH_OUT_Q_UNLOCKED ) ;
}
return 0 ;
}
static int
qeth_qdio_establish ( struct qeth_card * card )
{
struct qdio_initialize init_data ;
char * qib_param_field ;
struct qdio_buffer * * in_sbal_ptrs ;
struct qdio_buffer * * out_sbal_ptrs ;
int i , j , k ;
2006-09-15 18:26:52 +04:00
int rc = 0 ;
2005-04-17 02:20:36 +04:00
QETH_DBF_TEXT ( setup , 2 , " qdioest " ) ;
2006-03-24 14:15:31 +03:00
qib_param_field = kzalloc ( QDIO_MAX_BUFFERS_PER_Q * sizeof ( char ) ,
2005-04-17 02:20:36 +04:00
GFP_KERNEL ) ;
if ( ! qib_param_field )
return - ENOMEM ;
qeth_create_qib_param_field ( card , qib_param_field ) ;
qeth_create_qib_param_field_blkt ( card , qib_param_field ) ;
in_sbal_ptrs = kmalloc ( QDIO_MAX_BUFFERS_PER_Q * sizeof ( void * ) ,
GFP_KERNEL ) ;
if ( ! in_sbal_ptrs ) {
kfree ( qib_param_field ) ;
return - ENOMEM ;
}
for ( i = 0 ; i < QDIO_MAX_BUFFERS_PER_Q ; + + i )
in_sbal_ptrs [ i ] = ( struct qdio_buffer * )
virt_to_phys ( card - > qdio . in_q - > bufs [ i ] . buffer ) ;
out_sbal_ptrs =
kmalloc ( card - > qdio . no_out_queues * QDIO_MAX_BUFFERS_PER_Q *
sizeof ( void * ) , GFP_KERNEL ) ;
if ( ! out_sbal_ptrs ) {
kfree ( in_sbal_ptrs ) ;
kfree ( qib_param_field ) ;
return - ENOMEM ;
}
for ( i = 0 , k = 0 ; i < card - > qdio . no_out_queues ; + + i )
for ( j = 0 ; j < QDIO_MAX_BUFFERS_PER_Q ; + + j , + + k ) {
out_sbal_ptrs [ k ] = ( struct qdio_buffer * )
virt_to_phys ( card - > qdio . out_qs [ i ] - >
bufs [ j ] . buffer ) ;
}
memset ( & init_data , 0 , sizeof ( struct qdio_initialize ) ) ;
init_data . cdev = CARD_DDEV ( card ) ;
init_data . q_format = qeth_get_qdio_q_format ( card ) ;
init_data . qib_param_field_format = 0 ;
init_data . qib_param_field = qib_param_field ;
init_data . min_input_threshold = QETH_MIN_INPUT_THRESHOLD ;
init_data . max_input_threshold = QETH_MAX_INPUT_THRESHOLD ;
init_data . min_output_threshold = QETH_MIN_OUTPUT_THRESHOLD ;
init_data . max_output_threshold = QETH_MAX_OUTPUT_THRESHOLD ;
init_data . no_input_qs = 1 ;
init_data . no_output_qs = card - > qdio . no_out_queues ;
init_data . input_handler = ( qdio_handler_t * )
qeth_qdio_input_handler ;
init_data . output_handler = ( qdio_handler_t * )
qeth_qdio_output_handler ;
init_data . int_parm = ( unsigned long ) card ;
init_data . flags = QDIO_INBOUND_0COPY_SBALS |
QDIO_OUTBOUND_0COPY_SBALS |
QDIO_USE_OUTBOUND_PCIS ;
init_data . input_sbal_addr_array = ( void * * ) in_sbal_ptrs ;
init_data . output_sbal_addr_array = ( void * * ) out_sbal_ptrs ;
2006-09-15 18:26:52 +04:00
if ( atomic_cmpxchg ( & card - > qdio . state , QETH_QDIO_ALLOCATED ,
QETH_QDIO_ESTABLISHED ) = = QETH_QDIO_ALLOCATED )
if ( ( rc = qdio_initialize ( & init_data ) ) )
atomic_set ( & card - > qdio . state , QETH_QDIO_ALLOCATED ) ;
2005-04-17 02:20:36 +04:00
kfree ( out_sbal_ptrs ) ;
kfree ( in_sbal_ptrs ) ;
kfree ( qib_param_field ) ;
return rc ;
}
static int
qeth_qdio_activate ( struct qeth_card * card )
{
QETH_DBF_TEXT ( setup , 3 , " qdioact " ) ;
return qdio_activate ( CARD_DDEV ( card ) , 0 ) ;
}
static int
qeth_clear_channel ( struct qeth_channel * channel )
{
unsigned long flags ;
struct qeth_card * card ;
int rc ;
QETH_DBF_TEXT ( trace , 3 , " clearch " ) ;
card = CARD_FROM_CDEV ( channel - > ccwdev ) ;
spin_lock_irqsave ( get_ccwdev_lock ( channel - > ccwdev ) , flags ) ;
rc = ccw_device_clear ( channel - > ccwdev , QETH_CLEAR_CHANNEL_PARM ) ;
spin_unlock_irqrestore ( get_ccwdev_lock ( channel - > ccwdev ) , flags ) ;
if ( rc )
return rc ;
rc = wait_event_interruptible_timeout ( card - > wait_q ,
channel - > state = = CH_STATE_STOPPED , QETH_TIMEOUT ) ;
if ( rc = = - ERESTARTSYS )
return rc ;
if ( channel - > state ! = CH_STATE_STOPPED )
return - ETIME ;
channel - > state = CH_STATE_DOWN ;
return 0 ;
}
static int
qeth_halt_channel ( struct qeth_channel * channel )
{
unsigned long flags ;
struct qeth_card * card ;
int rc ;
QETH_DBF_TEXT ( trace , 3 , " haltch " ) ;
card = CARD_FROM_CDEV ( channel - > ccwdev ) ;
spin_lock_irqsave ( get_ccwdev_lock ( channel - > ccwdev ) , flags ) ;
rc = ccw_device_halt ( channel - > ccwdev , QETH_HALT_CHANNEL_PARM ) ;
spin_unlock_irqrestore ( get_ccwdev_lock ( channel - > ccwdev ) , flags ) ;
if ( rc )
return rc ;
rc = wait_event_interruptible_timeout ( card - > wait_q ,
channel - > state = = CH_STATE_HALTED , QETH_TIMEOUT ) ;
if ( rc = = - ERESTARTSYS )
return rc ;
if ( channel - > state ! = CH_STATE_HALTED )
return - ETIME ;
return 0 ;
}
static int
qeth_halt_channels ( struct qeth_card * card )
{
2005-09-14 20:05:31 +04:00
int rc1 = 0 , rc2 = 0 , rc3 = 0 ;
2005-04-17 02:20:36 +04:00
QETH_DBF_TEXT ( trace , 3 , " haltchs " ) ;
2005-09-14 20:05:31 +04:00
rc1 = qeth_halt_channel ( & card - > read ) ;
rc2 = qeth_halt_channel ( & card - > write ) ;
rc3 = qeth_halt_channel ( & card - > data ) ;
if ( rc1 )
return rc1 ;
2006-05-27 05:58:38 +04:00
if ( rc2 )
2005-09-14 20:05:31 +04:00
return rc2 ;
return rc3 ;
2005-04-17 02:20:36 +04:00
}
static int
qeth_clear_channels ( struct qeth_card * card )
{
2005-09-14 20:05:31 +04:00
int rc1 = 0 , rc2 = 0 , rc3 = 0 ;
2005-04-17 02:20:36 +04:00
QETH_DBF_TEXT ( trace , 3 , " clearchs " ) ;
2005-09-14 20:05:31 +04:00
rc1 = qeth_clear_channel ( & card - > read ) ;
rc2 = qeth_clear_channel ( & card - > write ) ;
rc3 = qeth_clear_channel ( & card - > data ) ;
if ( rc1 )
return rc1 ;
2006-05-27 05:58:38 +04:00
if ( rc2 )
2005-09-14 20:05:31 +04:00
return rc2 ;
return rc3 ;
2005-04-17 02:20:36 +04:00
}
static int
qeth_clear_halt_card ( struct qeth_card * card , int halt )
{
int rc = 0 ;
QETH_DBF_TEXT ( trace , 3 , " clhacrd " ) ;
QETH_DBF_HEX ( trace , 3 , & card , sizeof ( void * ) ) ;
if ( halt )
rc = qeth_halt_channels ( card ) ;
if ( rc )
return rc ;
return qeth_clear_channels ( card ) ;
}
static int
qeth_qdio_clear_card ( struct qeth_card * card , int use_halt )
{
int rc = 0 ;
QETH_DBF_TEXT ( trace , 3 , " qdioclr " ) ;
2006-09-15 18:26:52 +04:00
switch ( atomic_cmpxchg ( & card - > qdio . state , QETH_QDIO_ESTABLISHED ,
QETH_QDIO_CLEANING ) ) {
case QETH_QDIO_ESTABLISHED :
2005-04-17 02:20:36 +04:00
if ( ( rc = qdio_cleanup ( CARD_DDEV ( card ) ,
2006-09-15 18:26:52 +04:00
( card - > info . type = = QETH_CARD_TYPE_IQD ) ?
QDIO_FLAG_CLEANUP_USING_HALT :
QDIO_FLAG_CLEANUP_USING_CLEAR ) ) )
2005-04-17 02:20:36 +04:00
QETH_DBF_TEXT_ ( trace , 3 , " 1err%d " , rc ) ;
2006-09-15 18:26:52 +04:00
atomic_set ( & card - > qdio . state , QETH_QDIO_ALLOCATED ) ;
break ;
case QETH_QDIO_CLEANING :
return rc ;
default :
break ;
2005-04-17 02:20:36 +04:00
}
if ( ( rc = qeth_clear_halt_card ( card , use_halt ) ) )
QETH_DBF_TEXT_ ( trace , 3 , " 2err%d " , rc ) ;
card - > state = CARD_STATE_DOWN ;
return rc ;
}
static int
qeth_dm_act ( struct qeth_card * card )
{
int rc ;
struct qeth_cmd_buffer * iob ;
QETH_DBF_TEXT ( setup , 2 , " dmact " ) ;
iob = qeth_wait_for_buffer ( & card - > write ) ;
memcpy ( iob - > data , DM_ACT , DM_ACT_SIZE ) ;
memcpy ( QETH_DM_ACT_DEST_ADDR ( iob - > data ) ,
& card - > token . cm_connection_r , QETH_MPC_TOKEN_LENGTH ) ;
memcpy ( QETH_DM_ACT_CONNECTION_TOKEN ( iob - > data ) ,
& card - > token . ulp_connection_r , QETH_MPC_TOKEN_LENGTH ) ;
rc = qeth_send_control_data ( card , DM_ACT_SIZE , iob , NULL , NULL ) ;
return rc ;
}
static int
qeth_mpc_initialize ( struct qeth_card * card )
{
int rc ;
QETH_DBF_TEXT ( setup , 2 , " mpcinit " ) ;
if ( ( rc = qeth_issue_next_read ( card ) ) ) {
QETH_DBF_TEXT_ ( setup , 2 , " 1err%d " , rc ) ;
return rc ;
}
if ( ( rc = qeth_cm_enable ( card ) ) ) {
QETH_DBF_TEXT_ ( setup , 2 , " 2err%d " , rc ) ;
2005-09-14 20:05:31 +04:00
goto out_qdio ;
2005-04-17 02:20:36 +04:00
}
if ( ( rc = qeth_cm_setup ( card ) ) ) {
QETH_DBF_TEXT_ ( setup , 2 , " 3err%d " , rc ) ;
2005-09-14 20:05:31 +04:00
goto out_qdio ;
2005-04-17 02:20:36 +04:00
}
if ( ( rc = qeth_ulp_enable ( card ) ) ) {
QETH_DBF_TEXT_ ( setup , 2 , " 4err%d " , rc ) ;
2005-09-14 20:05:31 +04:00
goto out_qdio ;
2005-04-17 02:20:36 +04:00
}
if ( ( rc = qeth_ulp_setup ( card ) ) ) {
QETH_DBF_TEXT_ ( setup , 2 , " 5err%d " , rc ) ;
2005-09-14 20:05:31 +04:00
goto out_qdio ;
2005-04-17 02:20:36 +04:00
}
if ( ( rc = qeth_alloc_qdio_buffers ( card ) ) ) {
QETH_DBF_TEXT_ ( setup , 2 , " 5err%d " , rc ) ;
2005-09-14 20:05:31 +04:00
goto out_qdio ;
2005-04-17 02:20:36 +04:00
}
if ( ( rc = qeth_qdio_establish ( card ) ) ) {
QETH_DBF_TEXT_ ( setup , 2 , " 6err%d " , rc ) ;
qeth_free_qdio_buffers ( card ) ;
goto out_qdio ;
}
if ( ( rc = qeth_qdio_activate ( card ) ) ) {
QETH_DBF_TEXT_ ( setup , 2 , " 7err%d " , rc ) ;
goto out_qdio ;
}
if ( ( rc = qeth_dm_act ( card ) ) ) {
QETH_DBF_TEXT_ ( setup , 2 , " 8err%d " , rc ) ;
goto out_qdio ;
}
return 0 ;
out_qdio :
2005-09-30 12:19:19 +04:00
qeth_qdio_clear_card ( card , card - > info . type ! = QETH_CARD_TYPE_IQD ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
static struct net_device *
qeth_get_netdevice ( enum qeth_card_types type , enum qeth_link_types linktype )
{
struct net_device * dev = NULL ;
switch ( type ) {
case QETH_CARD_TYPE_OSAE :
switch ( linktype ) {
case QETH_LINK_TYPE_LANE_TR :
case QETH_LINK_TYPE_HSTR :
# ifdef CONFIG_TR
dev = alloc_trdev ( 0 ) ;
# endif /* CONFIG_TR */
break ;
default :
dev = alloc_etherdev ( 0 ) ;
}
break ;
case QETH_CARD_TYPE_IQD :
dev = alloc_netdev ( 0 , " hsi%d " , ether_setup ) ;
break ;
2005-09-30 12:19:19 +04:00
case QETH_CARD_TYPE_OSN :
dev = alloc_netdev ( 0 , " osn%d " , ether_setup ) ;
break ;
2005-04-17 02:20:36 +04:00
default :
dev = alloc_etherdev ( 0 ) ;
}
return dev ;
}
/*hard_header fake function; used in case fake_ll is set */
static int
qeth_fake_header ( struct sk_buff * skb , struct net_device * dev ,
unsigned short type , void * daddr , void * saddr ,
unsigned len )
{
2005-05-12 22:38:11 +04:00
if ( dev - > type = = ARPHRD_IEEE802_TR ) {
struct trh_hdr * hdr ;
hdr = ( struct trh_hdr * ) skb_push ( skb , QETH_FAKE_LL_LEN_TR ) ;
memcpy ( hdr - > saddr , dev - > dev_addr , TR_ALEN ) ;
memcpy ( hdr - > daddr , " FAKELL " , TR_ALEN ) ;
return QETH_FAKE_LL_LEN_TR ;
} else {
struct ethhdr * hdr ;
hdr = ( struct ethhdr * ) skb_push ( skb , QETH_FAKE_LL_LEN_ETH ) ;
memcpy ( hdr - > h_source , dev - > dev_addr , ETH_ALEN ) ;
memcpy ( hdr - > h_dest , " FAKELL " , ETH_ALEN ) ;
if ( type ! = ETH_P_802_3 )
hdr - > h_proto = htons ( type ) ;
else
hdr - > h_proto = htons ( len ) ;
return QETH_FAKE_LL_LEN_ETH ;
2005-04-17 02:20:36 +04:00
2005-05-12 22:38:11 +04:00
}
2005-04-17 02:20:36 +04:00
}
2007-02-05 23:18:53 +03:00
static int
2005-04-17 02:20:36 +04:00
qeth_send_packet ( struct qeth_card * , struct sk_buff * ) ;
static int
qeth_hard_start_xmit ( struct sk_buff * skb , struct net_device * dev )
{
int rc ;
struct qeth_card * card ;
QETH_DBF_TEXT ( trace , 6 , " hrdstxmi " ) ;
card = ( struct qeth_card * ) dev - > priv ;
if ( skb = = NULL ) {
card - > stats . tx_dropped + + ;
card - > stats . tx_errors + + ;
/* return OK; otherwise ksoftirqd goes to 100% */
return NETDEV_TX_OK ;
}
if ( ( card - > state ! = CARD_STATE_UP ) | | ! card - > lan_online ) {
card - > stats . tx_dropped + + ;
card - > stats . tx_errors + + ;
card - > stats . tx_carrier_errors + + ;
dev_kfree_skb_any ( skb ) ;
/* return OK; otherwise ksoftirqd goes to 100% */
return NETDEV_TX_OK ;
}
2006-09-15 18:26:34 +04:00
if ( card - > options . performance_stats ) {
card - > perf_stats . outbound_cnt + + ;
card - > perf_stats . outbound_start_time = qeth_get_micros ( ) ;
}
2005-04-17 02:20:36 +04:00
netif_stop_queue ( dev ) ;
if ( ( rc = qeth_send_packet ( card , skb ) ) ) {
if ( rc = = - EBUSY ) {
return NETDEV_TX_BUSY ;
} else {
card - > stats . tx_errors + + ;
card - > stats . tx_dropped + + ;
dev_kfree_skb_any ( skb ) ;
/*set to OK; otherwise ksoftirqd goes to 100% */
rc = NETDEV_TX_OK ;
}
}
netif_wake_queue ( dev ) ;
2006-09-15 18:26:34 +04:00
if ( card - > options . performance_stats )
card - > perf_stats . outbound_time + = qeth_get_micros ( ) -
card - > perf_stats . outbound_start_time ;
2005-04-17 02:20:36 +04:00
return rc ;
}
static int
qeth_verify_vlan_dev ( struct net_device * dev , struct qeth_card * card )
{
int rc = 0 ;
# ifdef CONFIG_QETH_VLAN
struct vlan_group * vg ;
int i ;
if ( ! ( vg = card - > vlangrp ) )
return rc ;
for ( i = 0 ; i < VLAN_GROUP_ARRAY_LEN ; i + + ) {
2007-03-03 07:44:51 +03:00
if ( vlan_group_get_device ( vg , i ) = = dev ) {
2005-04-17 02:20:36 +04:00
rc = QETH_VLAN_CARD ;
break ;
}
}
2005-12-13 10:21:47 +03:00
if ( rc & & ! ( VLAN_DEV_INFO ( dev ) - > real_dev - > priv = = ( void * ) card ) )
return 0 ;
2005-04-17 02:20:36 +04:00
# endif
return rc ;
}
static int
qeth_verify_dev ( struct net_device * dev )
{
struct qeth_card * card ;
unsigned long flags ;
int rc = 0 ;
read_lock_irqsave ( & qeth_card_list . rwlock , flags ) ;
list_for_each_entry ( card , & qeth_card_list . list , list ) {
if ( card - > dev = = dev ) {
rc = QETH_REAL_CARD ;
break ;
}
rc = qeth_verify_vlan_dev ( dev , card ) ;
if ( rc )
break ;
}
read_unlock_irqrestore ( & qeth_card_list . rwlock , flags ) ;
return rc ;
}
static struct qeth_card *
qeth_get_card_from_dev ( struct net_device * dev )
{
struct qeth_card * card = NULL ;
int rc ;
rc = qeth_verify_dev ( dev ) ;
if ( rc = = QETH_REAL_CARD )
card = ( struct qeth_card * ) dev - > priv ;
else if ( rc = = QETH_VLAN_CARD )
card = ( struct qeth_card * )
VLAN_DEV_INFO ( dev ) - > real_dev - > priv ;
QETH_DBF_TEXT_ ( trace , 4 , " %d " , rc ) ;
return card ;
}
static void
qeth_tx_timeout ( struct net_device * dev )
{
struct qeth_card * card ;
card = ( struct qeth_card * ) dev - > priv ;
card - > stats . tx_errors + + ;
qeth_schedule_recovery ( card ) ;
}
static int
qeth_open ( struct net_device * dev )
{
struct qeth_card * card ;
QETH_DBF_TEXT ( trace , 4 , " qethopen " ) ;
card = ( struct qeth_card * ) dev - > priv ;
if ( card - > state ! = CARD_STATE_SOFTSETUP )
return - ENODEV ;
2005-09-30 12:19:19 +04:00
if ( ( card - > info . type ! = QETH_CARD_TYPE_OSN ) & &
( card - > options . layer2 ) & &
2005-11-10 15:51:17 +03:00
( ! ( card - > info . mac_bits & QETH_LAYER2_MAC_REGISTERED ) ) ) {
2005-04-17 02:20:36 +04:00
QETH_DBF_TEXT ( trace , 4 , " nomacadr " ) ;
return - EPERM ;
}
card - > data . state = CH_STATE_UP ;
card - > state = CARD_STATE_UP ;
2006-05-24 11:51:13 +04:00
card - > dev - > flags | = IFF_UP ;
netif_start_queue ( dev ) ;
2006-05-27 05:58:38 +04:00
2005-12-13 10:22:30 +03:00
if ( ! card - > lan_online & & netif_carrier_ok ( dev ) )
netif_carrier_off ( dev ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static int
qeth_stop ( struct net_device * dev )
{
struct qeth_card * card ;
QETH_DBF_TEXT ( trace , 4 , " qethstop " ) ;
card = ( struct qeth_card * ) dev - > priv ;
2006-05-24 11:51:11 +04:00
netif_tx_disable ( dev ) ;
2005-04-17 02:20:36 +04:00
card - > dev - > flags & = ~ IFF_UP ;
if ( card - > state = = CARD_STATE_UP )
card - > state = CARD_STATE_SOFTSETUP ;
return 0 ;
}
2007-02-05 23:18:53 +03:00
static int
2005-04-17 02:20:36 +04:00
qeth_get_cast_type ( struct qeth_card * card , struct sk_buff * skb )
{
int cast_type = RTN_UNSPEC ;
2005-09-30 12:19:19 +04:00
if ( card - > info . type = = QETH_CARD_TYPE_OSN )
return cast_type ;
2005-04-17 02:20:36 +04:00
if ( skb - > dst & & skb - > dst - > neighbour ) {
cast_type = skb - > dst - > neighbour - > type ;
if ( ( cast_type = = RTN_BROADCAST ) | |
( cast_type = = RTN_MULTICAST ) | |
( cast_type = = RTN_ANYCAST ) )
return cast_type ;
else
return RTN_UNSPEC ;
}
/* try something else */
if ( skb - > protocol = = ETH_P_IPV6 )
2007-04-11 07:50:43 +04:00
return ( skb_network_header ( skb ) [ 24 ] = = 0xff ) ?
RTN_MULTICAST : 0 ;
2005-04-17 02:20:36 +04:00
else if ( skb - > protocol = = ETH_P_IP )
2007-04-11 07:50:43 +04:00
return ( ( skb_network_header ( skb ) [ 16 ] & 0xf0 ) = = 0xe0 ) ?
RTN_MULTICAST : 0 ;
2005-04-17 02:20:36 +04:00
/* ... */
if ( ! memcmp ( skb - > data , skb - > dev - > broadcast , 6 ) )
return RTN_BROADCAST ;
else {
u16 hdr_mac ;
hdr_mac = * ( ( u16 * ) skb - > data ) ;
/* tr multicast? */
switch ( card - > info . link_type ) {
case QETH_LINK_TYPE_HSTR :
case QETH_LINK_TYPE_LANE_TR :
if ( ( hdr_mac = = QETH_TR_MAC_NC ) | |
( hdr_mac = = QETH_TR_MAC_C ) )
return RTN_MULTICAST ;
2006-03-22 18:03:41 +03:00
break ;
2005-04-17 02:20:36 +04:00
/* eth or so multicast? */
default :
if ( ( hdr_mac = = QETH_ETH_MAC_V4 ) | |
( hdr_mac = = QETH_ETH_MAC_V6 ) )
return RTN_MULTICAST ;
}
}
return cast_type ;
}
2007-02-05 23:18:53 +03:00
static int
2005-04-17 02:20:36 +04:00
qeth_get_priority_queue ( struct qeth_card * card , struct sk_buff * skb ,
int ipv , int cast_type )
{
if ( ! ipv & & ( card - > info . type = = QETH_CARD_TYPE_OSAE ) )
return card - > qdio . default_out_queue ;
switch ( card - > qdio . no_out_queues ) {
case 4 :
if ( cast_type & & card - > info . is_multicast_different )
return card - > info . is_multicast_different &
( card - > qdio . no_out_queues - 1 ) ;
if ( card - > qdio . do_prio_queueing & & ( ipv = = 4 ) ) {
2007-04-21 09:47:35 +04:00
const u8 tos = ip_hdr ( skb ) - > tos ;
2005-04-17 02:20:36 +04:00
if ( card - > qdio . do_prio_queueing = = QETH_PRIO_Q_ING_TOS ) {
2007-04-21 09:47:35 +04:00
if ( tos & IP_TOS_NOTIMPORTANT )
2005-04-17 02:20:36 +04:00
return 3 ;
2007-04-21 09:47:35 +04:00
if ( tos & IP_TOS_HIGHRELIABILITY )
2005-04-17 02:20:36 +04:00
return 2 ;
2007-04-21 09:47:35 +04:00
if ( tos & IP_TOS_HIGHTHROUGHPUT )
2005-04-17 02:20:36 +04:00
return 1 ;
2007-04-21 09:47:35 +04:00
if ( tos & IP_TOS_LOWDELAY )
2005-04-17 02:20:36 +04:00
return 0 ;
}
if ( card - > qdio . do_prio_queueing = = QETH_PRIO_Q_ING_PREC )
2007-04-21 09:47:35 +04:00
return 3 - ( tos > > 6 ) ;
2005-04-17 02:20:36 +04:00
} else if ( card - > qdio . do_prio_queueing & & ( ipv = = 6 ) ) {
/* TODO: IPv6!!! */
}
return card - > qdio . default_out_queue ;
case 1 : /* fallthrough for single-out-queue 1920-device */
default :
return card - > qdio . default_out_queue ;
}
}
static inline int
qeth_get_ip_version ( struct sk_buff * skb )
{
switch ( skb - > protocol ) {
case ETH_P_IPV6 :
return 6 ;
case ETH_P_IP :
return 4 ;
default :
return 0 ;
}
}
2007-02-05 23:18:53 +03:00
static struct qeth_hdr *
2006-09-15 18:26:19 +04:00
__qeth_prepare_skb ( struct qeth_card * card , struct sk_buff * skb , int ipv )
2005-04-17 02:20:36 +04:00
{
# ifdef CONFIG_QETH_VLAN
u16 * tag ;
2006-09-15 18:26:19 +04:00
if ( card - > vlangrp & & vlan_tx_tag_present ( skb ) & &
2005-04-17 02:20:36 +04:00
( ( ipv = = 6 ) | | card - > options . layer2 ) ) {
/*
* Move the mac addresses ( 6 bytes src , 6 bytes dest )
* to the beginning of the new header . We are using three
* memcpys instead of one memmove to save cycles .
*/
2006-09-15 18:26:19 +04:00
skb_push ( skb , VLAN_HLEN ) ;
2007-03-31 18:55:19 +04:00
skb_copy_to_linear_data ( skb , skb - > data + 4 , 4 ) ;
skb_copy_to_linear_data_offset ( skb , 4 , skb - > data + 8 , 4 ) ;
skb_copy_to_linear_data_offset ( skb , 8 , skb - > data + 12 , 4 ) ;
2006-09-15 18:26:19 +04:00
tag = ( u16 * ) ( skb - > data + 12 ) ;
2005-04-17 02:20:36 +04:00
/*
* first two bytes = ETH_P_8021Q ( 0x8100 )
* second two bytes = VLANID
*/
* tag = __constant_htons ( ETH_P_8021Q ) ;
2006-09-15 18:26:19 +04:00
* ( tag + 1 ) = htons ( vlan_tx_tag_get ( skb ) ) ;
2005-04-17 02:20:36 +04:00
}
# endif
2006-09-15 18:26:19 +04:00
return ( ( struct qeth_hdr * )
qeth_push_skb ( card , skb , sizeof ( struct qeth_hdr ) ) ) ;
}
2007-02-05 23:18:53 +03:00
static void
2006-09-15 18:26:19 +04:00
__qeth_free_new_skb ( struct sk_buff * orig_skb , struct sk_buff * new_skb )
{
if ( orig_skb ! = new_skb )
dev_kfree_skb_any ( new_skb ) ;
}
2007-02-05 23:18:53 +03:00
static struct sk_buff *
2006-09-15 18:26:19 +04:00
qeth_prepare_skb ( struct qeth_card * card , struct sk_buff * skb ,
struct qeth_hdr * * hdr , int ipv )
{
2007-01-08 19:29:58 +03:00
struct sk_buff * new_skb , * new_skb2 ;
2006-09-15 18:26:19 +04:00
QETH_DBF_TEXT ( trace , 6 , " prepskb " ) ;
2007-01-08 19:29:58 +03:00
new_skb = skb ;
new_skb = qeth_pskb_unshare ( skb , GFP_ATOMIC ) ;
if ( ! new_skb )
2006-09-15 18:26:19 +04:00
return NULL ;
2007-01-08 19:29:58 +03:00
new_skb2 = qeth_realloc_headroom ( card , new_skb ,
sizeof ( struct qeth_hdr ) ) ;
if ( ! new_skb2 ) {
__qeth_free_new_skb ( skb , new_skb ) ;
return NULL ;
}
if ( new_skb ! = skb )
__qeth_free_new_skb ( new_skb2 , new_skb ) ;
new_skb = new_skb2 ;
2006-09-15 18:26:19 +04:00
* hdr = __qeth_prepare_skb ( card , new_skb , ipv ) ;
if ( * hdr = = NULL ) {
__qeth_free_new_skb ( skb , new_skb ) ;
return NULL ;
}
return new_skb ;
2005-04-17 02:20:36 +04:00
}
static inline u8
qeth_get_qeth_hdr_flags4 ( int cast_type )
{
if ( cast_type = = RTN_MULTICAST )
return QETH_CAST_MULTICAST ;
if ( cast_type = = RTN_BROADCAST )
return QETH_CAST_BROADCAST ;
return QETH_CAST_UNICAST ;
}
static inline u8
qeth_get_qeth_hdr_flags6 ( int cast_type )
{
u8 ct = QETH_HDR_PASSTHRU | QETH_HDR_IPV6 ;
if ( cast_type = = RTN_MULTICAST )
return ct | QETH_CAST_MULTICAST ;
if ( cast_type = = RTN_ANYCAST )
return ct | QETH_CAST_ANYCAST ;
if ( cast_type = = RTN_BROADCAST )
return ct | QETH_CAST_BROADCAST ;
return ct | QETH_CAST_UNICAST ;
}
2007-02-05 23:18:53 +03:00
static void
2005-04-17 02:20:36 +04:00
qeth_layer2_get_packet_type ( struct qeth_card * card , struct qeth_hdr * hdr ,
struct sk_buff * skb )
{
__u16 hdr_mac ;
if ( ! memcmp ( skb - > data + QETH_HEADER_SIZE ,
skb - > dev - > broadcast , 6 ) ) { /* broadcast? */
* ( __u32 * ) hdr - > hdr . l2 . flags | =
QETH_LAYER2_FLAG_BROADCAST < < 8 ;
return ;
}
hdr_mac = * ( ( __u16 * ) skb - > data ) ;
/* tr multicast? */
switch ( card - > info . link_type ) {
case QETH_LINK_TYPE_HSTR :
case QETH_LINK_TYPE_LANE_TR :
if ( ( hdr_mac = = QETH_TR_MAC_NC ) | |
( hdr_mac = = QETH_TR_MAC_C ) )
* ( __u32 * ) hdr - > hdr . l2 . flags | =
QETH_LAYER2_FLAG_MULTICAST < < 8 ;
else
* ( __u32 * ) hdr - > hdr . l2 . flags | =
QETH_LAYER2_FLAG_UNICAST < < 8 ;
break ;
/* eth or so multicast? */
default :
if ( ( hdr_mac = = QETH_ETH_MAC_V4 ) | |
( hdr_mac = = QETH_ETH_MAC_V6 ) )
* ( __u32 * ) hdr - > hdr . l2 . flags | =
QETH_LAYER2_FLAG_MULTICAST < < 8 ;
else
* ( __u32 * ) hdr - > hdr . l2 . flags | =
QETH_LAYER2_FLAG_UNICAST < < 8 ;
}
}
2007-02-05 23:18:53 +03:00
static void
2005-04-17 02:20:36 +04:00
qeth_layer2_fill_header ( struct qeth_card * card , struct qeth_hdr * hdr ,
struct sk_buff * skb , int cast_type )
{
memset ( hdr , 0 , sizeof ( struct qeth_hdr ) ) ;
hdr - > hdr . l2 . id = QETH_HEADER_TYPE_LAYER2 ;
/* set byte 0 to "0x02" and byte 3 to casting flags */
if ( cast_type = = RTN_MULTICAST )
* ( __u32 * ) hdr - > hdr . l2 . flags | = QETH_LAYER2_FLAG_MULTICAST < < 8 ;
else if ( cast_type = = RTN_BROADCAST )
* ( __u32 * ) hdr - > hdr . l2 . flags | = QETH_LAYER2_FLAG_BROADCAST < < 8 ;
else
qeth_layer2_get_packet_type ( card , hdr , skb ) ;
hdr - > hdr . l2 . pkt_length = skb - > len - QETH_HEADER_SIZE ;
# ifdef CONFIG_QETH_VLAN
/* VSWITCH relies on the VLAN
* information to be present in
* the QDIO header */
if ( ( card - > vlangrp ! = NULL ) & &
vlan_tx_tag_present ( skb ) ) {
* ( __u32 * ) hdr - > hdr . l2 . flags | = QETH_LAYER2_FLAG_VLAN < < 8 ;
hdr - > hdr . l2 . vlan_id = vlan_tx_tag_get ( skb ) ;
}
# endif
}
void
qeth_fill_header ( struct qeth_card * card , struct qeth_hdr * hdr ,
struct sk_buff * skb , int ipv , int cast_type )
{
QETH_DBF_TEXT ( trace , 6 , " fillhdr " ) ;
memset ( hdr , 0 , sizeof ( struct qeth_hdr ) ) ;
if ( card - > options . layer2 ) {
qeth_layer2_fill_header ( card , hdr , skb , cast_type ) ;
return ;
}
hdr - > hdr . l3 . id = QETH_HEADER_TYPE_LAYER3 ;
hdr - > hdr . l3 . ext_flags = 0 ;
# ifdef CONFIG_QETH_VLAN
/*
* before we ' re going to overwrite this location with next hop ip .
* v6 uses passthrough , v4 sets the tag in the QDIO header .
*/
if ( card - > vlangrp & & vlan_tx_tag_present ( skb ) ) {
hdr - > hdr . l3 . ext_flags = ( ipv = = 4 ) ?
QETH_HDR_EXT_VLAN_FRAME :
QETH_HDR_EXT_INCLUDE_VLAN_TAG ;
hdr - > hdr . l3 . vlan_id = vlan_tx_tag_get ( skb ) ;
}
# endif /* CONFIG_QETH_VLAN */
hdr - > hdr . l3 . length = skb - > len - sizeof ( struct qeth_hdr ) ;
if ( ipv = = 4 ) { /* IPv4 */
hdr - > hdr . l3 . flags = qeth_get_qeth_hdr_flags4 ( cast_type ) ;
memset ( hdr - > hdr . l3 . dest_addr , 0 , 12 ) ;
if ( ( skb - > dst ) & & ( skb - > dst - > neighbour ) ) {
* ( ( u32 * ) ( & hdr - > hdr . l3 . dest_addr [ 12 ] ) ) =
* ( ( u32 * ) skb - > dst - > neighbour - > primary_key ) ;
} else {
/* fill in destination address used in ip header */
2007-04-21 09:47:35 +04:00
* ( ( u32 * ) ( & hdr - > hdr . l3 . dest_addr [ 12 ] ) ) =
ip_hdr ( skb ) - > daddr ;
2005-04-17 02:20:36 +04:00
}
} else if ( ipv = = 6 ) { /* IPv6 or passthru */
hdr - > hdr . l3 . flags = qeth_get_qeth_hdr_flags6 ( cast_type ) ;
if ( ( skb - > dst ) & & ( skb - > dst - > neighbour ) ) {
memcpy ( hdr - > hdr . l3 . dest_addr ,
skb - > dst - > neighbour - > primary_key , 16 ) ;
} else {
/* fill in destination address used in ip header */
2007-04-26 04:54:47 +04:00
memcpy ( hdr - > hdr . l3 . dest_addr ,
& ipv6_hdr ( skb ) - > daddr , 16 ) ;
2005-04-17 02:20:36 +04:00
}
} else { /* passthrough */
2005-05-12 22:38:11 +04:00
if ( ( skb - > dev - > type = = ARPHRD_IEEE802_TR ) & &
2006-05-27 05:58:38 +04:00
! memcmp ( skb - > data + sizeof ( struct qeth_hdr ) +
2005-05-12 22:38:28 +04:00
sizeof ( __u16 ) , skb - > dev - > broadcast , 6 ) ) {
hdr - > hdr . l3 . flags = QETH_CAST_BROADCAST |
QETH_HDR_PASSTHRU ;
2005-05-12 22:38:11 +04:00
} else if ( ! memcmp ( skb - > data + sizeof ( struct qeth_hdr ) ,
2005-04-17 02:20:36 +04:00
skb - > dev - > broadcast , 6 ) ) { /* broadcast? */
2005-05-12 22:38:11 +04:00
hdr - > hdr . l3 . flags = QETH_CAST_BROADCAST |
QETH_HDR_PASSTHRU ;
2005-04-17 02:20:36 +04:00
} else {
hdr - > hdr . l3 . flags = ( cast_type = = RTN_MULTICAST ) ?
QETH_CAST_MULTICAST | QETH_HDR_PASSTHRU :
QETH_CAST_UNICAST | QETH_HDR_PASSTHRU ;
}
}
}
2007-02-05 23:18:53 +03:00
static void
2005-04-17 02:20:36 +04:00
__qeth_fill_buffer ( struct sk_buff * skb , struct qdio_buffer * buffer ,
2005-05-12 22:39:09 +04:00
int is_tso , int * next_element_to_fill )
2005-04-17 02:20:36 +04:00
{
int length = skb - > len ;
int length_here ;
int element ;
char * data ;
2005-05-12 22:39:09 +04:00
int first_lap ;
2005-04-17 02:20:36 +04:00
element = * next_element_to_fill ;
data = skb - > data ;
2005-05-12 22:39:09 +04:00
first_lap = ( is_tso = = 0 ? 1 : 0 ) ;
2005-04-17 02:20:36 +04:00
while ( length > 0 ) {
/* length_here is the remaining amount of data in this page */
length_here = PAGE_SIZE - ( ( unsigned long ) data % PAGE_SIZE ) ;
if ( length < length_here )
length_here = length ;
2005-05-12 22:39:09 +04:00
2005-04-17 02:20:36 +04:00
buffer - > element [ element ] . addr = data ;
buffer - > element [ element ] . length = length_here ;
length - = length_here ;
2005-05-12 22:39:09 +04:00
if ( ! length ) {
2005-04-17 02:20:36 +04:00
if ( first_lap )
buffer - > element [ element ] . flags = 0 ;
else
buffer - > element [ element ] . flags =
SBAL_FLAGS_LAST_FRAG ;
} else {
if ( first_lap )
buffer - > element [ element ] . flags =
SBAL_FLAGS_FIRST_FRAG ;
else
buffer - > element [ element ] . flags =
SBAL_FLAGS_MIDDLE_FRAG ;
}
data + = length_here ;
element + + ;
first_lap = 0 ;
}
* next_element_to_fill = element ;
}
2007-02-05 23:18:53 +03:00
static int
2005-04-17 02:20:36 +04:00
qeth_fill_buffer ( struct qeth_qdio_out_q * queue ,
struct qeth_qdio_out_buffer * buf ,
struct sk_buff * skb )
{
struct qdio_buffer * buffer ;
2005-05-12 22:39:09 +04:00
struct qeth_hdr_tso * hdr ;
int flush_cnt = 0 , hdr_len , large_send = 0 ;
2005-04-17 02:20:36 +04:00
QETH_DBF_TEXT ( trace , 6 , " qdfillbf " ) ;
2005-05-12 22:39:09 +04:00
2005-04-17 02:20:36 +04:00
buffer = buf - > buffer ;
atomic_inc ( & skb - > users ) ;
skb_queue_tail ( & buf - > skb_list , skb ) ;
2005-05-12 22:39:09 +04:00
hdr = ( struct qeth_hdr_tso * ) skb - > data ;
/*check first on TSO ....*/
if ( hdr - > hdr . hdr . l3 . id = = QETH_HEADER_TYPE_TSO ) {
int element = buf - > next_element_to_fill ;
hdr_len = sizeof ( struct qeth_hdr_tso ) + hdr - > ext . dg_hdr_len ;
/*fill first buffer entry only with header information */
buffer - > element [ element ] . addr = skb - > data ;
buffer - > element [ element ] . length = hdr_len ;
buffer - > element [ element ] . flags = SBAL_FLAGS_FIRST_FRAG ;
buf - > next_element_to_fill + + ;
skb - > data + = hdr_len ;
skb - > len - = hdr_len ;
large_send = 1 ;
}
2005-04-17 02:20:36 +04:00
if ( skb_shinfo ( skb ) - > nr_frags = = 0 )
2005-05-12 22:39:09 +04:00
__qeth_fill_buffer ( skb , buffer , large_send ,
2005-04-17 02:20:36 +04:00
( int * ) & buf - > next_element_to_fill ) ;
else
2005-05-12 22:39:09 +04:00
__qeth_fill_buffer_frag ( skb , buffer , large_send ,
2005-04-17 02:20:36 +04:00
( int * ) & buf - > next_element_to_fill ) ;
if ( ! queue - > do_pack ) {
QETH_DBF_TEXT ( trace , 6 , " fillbfnp " ) ;
/* set state to PRIMED -> will be flushed */
atomic_set ( & buf - > state , QETH_QDIO_BUF_PRIMED ) ;
flush_cnt = 1 ;
} else {
QETH_DBF_TEXT ( trace , 6 , " fillbfpa " ) ;
2006-09-15 18:26:34 +04:00
if ( queue - > card - > options . performance_stats )
queue - > card - > perf_stats . skbs_sent_pack + + ;
2005-04-17 02:20:36 +04:00
if ( buf - > next_element_to_fill > =
QETH_MAX_BUFFER_ELEMENTS ( queue - > card ) ) {
/*
* packed buffer if full - > set state PRIMED
* - > will be flushed
*/
atomic_set ( & buf - > state , QETH_QDIO_BUF_PRIMED ) ;
flush_cnt = 1 ;
}
}
return flush_cnt ;
}
2007-02-05 23:18:53 +03:00
static int
2005-04-17 02:20:36 +04:00
qeth_do_send_packet_fast ( struct qeth_card * card , struct qeth_qdio_out_q * queue ,
struct sk_buff * skb , struct qeth_hdr * hdr ,
int elements_needed ,
struct qeth_eddp_context * ctx )
{
struct qeth_qdio_out_buffer * buffer ;
int buffers_needed = 0 ;
int flush_cnt = 0 ;
int index ;
QETH_DBF_TEXT ( trace , 6 , " dosndpfa " ) ;
/* spin until we get the queue ... */
2006-01-06 11:19:07 +03:00
while ( atomic_cmpxchg ( & queue - > state , QETH_OUT_Q_UNLOCKED ,
QETH_OUT_Q_LOCKED ) ! = QETH_OUT_Q_UNLOCKED ) ;
2005-04-17 02:20:36 +04:00
/* ... now we've got the queue */
index = queue - > next_buf_to_fill ;
buffer = & queue - > bufs [ queue - > next_buf_to_fill ] ;
/*
* check if buffer is empty to make sure that we do not ' overtake '
* ourselves and try to fill a buffer that is already primed
*/
2006-09-15 18:26:19 +04:00
if ( atomic_read ( & buffer - > state ) ! = QETH_QDIO_BUF_EMPTY )
goto out ;
2005-04-17 02:20:36 +04:00
if ( ctx = = NULL )
queue - > next_buf_to_fill = ( queue - > next_buf_to_fill + 1 ) %
QDIO_MAX_BUFFERS_PER_Q ;
else {
buffers_needed = qeth_eddp_check_buffers_for_context ( queue , ctx ) ;
2006-09-15 18:26:19 +04:00
if ( buffers_needed < 0 )
goto out ;
2005-04-17 02:20:36 +04:00
queue - > next_buf_to_fill =
( queue - > next_buf_to_fill + buffers_needed ) %
QDIO_MAX_BUFFERS_PER_Q ;
}
atomic_set ( & queue - > state , QETH_OUT_Q_UNLOCKED ) ;
if ( ctx = = NULL ) {
qeth_fill_buffer ( queue , buffer , skb ) ;
qeth_flush_buffers ( queue , 0 , index , 1 ) ;
} else {
flush_cnt = qeth_eddp_fill_buffer ( queue , ctx , index ) ;
WARN_ON ( buffers_needed ! = flush_cnt ) ;
qeth_flush_buffers ( queue , 0 , index , flush_cnt ) ;
}
return 0 ;
2006-09-15 18:26:19 +04:00
out :
atomic_set ( & queue - > state , QETH_OUT_Q_UNLOCKED ) ;
return - EBUSY ;
2005-04-17 02:20:36 +04:00
}
2007-02-05 23:18:53 +03:00
static int
2005-04-17 02:20:36 +04:00
qeth_do_send_packet ( struct qeth_card * card , struct qeth_qdio_out_q * queue ,
struct sk_buff * skb , struct qeth_hdr * hdr ,
int elements_needed , struct qeth_eddp_context * ctx )
{
struct qeth_qdio_out_buffer * buffer ;
int start_index ;
int flush_count = 0 ;
int do_pack = 0 ;
int tmp ;
int rc = 0 ;
QETH_DBF_TEXT ( trace , 6 , " dosndpkt " ) ;
/* spin until we get the queue ... */
2006-01-06 11:19:07 +03:00
while ( atomic_cmpxchg ( & queue - > state , QETH_OUT_Q_UNLOCKED ,
QETH_OUT_Q_LOCKED ) ! = QETH_OUT_Q_UNLOCKED ) ;
2005-04-17 02:20:36 +04:00
start_index = queue - > next_buf_to_fill ;
buffer = & queue - > bufs [ queue - > next_buf_to_fill ] ;
/*
* check if buffer is empty to make sure that we do not ' overtake '
* ourselves and try to fill a buffer that is already primed
*/
2006-09-15 18:26:19 +04:00
if ( atomic_read ( & buffer - > state ) ! = QETH_QDIO_BUF_EMPTY ) {
2005-04-17 02:20:36 +04:00
atomic_set ( & queue - > state , QETH_OUT_Q_UNLOCKED ) ;
return - EBUSY ;
}
/* check if we need to switch packing state of this queue */
qeth_switch_to_packing_if_needed ( queue ) ;
if ( queue - > do_pack ) {
do_pack = 1 ;
if ( ctx = = NULL ) {
/* does packet fit in current buffer? */
if ( ( QETH_MAX_BUFFER_ELEMENTS ( card ) -
buffer - > next_element_to_fill ) < elements_needed ) {
/* ... no -> set state PRIMED */
atomic_set ( & buffer - > state , QETH_QDIO_BUF_PRIMED ) ;
flush_count + + ;
queue - > next_buf_to_fill =
( queue - > next_buf_to_fill + 1 ) %
QDIO_MAX_BUFFERS_PER_Q ;
buffer = & queue - > bufs [ queue - > next_buf_to_fill ] ;
/* we did a step forward, so check buffer state
* again */
if ( atomic_read ( & buffer - > state ) ! =
QETH_QDIO_BUF_EMPTY ) {
qeth_flush_buffers ( queue , 0 , start_index , flush_count ) ;
atomic_set ( & queue - > state , QETH_OUT_Q_UNLOCKED ) ;
return - EBUSY ;
}
}
} else {
/* check if we have enough elements (including following
* free buffers ) to handle eddp context */
if ( qeth_eddp_check_buffers_for_context ( queue , ctx ) < 0 ) {
printk ( " eddp tx_dropped 1 \n " ) ;
rc = - EBUSY ;
goto out ;
}
}
}
if ( ctx = = NULL )
tmp = qeth_fill_buffer ( queue , buffer , skb ) ;
else {
tmp = qeth_eddp_fill_buffer ( queue , ctx , queue - > next_buf_to_fill ) ;
if ( tmp < 0 ) {
printk ( " eddp tx_dropped 2 \n " ) ;
rc = - EBUSY ;
goto out ;
}
}
queue - > next_buf_to_fill = ( queue - > next_buf_to_fill + tmp ) %
QDIO_MAX_BUFFERS_PER_Q ;
flush_count + = tmp ;
out :
if ( flush_count )
qeth_flush_buffers ( queue , 0 , start_index , flush_count ) ;
2005-11-10 15:50:58 +03:00
else if ( ! atomic_read ( & queue - > set_pci_flags_count ) )
2006-12-04 17:40:59 +03:00
atomic_xchg ( & queue - > state , QETH_OUT_Q_LOCKED_FLUSH ) ;
2005-04-17 02:20:36 +04:00
/*
* queue - > state will go from LOCKED - > UNLOCKED or from
* LOCKED_FLUSH - > LOCKED if output_handler wanted to ' notify ' us
* ( switch packing state or flush buffer to get another pci flag out ) .
* In that case we will enter this loop
*/
while ( atomic_dec_return ( & queue - > state ) ) {
flush_count = 0 ;
start_index = queue - > next_buf_to_fill ;
/* check if we can go back to non-packing state */
flush_count + = qeth_switch_to_nonpacking_if_needed ( queue ) ;
/*
* check if we need to flush a packing buffer to get a pci
* flag out on the queue
*/
if ( ! flush_count & & ! atomic_read ( & queue - > set_pci_flags_count ) )
flush_count + = qeth_flush_buffers_on_no_pci ( queue ) ;
if ( flush_count )
qeth_flush_buffers ( queue , 0 , start_index , flush_count ) ;
}
/* at this point the queue is UNLOCKED again */
2006-09-15 18:26:34 +04:00
if ( queue - > card - > options . performance_stats & & do_pack )
2005-04-17 02:20:36 +04:00
queue - > card - > perf_stats . bufs_sent_pack + = flush_count ;
return rc ;
}
2007-02-05 23:18:53 +03:00
static int
2006-05-27 05:58:38 +04:00
qeth_get_elements_no ( struct qeth_card * card , void * hdr ,
2005-09-14 20:03:26 +04:00
struct sk_buff * skb , int elems )
2005-05-12 22:39:09 +04:00
{
int elements_needed = 0 ;
2006-09-15 18:26:19 +04:00
if ( skb_shinfo ( skb ) - > nr_frags > 0 )
2005-05-12 22:39:09 +04:00
elements_needed = ( skb_shinfo ( skb ) - > nr_frags + 1 ) ;
2006-09-15 18:26:19 +04:00
if ( elements_needed = = 0 )
2005-05-12 22:39:09 +04:00
elements_needed = 1 + ( ( ( ( ( unsigned long ) hdr ) % PAGE_SIZE )
+ skb - > len ) > > PAGE_SHIFT ) ;
2005-09-14 20:03:26 +04:00
if ( ( elements_needed + elems ) > QETH_MAX_BUFFER_ELEMENTS ( card ) ) {
2006-09-15 18:26:19 +04:00
PRINT_ERR ( " Invalid size of IP packet "
" (Number=%d / Length=%d). Discarded. \n " ,
2005-09-14 20:03:26 +04:00
( elements_needed + elems ) , skb - > len ) ;
2005-05-12 22:39:09 +04:00
return 0 ;
}
return elements_needed ;
}
2006-09-15 18:26:19 +04:00
2007-02-05 23:18:53 +03:00
static int
2005-04-17 02:20:36 +04:00
qeth_send_packet ( struct qeth_card * card , struct sk_buff * skb )
{
int ipv = 0 ;
int cast_type ;
struct qeth_qdio_out_q * queue ;
2005-09-14 20:05:31 +04:00
struct qeth_hdr * hdr = NULL ;
2005-04-17 02:20:36 +04:00
int elements_needed = 0 ;
enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO ;
struct qeth_eddp_context * ctx = NULL ;
2006-03-22 18:03:39 +03:00
int tx_bytes = skb - > len ;
2006-05-24 11:51:13 +04:00
unsigned short nr_frags = skb_shinfo ( skb ) - > nr_frags ;
2006-06-22 13:40:14 +04:00
unsigned short tso_size = skb_shinfo ( skb ) - > gso_size ;
2006-09-15 18:26:19 +04:00
struct sk_buff * new_skb , * new_skb2 ;
2005-04-17 02:20:36 +04:00
int rc ;
QETH_DBF_TEXT ( trace , 6 , " sendpkt " ) ;
2006-09-15 18:26:19 +04:00
new_skb = skb ;
if ( ( card - > info . type = = QETH_CARD_TYPE_OSN ) & &
( skb - > protocol = = htons ( ETH_P_IPV6 ) ) )
return - EPERM ;
cast_type = qeth_get_cast_type ( card , skb ) ;
if ( ( cast_type = = RTN_BROADCAST ) & &
( card - > info . broadcast_capable = = 0 ) )
return - EPERM ;
queue = card - > qdio . out_qs
[ qeth_get_priority_queue ( card , skb , ipv , cast_type ) ] ;
2005-04-17 02:20:36 +04:00
if ( ! card - > options . layer2 ) {
ipv = qeth_get_ip_version ( skb ) ;
if ( ( card - > dev - > hard_header = = qeth_fake_header ) & & ipv ) {
2006-09-15 18:26:19 +04:00
new_skb = qeth_pskb_unshare ( skb , GFP_ATOMIC ) ;
if ( ! new_skb )
return - ENOMEM ;
2005-05-12 22:38:11 +04:00
if ( card - > dev - > type = = ARPHRD_IEEE802_TR ) {
2006-09-15 18:26:19 +04:00
skb_pull ( new_skb , QETH_FAKE_LL_LEN_TR ) ;
2005-05-12 22:38:11 +04:00
} else {
2006-09-15 18:26:19 +04:00
skb_pull ( new_skb , QETH_FAKE_LL_LEN_ETH ) ;
2005-05-12 22:38:11 +04:00
}
2005-04-17 02:20:36 +04:00
}
}
2006-07-09 00:34:32 +04:00
if ( skb_is_gso ( skb ) )
2005-04-17 02:20:36 +04:00
large_send = card - > options . large_send ;
2006-09-15 18:26:19 +04:00
/* check on OSN device*/
if ( card - > info . type = = QETH_CARD_TYPE_OSN )
hdr = ( struct qeth_hdr * ) new_skb - > data ;
/*are we able to do TSO ? */
2005-04-17 02:20:36 +04:00
if ( ( large_send = = QETH_LARGE_SEND_TSO ) & &
( cast_type = = RTN_UNSPEC ) ) {
2006-09-15 18:26:19 +04:00
rc = qeth_tso_prepare_packet ( card , new_skb , ipv , cast_type ) ;
2005-05-12 22:39:09 +04:00
if ( rc ) {
2006-09-15 18:26:19 +04:00
__qeth_free_new_skb ( skb , new_skb ) ;
return rc ;
2006-05-27 05:58:38 +04:00
}
2005-05-12 22:39:09 +04:00
elements_needed + + ;
2006-09-15 18:26:19 +04:00
} else if ( card - > info . type ! = QETH_CARD_TYPE_OSN ) {
new_skb2 = qeth_prepare_skb ( card , new_skb , & hdr , ipv ) ;
if ( ! new_skb2 ) {
__qeth_free_new_skb ( skb , new_skb ) ;
return - EINVAL ;
2005-05-12 22:39:09 +04:00
}
2006-09-15 18:26:19 +04:00
if ( new_skb ! = skb )
__qeth_free_new_skb ( new_skb2 , new_skb ) ;
new_skb = new_skb2 ;
qeth_fill_header ( card , hdr , new_skb , ipv , cast_type ) ;
2005-04-17 02:20:36 +04:00
}
if ( large_send = = QETH_LARGE_SEND_EDDP ) {
2007-05-02 17:17:11 +04:00
ctx = qeth_eddp_create_context ( card , new_skb , hdr ,
skb - > sk - > sk_protocol ) ;
2005-04-17 02:20:36 +04:00
if ( ctx = = NULL ) {
2006-09-15 18:26:19 +04:00
__qeth_free_new_skb ( skb , new_skb ) ;
2005-04-17 02:20:36 +04:00
PRINT_WARN ( " could not create eddp context \n " ) ;
return - EINVAL ;
}
} else {
2006-09-15 18:26:19 +04:00
int elems = qeth_get_elements_no ( card , ( void * ) hdr , new_skb ,
2005-09-14 20:03:26 +04:00
elements_needed ) ;
2006-09-15 18:26:19 +04:00
if ( ! elems ) {
__qeth_free_new_skb ( skb , new_skb ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2006-09-15 18:26:19 +04:00
}
2005-09-14 20:03:26 +04:00
elements_needed + = elems ;
2005-04-17 02:20:36 +04:00
}
if ( card - > info . type ! = QETH_CARD_TYPE_IQD )
2006-09-15 18:26:19 +04:00
rc = qeth_do_send_packet ( card , queue , new_skb , hdr ,
2005-04-17 02:20:36 +04:00
elements_needed , ctx ) ;
else
2006-09-15 18:26:19 +04:00
rc = qeth_do_send_packet_fast ( card , queue , new_skb , hdr ,
2005-04-17 02:20:36 +04:00
elements_needed , ctx ) ;
2006-09-15 18:26:19 +04:00
if ( ! rc ) {
2005-04-17 02:20:36 +04:00
card - > stats . tx_packets + + ;
2006-03-22 18:03:39 +03:00
card - > stats . tx_bytes + = tx_bytes ;
2006-09-15 18:26:19 +04:00
if ( new_skb ! = skb )
dev_kfree_skb_any ( skb ) ;
2006-09-15 18:26:34 +04:00
if ( card - > options . performance_stats ) {
if ( tso_size & &
! ( large_send = = QETH_LARGE_SEND_NO ) ) {
card - > perf_stats . large_send_bytes + = tx_bytes ;
card - > perf_stats . large_send_cnt + + ;
}
if ( nr_frags > 0 ) {
card - > perf_stats . sg_skbs_sent + + ;
/* nr_frags + skb->data */
card - > perf_stats . sg_frags_sent + =
nr_frags + 1 ;
}
2005-04-17 02:20:36 +04:00
}
2006-09-15 18:26:19 +04:00
} else {
card - > stats . tx_dropped + + ;
__qeth_free_new_skb ( skb , new_skb ) ;
2005-04-17 02:20:36 +04:00
}
if ( ctx ! = NULL ) {
/* drop creator's reference */
qeth_eddp_put_context ( ctx ) ;
/* free skb; it's not referenced by a buffer */
2006-09-15 18:26:19 +04:00
if ( ! rc )
dev_kfree_skb_any ( new_skb ) ;
2005-04-17 02:20:36 +04:00
}
return rc ;
}
static int
qeth_mdio_read ( struct net_device * dev , int phy_id , int regnum )
{
struct qeth_card * card = ( struct qeth_card * ) dev - > priv ;
int rc = 0 ;
switch ( regnum ) {
case MII_BMCR : /* Basic mode control register */
rc = BMCR_FULLDPLX ;
if ( ( card - > info . link_type ! = QETH_LINK_TYPE_GBIT_ETH ) & &
2005-09-30 12:19:19 +04:00
( card - > info . link_type ! = QETH_LINK_TYPE_OSN ) & &
2005-04-17 02:20:36 +04:00
( card - > info . link_type ! = QETH_LINK_TYPE_10GBIT_ETH ) )
rc | = BMCR_SPEED100 ;
break ;
case MII_BMSR : /* Basic mode status register */
rc = BMSR_ERCAP | BMSR_ANEGCOMPLETE | BMSR_LSTATUS |
BMSR_10HALF | BMSR_10FULL | BMSR_100HALF | BMSR_100FULL |
BMSR_100BASE4 ;
break ;
case MII_PHYSID1 : /* PHYS ID 1 */
rc = ( dev - > dev_addr [ 0 ] < < 16 ) | ( dev - > dev_addr [ 1 ] < < 8 ) |
dev - > dev_addr [ 2 ] ;
rc = ( rc > > 5 ) & 0xFFFF ;
break ;
case MII_PHYSID2 : /* PHYS ID 2 */
rc = ( dev - > dev_addr [ 2 ] < < 10 ) & 0xFFFF ;
break ;
case MII_ADVERTISE : /* Advertisement control reg */
rc = ADVERTISE_ALL ;
break ;
case MII_LPA : /* Link partner ability reg */
rc = LPA_10HALF | LPA_10FULL | LPA_100HALF | LPA_100FULL |
LPA_100BASE4 | LPA_LPACK ;
break ;
case MII_EXPANSION : /* Expansion register */
break ;
case MII_DCOUNTER : /* disconnect counter */
break ;
case MII_FCSCOUNTER : /* false carrier counter */
break ;
case MII_NWAYTEST : /* N-way auto-neg test register */
break ;
case MII_RERRCOUNTER : /* rx error counter */
rc = card - > stats . rx_errors ;
break ;
case MII_SREVISION : /* silicon revision */
break ;
case MII_RESV1 : /* reserved 1 */
break ;
case MII_LBRERROR : /* loopback, rx, bypass error */
break ;
case MII_PHYADDR : /* physical address */
break ;
case MII_RESV2 : /* reserved 2 */
break ;
case MII_TPISTATUS : /* TPI status for 10mbps */
break ;
case MII_NCONFIG : /* network interface config */
break ;
default :
break ;
}
return rc ;
}
2007-02-05 23:18:53 +03:00
static const char *
2005-04-17 02:20:36 +04:00
qeth_arp_get_error_cause ( int * rc )
{
switch ( * rc ) {
case QETH_IPA_ARP_RC_FAILED :
* rc = - EIO ;
return " operation failed " ;
case QETH_IPA_ARP_RC_NOTSUPP :
* rc = - EOPNOTSUPP ;
return " operation not supported " ;
case QETH_IPA_ARP_RC_OUT_OF_RANGE :
* rc = - EINVAL ;
return " argument out of range " ;
case QETH_IPA_ARP_RC_Q_NOTSUPP :
* rc = - EOPNOTSUPP ;
return " query operation not supported " ;
case QETH_IPA_ARP_RC_Q_NO_DATA :
* rc = - ENOENT ;
return " no query data available " ;
default :
return " unknown error " ;
}
}
static int
qeth_send_simple_setassparms ( struct qeth_card * , enum qeth_ipa_funcs ,
__u16 , long ) ;
static int
qeth_arp_set_no_entries ( struct qeth_card * card , int no_entries )
{
int tmp ;
int rc ;
QETH_DBF_TEXT ( trace , 3 , " arpstnoe " ) ;
2005-09-14 20:05:31 +04:00
/*
* currently GuestLAN only supports the ARP assist function
* IPA_CMD_ASS_ARP_QUERY_INFO , but not IPA_CMD_ASS_ARP_SET_NO_ENTRIES ;
* thus we say EOPNOTSUPP for this ARP function
*/
2005-04-17 02:20:36 +04:00
if ( card - > info . guestlan )
return - EOPNOTSUPP ;
if ( ! qeth_is_supported ( card , IPA_ARP_PROCESSING ) ) {
PRINT_WARN ( " ARP processing not supported "
" on %s! \n " , QETH_CARD_IFNAME ( card ) ) ;
return - EOPNOTSUPP ;
}
rc = qeth_send_simple_setassparms ( card , IPA_ARP_PROCESSING ,
IPA_CMD_ASS_ARP_SET_NO_ENTRIES ,
no_entries ) ;
if ( rc ) {
tmp = rc ;
PRINT_WARN ( " Could not set number of ARP entries on %s: "
" %s (0x%x/%d) \n " ,
QETH_CARD_IFNAME ( card ) , qeth_arp_get_error_cause ( & rc ) ,
tmp , tmp ) ;
}
return rc ;
}
2007-02-05 23:18:53 +03:00
static void
2005-04-17 02:20:36 +04:00
qeth_copy_arp_entries_stripped ( struct qeth_arp_query_info * qinfo ,
struct qeth_arp_query_data * qdata ,
int entry_size , int uentry_size )
{
char * entry_ptr ;
char * uentry_ptr ;
int i ;
entry_ptr = ( char * ) & qdata - > data ;
uentry_ptr = ( char * ) ( qinfo - > udata + qinfo - > udata_offset ) ;
for ( i = 0 ; i < qdata - > no_entries ; + + i ) {
/* strip off 32 bytes "media specific information" */
memcpy ( uentry_ptr , ( entry_ptr + 32 ) , entry_size - 32 ) ;
entry_ptr + = entry_size ;
uentry_ptr + = uentry_size ;
}
}
static int
qeth_arp_query_cb ( struct qeth_card * card , struct qeth_reply * reply ,
unsigned long data )
{
struct qeth_ipa_cmd * cmd ;
struct qeth_arp_query_data * qdata ;
struct qeth_arp_query_info * qinfo ;
int entry_size ;
int uentry_size ;
int i ;
QETH_DBF_TEXT ( trace , 4 , " arpquecb " ) ;
qinfo = ( struct qeth_arp_query_info * ) reply - > param ;
cmd = ( struct qeth_ipa_cmd * ) data ;
if ( cmd - > hdr . return_code ) {
QETH_DBF_TEXT_ ( trace , 4 , " qaer1%i " , cmd - > hdr . return_code ) ;
return 0 ;
}
if ( cmd - > data . setassparms . hdr . return_code ) {
cmd - > hdr . return_code = cmd - > data . setassparms . hdr . return_code ;
QETH_DBF_TEXT_ ( trace , 4 , " qaer2%i " , cmd - > hdr . return_code ) ;
return 0 ;
}
qdata = & cmd - > data . setassparms . data . query_arp ;
switch ( qdata - > reply_bits ) {
case 5 :
uentry_size = entry_size = sizeof ( struct qeth_arp_qi_entry5 ) ;
if ( qinfo - > mask_bits & QETH_QARP_STRIP_ENTRIES )
uentry_size = sizeof ( struct qeth_arp_qi_entry5_short ) ;
break ;
case 7 :
/* fall through to default */
default :
/* tr is the same as eth -> entry7 */
uentry_size = entry_size = sizeof ( struct qeth_arp_qi_entry7 ) ;
if ( qinfo - > mask_bits & QETH_QARP_STRIP_ENTRIES )
uentry_size = sizeof ( struct qeth_arp_qi_entry7_short ) ;
break ;
}
/* check if there is enough room in userspace */
if ( ( qinfo - > udata_len - qinfo - > udata_offset ) <
qdata - > no_entries * uentry_size ) {
QETH_DBF_TEXT_ ( trace , 4 , " qaer3%i " , - ENOMEM ) ;
cmd - > hdr . return_code = - ENOMEM ;
PRINT_WARN ( " query ARP user space buffer is too small for "
" the returned number of ARP entries. "
" Aborting query! \n " ) ;
goto out_error ;
}
QETH_DBF_TEXT_ ( trace , 4 , " anore%i " ,
cmd - > data . setassparms . hdr . number_of_replies ) ;
QETH_DBF_TEXT_ ( trace , 4 , " aseqn%i " , cmd - > data . setassparms . hdr . seq_no ) ;
QETH_DBF_TEXT_ ( trace , 4 , " anoen%i " , qdata - > no_entries ) ;
if ( qinfo - > mask_bits & QETH_QARP_STRIP_ENTRIES ) {
/* strip off "media specific information" */
qeth_copy_arp_entries_stripped ( qinfo , qdata , entry_size ,
uentry_size ) ;
} else
/*copy entries to user buffer*/
memcpy ( qinfo - > udata + qinfo - > udata_offset ,
( char * ) & qdata - > data , qdata - > no_entries * uentry_size ) ;
qinfo - > no_entries + = qdata - > no_entries ;
qinfo - > udata_offset + = ( qdata - > no_entries * uentry_size ) ;
/* check if all replies received ... */
if ( cmd - > data . setassparms . hdr . seq_no <
cmd - > data . setassparms . hdr . number_of_replies )
return 1 ;
memcpy ( qinfo - > udata , & qinfo - > no_entries , 4 ) ;
/* keep STRIP_ENTRIES flag so the user program can distinguish
* stripped entries from normal ones */
if ( qinfo - > mask_bits & QETH_QARP_STRIP_ENTRIES )
qdata - > reply_bits | = QETH_QARP_STRIP_ENTRIES ;
memcpy ( qinfo - > udata + QETH_QARP_MASK_OFFSET , & qdata - > reply_bits , 2 ) ;
return 0 ;
out_error :
i = 0 ;
memcpy ( qinfo - > udata , & i , 4 ) ;
return 0 ;
}
static int
qeth_send_ipa_arp_cmd ( struct qeth_card * card , struct qeth_cmd_buffer * iob ,
int len , int ( * reply_cb ) ( struct qeth_card * ,
struct qeth_reply * ,
unsigned long ) ,
void * reply_param )
{
QETH_DBF_TEXT ( trace , 4 , " sendarp " ) ;
memcpy ( iob - > data , IPA_PDU_HEADER , IPA_PDU_HEADER_SIZE ) ;
memcpy ( QETH_IPA_CMD_DEST_ADDR ( iob - > data ) ,
& card - > token . ulp_connection_r , QETH_MPC_TOKEN_LENGTH ) ;
return qeth_send_control_data ( card , IPA_PDU_HEADER_SIZE + len , iob ,
reply_cb , reply_param ) ;
}
static int
qeth_send_ipa_snmp_cmd ( struct qeth_card * card , struct qeth_cmd_buffer * iob ,
int len , int ( * reply_cb ) ( struct qeth_card * ,
struct qeth_reply * ,
unsigned long ) ,
void * reply_param )
{
u16 s1 , s2 ;
QETH_DBF_TEXT ( trace , 4 , " sendsnmp " ) ;
memcpy ( iob - > data , IPA_PDU_HEADER , IPA_PDU_HEADER_SIZE ) ;
memcpy ( QETH_IPA_CMD_DEST_ADDR ( iob - > data ) ,
& card - > token . ulp_connection_r , QETH_MPC_TOKEN_LENGTH ) ;
/* adjust PDU length fields in IPA_PDU_HEADER */
s1 = ( u32 ) IPA_PDU_HEADER_SIZE + len ;
s2 = ( u32 ) len ;
memcpy ( QETH_IPA_PDU_LEN_TOTAL ( iob - > data ) , & s1 , 2 ) ;
memcpy ( QETH_IPA_PDU_LEN_PDU1 ( iob - > data ) , & s2 , 2 ) ;
memcpy ( QETH_IPA_PDU_LEN_PDU2 ( iob - > data ) , & s2 , 2 ) ;
memcpy ( QETH_IPA_PDU_LEN_PDU3 ( iob - > data ) , & s2 , 2 ) ;
return qeth_send_control_data ( card , IPA_PDU_HEADER_SIZE + len , iob ,
reply_cb , reply_param ) ;
}
static struct qeth_cmd_buffer *
qeth_get_setassparms_cmd ( struct qeth_card * , enum qeth_ipa_funcs ,
__u16 , __u16 , enum qeth_prot_versions ) ;
static int
2006-07-12 18:41:55 +04:00
qeth_arp_query ( struct qeth_card * card , char __user * udata )
2005-04-17 02:20:36 +04:00
{
struct qeth_cmd_buffer * iob ;
struct qeth_arp_query_info qinfo = { 0 , } ;
int tmp ;
int rc ;
QETH_DBF_TEXT ( trace , 3 , " arpquery " ) ;
if ( ! qeth_is_supported ( card , /*IPA_QUERY_ARP_ADDR_INFO*/
IPA_ARP_PROCESSING ) ) {
PRINT_WARN ( " ARP processing not supported "
" on %s! \n " , QETH_CARD_IFNAME ( card ) ) ;
return - EOPNOTSUPP ;
}
/* get size of userspace buffer and mask_bits -> 6 bytes */
if ( copy_from_user ( & qinfo , udata , 6 ) )
return - EFAULT ;
2006-03-24 14:15:31 +03:00
if ( ! ( qinfo . udata = kzalloc ( qinfo . udata_len , GFP_KERNEL ) ) )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
qinfo . udata_offset = QETH_QARP_ENTRIES_OFFSET ;
iob = qeth_get_setassparms_cmd ( card , IPA_ARP_PROCESSING ,
IPA_CMD_ASS_ARP_QUERY_INFO ,
sizeof ( int ) , QETH_PROT_IPV4 ) ;
rc = qeth_send_ipa_arp_cmd ( card , iob ,
QETH_SETASS_BASE_LEN + QETH_ARP_CMD_LEN ,
qeth_arp_query_cb , ( void * ) & qinfo ) ;
if ( rc ) {
tmp = rc ;
PRINT_WARN ( " Error while querying ARP cache on %s: %s "
" (0x%x/%d) \n " ,
QETH_CARD_IFNAME ( card ) , qeth_arp_get_error_cause ( & rc ) ,
tmp , tmp ) ;
2006-12-28 15:05:36 +03:00
if ( copy_to_user ( udata , qinfo . udata , 4 ) )
rc = - EFAULT ;
2005-04-17 02:20:36 +04:00
} else {
2006-12-28 15:05:36 +03:00
if ( copy_to_user ( udata , qinfo . udata , qinfo . udata_len ) )
rc = - EFAULT ;
2005-04-17 02:20:36 +04:00
}
kfree ( qinfo . udata ) ;
return rc ;
}
/**
* SNMP command callback
*/
static int
qeth_snmp_command_cb ( struct qeth_card * card , struct qeth_reply * reply ,
unsigned long sdata )
{
struct qeth_ipa_cmd * cmd ;
struct qeth_arp_query_info * qinfo ;
struct qeth_snmp_cmd * snmp ;
unsigned char * data ;
__u16 data_len ;
QETH_DBF_TEXT ( trace , 3 , " snpcmdcb " ) ;
cmd = ( struct qeth_ipa_cmd * ) sdata ;
data = ( unsigned char * ) ( ( char * ) cmd - reply - > offset ) ;
qinfo = ( struct qeth_arp_query_info * ) reply - > param ;
snmp = & cmd - > data . setadapterparms . data . snmp ;
if ( cmd - > hdr . return_code ) {
QETH_DBF_TEXT_ ( trace , 4 , " scer1%i " , cmd - > hdr . return_code ) ;
return 0 ;
}
if ( cmd - > data . setadapterparms . hdr . return_code ) {
cmd - > hdr . return_code = cmd - > data . setadapterparms . hdr . return_code ;
QETH_DBF_TEXT_ ( trace , 4 , " scer2%i " , cmd - > hdr . return_code ) ;
return 0 ;
}
data_len = * ( ( __u16 * ) QETH_IPA_PDU_LEN_PDU1 ( data ) ) ;
if ( cmd - > data . setadapterparms . hdr . seq_no = = 1 )
data_len - = ( __u16 ) ( ( char * ) & snmp - > data - ( char * ) cmd ) ;
else
data_len - = ( __u16 ) ( ( char * ) & snmp - > request - ( char * ) cmd ) ;
/* check if there is enough room in userspace */
if ( ( qinfo - > udata_len - qinfo - > udata_offset ) < data_len ) {
QETH_DBF_TEXT_ ( trace , 4 , " scer3%i " , - ENOMEM ) ;
cmd - > hdr . return_code = - ENOMEM ;
return 0 ;
}
QETH_DBF_TEXT_ ( trace , 4 , " snore%i " ,
cmd - > data . setadapterparms . hdr . used_total ) ;
QETH_DBF_TEXT_ ( trace , 4 , " sseqn%i " , cmd - > data . setadapterparms . hdr . seq_no ) ;
/*copy entries to user buffer*/
if ( cmd - > data . setadapterparms . hdr . seq_no = = 1 ) {
memcpy ( qinfo - > udata + qinfo - > udata_offset ,
( char * ) snmp ,
data_len + offsetof ( struct qeth_snmp_cmd , data ) ) ;
qinfo - > udata_offset + = offsetof ( struct qeth_snmp_cmd , data ) ;
} else {
memcpy ( qinfo - > udata + qinfo - > udata_offset ,
( char * ) & snmp - > request , data_len ) ;
}
qinfo - > udata_offset + = data_len ;
/* check if all replies received ... */
QETH_DBF_TEXT_ ( trace , 4 , " srtot%i " ,
cmd - > data . setadapterparms . hdr . used_total ) ;
QETH_DBF_TEXT_ ( trace , 4 , " srseq%i " ,
cmd - > data . setadapterparms . hdr . seq_no ) ;
if ( cmd - > data . setadapterparms . hdr . seq_no <
cmd - > data . setadapterparms . hdr . used_total )
return 1 ;
return 0 ;
}
static struct qeth_cmd_buffer *
qeth_get_ipacmd_buffer ( struct qeth_card * , enum qeth_ipa_cmds ,
enum qeth_prot_versions ) ;
static struct qeth_cmd_buffer *
qeth_get_adapter_cmd ( struct qeth_card * card , __u32 command , __u32 cmdlen )
{
struct qeth_cmd_buffer * iob ;
struct qeth_ipa_cmd * cmd ;
iob = qeth_get_ipacmd_buffer ( card , IPA_CMD_SETADAPTERPARMS ,
QETH_PROT_IPV4 ) ;
cmd = ( struct qeth_ipa_cmd * ) ( iob - > data + IPA_PDU_HEADER_SIZE ) ;
cmd - > data . setadapterparms . hdr . cmdlength = cmdlen ;
cmd - > data . setadapterparms . hdr . command_code = command ;
cmd - > data . setadapterparms . hdr . used_total = 1 ;
cmd - > data . setadapterparms . hdr . seq_no = 1 ;
return iob ;
}
/**
* function to send SNMP commands to OSA - E card
*/
static int
2006-07-12 18:41:55 +04:00
qeth_snmp_command ( struct qeth_card * card , char __user * udata )
2005-04-17 02:20:36 +04:00
{
struct qeth_cmd_buffer * iob ;
struct qeth_ipa_cmd * cmd ;
struct qeth_snmp_ureq * ureq ;
int req_len ;
struct qeth_arp_query_info qinfo = { 0 , } ;
int rc = 0 ;
QETH_DBF_TEXT ( trace , 3 , " snmpcmd " ) ;
if ( card - > info . guestlan )
return - EOPNOTSUPP ;
if ( ( ! qeth_adp_supported ( card , IPA_SETADP_SET_SNMP_CONTROL ) ) & &
( ! card - > options . layer2 ) ) {
PRINT_WARN ( " SNMP Query MIBS not supported "
" on %s! \n " , QETH_CARD_IFNAME ( card ) ) ;
return - EOPNOTSUPP ;
}
/* skip 4 bytes (data_len struct member) to get req_len */
if ( copy_from_user ( & req_len , udata + sizeof ( int ) , sizeof ( int ) ) )
return - EFAULT ;
ureq = kmalloc ( req_len + sizeof ( struct qeth_snmp_ureq_hdr ) , GFP_KERNEL ) ;
if ( ! ureq ) {
QETH_DBF_TEXT ( trace , 2 , " snmpnome " ) ;
return - ENOMEM ;
}
if ( copy_from_user ( ureq , udata ,
req_len + sizeof ( struct qeth_snmp_ureq_hdr ) ) ) {
kfree ( ureq ) ;
return - EFAULT ;
}
qinfo . udata_len = ureq - > hdr . data_len ;
2006-03-24 14:15:31 +03:00
if ( ! ( qinfo . udata = kzalloc ( qinfo . udata_len , GFP_KERNEL ) ) ) {
2005-04-17 02:20:36 +04:00
kfree ( ureq ) ;
return - ENOMEM ;
}
qinfo . udata_offset = sizeof ( struct qeth_snmp_ureq_hdr ) ;
iob = qeth_get_adapter_cmd ( card , IPA_SETADP_SET_SNMP_CONTROL ,
QETH_SNMP_SETADP_CMDLENGTH + req_len ) ;
cmd = ( struct qeth_ipa_cmd * ) ( iob - > data + IPA_PDU_HEADER_SIZE ) ;
memcpy ( & cmd - > data . setadapterparms . data . snmp , & ureq - > cmd , req_len ) ;
rc = qeth_send_ipa_snmp_cmd ( card , iob , QETH_SETADP_BASE_LEN + req_len ,
qeth_snmp_command_cb , ( void * ) & qinfo ) ;
if ( rc )
PRINT_WARN ( " SNMP command failed on %s: (0x%x) \n " ,
QETH_CARD_IFNAME ( card ) , rc ) ;
2006-12-28 15:05:36 +03:00
else {
if ( copy_to_user ( udata , qinfo . udata , qinfo . udata_len ) )
rc = - EFAULT ;
}
2005-04-17 02:20:36 +04:00
kfree ( ureq ) ;
kfree ( qinfo . udata ) ;
return rc ;
}
static int
qeth_default_setassparms_cb ( struct qeth_card * , struct qeth_reply * ,
unsigned long ) ;
static int
2005-11-10 15:51:25 +03:00
qeth_default_setadapterparms_cb ( struct qeth_card * card ,
struct qeth_reply * reply ,
unsigned long data ) ;
static int
2005-04-17 02:20:36 +04:00
qeth_send_setassparms ( struct qeth_card * , struct qeth_cmd_buffer * ,
__u16 , long ,
int ( * reply_cb )
( struct qeth_card * , struct qeth_reply * , unsigned long ) ,
void * reply_param ) ;
static int
qeth_arp_add_entry ( struct qeth_card * card , struct qeth_arp_cache_entry * entry )
{
struct qeth_cmd_buffer * iob ;
char buf [ 16 ] ;
int tmp ;
int rc ;
QETH_DBF_TEXT ( trace , 3 , " arpadent " ) ;
/*
2005-09-14 20:05:31 +04:00
* currently GuestLAN only supports the ARP assist function
* IPA_CMD_ASS_ARP_QUERY_INFO , but not IPA_CMD_ASS_ARP_ADD_ENTRY ;
* thus we say EOPNOTSUPP for this ARP function
2005-04-17 02:20:36 +04:00
*/
if ( card - > info . guestlan )
return - EOPNOTSUPP ;
if ( ! qeth_is_supported ( card , IPA_ARP_PROCESSING ) ) {
PRINT_WARN ( " ARP processing not supported "
" on %s! \n " , QETH_CARD_IFNAME ( card ) ) ;
return - EOPNOTSUPP ;
}
iob = qeth_get_setassparms_cmd ( card , IPA_ARP_PROCESSING ,
IPA_CMD_ASS_ARP_ADD_ENTRY ,
sizeof ( struct qeth_arp_cache_entry ) ,
QETH_PROT_IPV4 ) ;
rc = qeth_send_setassparms ( card , iob ,
sizeof ( struct qeth_arp_cache_entry ) ,
( unsigned long ) entry ,
qeth_default_setassparms_cb , NULL ) ;
if ( rc ) {
tmp = rc ;
qeth_ipaddr4_to_string ( ( u8 * ) entry - > ipaddr , buf ) ;
PRINT_WARN ( " Could not add ARP entry for address %s on %s: "
" %s (0x%x/%d) \n " ,
buf , QETH_CARD_IFNAME ( card ) ,
qeth_arp_get_error_cause ( & rc ) , tmp , tmp ) ;
}
return rc ;
}
static int
qeth_arp_remove_entry ( struct qeth_card * card , struct qeth_arp_cache_entry * entry )
{
struct qeth_cmd_buffer * iob ;
char buf [ 16 ] = { 0 , } ;
int tmp ;
int rc ;
QETH_DBF_TEXT ( trace , 3 , " arprment " ) ;
/*
2005-09-14 20:05:31 +04:00
* currently GuestLAN only supports the ARP assist function
* IPA_CMD_ASS_ARP_QUERY_INFO , but not IPA_CMD_ASS_ARP_REMOVE_ENTRY ;
* thus we say EOPNOTSUPP for this ARP function
2005-04-17 02:20:36 +04:00
*/
if ( card - > info . guestlan )
return - EOPNOTSUPP ;
if ( ! qeth_is_supported ( card , IPA_ARP_PROCESSING ) ) {
PRINT_WARN ( " ARP processing not supported "
" on %s! \n " , QETH_CARD_IFNAME ( card ) ) ;
return - EOPNOTSUPP ;
}
memcpy ( buf , entry , 12 ) ;
iob = qeth_get_setassparms_cmd ( card , IPA_ARP_PROCESSING ,
IPA_CMD_ASS_ARP_REMOVE_ENTRY ,
12 ,
QETH_PROT_IPV4 ) ;
rc = qeth_send_setassparms ( card , iob ,
12 , ( unsigned long ) buf ,
qeth_default_setassparms_cb , NULL ) ;
if ( rc ) {
tmp = rc ;
memset ( buf , 0 , 16 ) ;
qeth_ipaddr4_to_string ( ( u8 * ) entry - > ipaddr , buf ) ;
PRINT_WARN ( " Could not delete ARP entry for address %s on %s: "
" %s (0x%x/%d) \n " ,
buf , QETH_CARD_IFNAME ( card ) ,
qeth_arp_get_error_cause ( & rc ) , tmp , tmp ) ;
}
return rc ;
}
static int
qeth_arp_flush_cache ( struct qeth_card * card )
{
int rc ;
int tmp ;
QETH_DBF_TEXT ( trace , 3 , " arpflush " ) ;
/*
2005-09-14 20:05:31 +04:00
* currently GuestLAN only supports the ARP assist function
* IPA_CMD_ASS_ARP_QUERY_INFO , but not IPA_CMD_ASS_ARP_FLUSH_CACHE ;
* thus we say EOPNOTSUPP for this ARP function
*/
2005-04-17 02:20:36 +04:00
if ( card - > info . guestlan | | ( card - > info . type = = QETH_CARD_TYPE_IQD ) )
return - EOPNOTSUPP ;
if ( ! qeth_is_supported ( card , IPA_ARP_PROCESSING ) ) {
PRINT_WARN ( " ARP processing not supported "
" on %s! \n " , QETH_CARD_IFNAME ( card ) ) ;
return - EOPNOTSUPP ;
}
rc = qeth_send_simple_setassparms ( card , IPA_ARP_PROCESSING ,
IPA_CMD_ASS_ARP_FLUSH_CACHE , 0 ) ;
if ( rc ) {
tmp = rc ;
PRINT_WARN ( " Could not flush ARP cache on %s: %s (0x%x/%d) \n " ,
QETH_CARD_IFNAME ( card ) , qeth_arp_get_error_cause ( & rc ) ,
tmp , tmp ) ;
}
return rc ;
}
static int
qeth_do_ioctl ( struct net_device * dev , struct ifreq * rq , int cmd )
{
struct qeth_card * card = ( struct qeth_card * ) dev - > priv ;
struct qeth_arp_cache_entry arp_entry ;
struct mii_ioctl_data * mii_data ;
int rc = 0 ;
if ( ! card )
return - ENODEV ;
if ( ( card - > state ! = CARD_STATE_UP ) & &
( card - > state ! = CARD_STATE_SOFTSETUP ) )
return - ENODEV ;
2005-09-30 12:19:19 +04:00
if ( card - > info . type = = QETH_CARD_TYPE_OSN )
return - EPERM ;
2005-04-17 02:20:36 +04:00
switch ( cmd ) {
case SIOC_QETH_ARP_SET_NO_ENTRIES :
if ( ! capable ( CAP_NET_ADMIN ) | |
( card - > options . layer2 ) ) {
rc = - EPERM ;
break ;
}
rc = qeth_arp_set_no_entries ( card , rq - > ifr_ifru . ifru_ivalue ) ;
break ;
case SIOC_QETH_ARP_QUERY_INFO :
if ( ! capable ( CAP_NET_ADMIN ) | |
( card - > options . layer2 ) ) {
rc = - EPERM ;
break ;
}
rc = qeth_arp_query ( card , rq - > ifr_ifru . ifru_data ) ;
break ;
case SIOC_QETH_ARP_ADD_ENTRY :
if ( ! capable ( CAP_NET_ADMIN ) | |
( card - > options . layer2 ) ) {
rc = - EPERM ;
break ;
}
if ( copy_from_user ( & arp_entry , rq - > ifr_ifru . ifru_data ,
sizeof ( struct qeth_arp_cache_entry ) ) )
rc = - EFAULT ;
else
rc = qeth_arp_add_entry ( card , & arp_entry ) ;
break ;
case SIOC_QETH_ARP_REMOVE_ENTRY :
if ( ! capable ( CAP_NET_ADMIN ) | |
( card - > options . layer2 ) ) {
rc = - EPERM ;
break ;
}
if ( copy_from_user ( & arp_entry , rq - > ifr_ifru . ifru_data ,
sizeof ( struct qeth_arp_cache_entry ) ) )
rc = - EFAULT ;
else
rc = qeth_arp_remove_entry ( card , & arp_entry ) ;
break ;
case SIOC_QETH_ARP_FLUSH_CACHE :
if ( ! capable ( CAP_NET_ADMIN ) | |
( card - > options . layer2 ) ) {
rc = - EPERM ;
break ;
}
rc = qeth_arp_flush_cache ( card ) ;
break ;
case SIOC_QETH_ADP_SET_SNMP_CONTROL :
rc = qeth_snmp_command ( card , rq - > ifr_ifru . ifru_data ) ;
break ;
case SIOC_QETH_GET_CARD_TYPE :
if ( ( card - > info . type = = QETH_CARD_TYPE_OSAE ) & &
! card - > info . guestlan )
return 1 ;
return 0 ;
break ;
case SIOCGMIIPHY :
mii_data = if_mii ( rq ) ;
mii_data - > phy_id = 0 ;
break ;
case SIOCGMIIREG :
mii_data = if_mii ( rq ) ;
if ( mii_data - > phy_id ! = 0 )
rc = - EINVAL ;
else
mii_data - > val_out = qeth_mdio_read ( dev , mii_data - > phy_id ,
mii_data - > reg_num ) ;
break ;
default :
rc = - EOPNOTSUPP ;
}
if ( rc )
QETH_DBF_TEXT_ ( trace , 2 , " ioce%d " , rc ) ;
return rc ;
}
static struct net_device_stats *
qeth_get_stats ( struct net_device * dev )
{
struct qeth_card * card ;
card = ( struct qeth_card * ) ( dev - > priv ) ;
QETH_DBF_TEXT ( trace , 5 , " getstat " ) ;
return & card - > stats ;
}
static int
qeth_change_mtu ( struct net_device * dev , int new_mtu )
{
struct qeth_card * card ;
char dbf_text [ 15 ] ;
card = ( struct qeth_card * ) ( dev - > priv ) ;
QETH_DBF_TEXT ( trace , 4 , " chgmtu " ) ;
sprintf ( dbf_text , " %8x " , new_mtu ) ;
QETH_DBF_TEXT ( trace , 4 , dbf_text ) ;
if ( new_mtu < 64 )
return - EINVAL ;
if ( new_mtu > 65535 )
return - EINVAL ;
if ( ( ! qeth_is_supported ( card , IPA_IP_FRAGMENTATION ) ) & &
( ! qeth_mtu_is_valid ( card , new_mtu ) ) )
return - EINVAL ;
dev - > mtu = new_mtu ;
return 0 ;
}
# ifdef CONFIG_QETH_VLAN
static void
qeth_vlan_rx_register ( struct net_device * dev , struct vlan_group * grp )
{
struct qeth_card * card ;
unsigned long flags ;
QETH_DBF_TEXT ( trace , 4 , " vlanreg " ) ;
card = ( struct qeth_card * ) dev - > priv ;
spin_lock_irqsave ( & card - > vlanlock , flags ) ;
card - > vlangrp = grp ;
spin_unlock_irqrestore ( & card - > vlanlock , flags ) ;
}
2007-02-05 23:18:53 +03:00
static void
2005-04-17 02:20:36 +04:00
qeth_free_vlan_buffer ( struct qeth_card * card , struct qeth_qdio_out_buffer * buf ,
unsigned short vid )
{
int i ;
struct sk_buff * skb ;
struct sk_buff_head tmp_list ;
skb_queue_head_init ( & tmp_list ) ;
2006-07-03 11:25:26 +04:00
lockdep_set_class ( & tmp_list . lock , & qdio_out_skb_queue_key ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < QETH_MAX_BUFFER_ELEMENTS ( card ) ; + + i ) {
while ( ( skb = skb_dequeue ( & buf - > skb_list ) ) ) {
if ( vlan_tx_tag_present ( skb ) & &
( vlan_tx_tag_get ( skb ) = = vid ) ) {
atomic_dec ( & skb - > users ) ;
dev_kfree_skb ( skb ) ;
} else
skb_queue_tail ( & tmp_list , skb ) ;
}
}
while ( ( skb = skb_dequeue ( & tmp_list ) ) )
skb_queue_tail ( & buf - > skb_list , skb ) ;
}
static void
qeth_free_vlan_skbs ( struct qeth_card * card , unsigned short vid )
{
int i , j ;
QETH_DBF_TEXT ( trace , 4 , " frvlskbs " ) ;
for ( i = 0 ; i < card - > qdio . no_out_queues ; + + i ) {
for ( j = 0 ; j < QDIO_MAX_BUFFERS_PER_Q ; + + j )
qeth_free_vlan_buffer ( card , & card - > qdio .
out_qs [ i ] - > bufs [ j ] , vid ) ;
}
}
static void
qeth_free_vlan_addresses4 ( struct qeth_card * card , unsigned short vid )
{
struct in_device * in_dev ;
struct in_ifaddr * ifa ;
struct qeth_ipaddr * addr ;
QETH_DBF_TEXT ( trace , 4 , " frvaddr4 " ) ;
2005-11-10 15:49:28 +03:00
2005-04-17 02:20:36 +04:00
rcu_read_lock ( ) ;
2007-03-03 07:44:51 +03:00
in_dev = __in_dev_get_rcu ( vlan_group_get_device ( card - > vlangrp , vid ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! in_dev )
goto out ;
for ( ifa = in_dev - > ifa_list ; ifa ; ifa = ifa - > ifa_next ) {
addr = qeth_get_addr_buffer ( QETH_PROT_IPV4 ) ;
if ( addr ) {
addr - > u . a4 . addr = ifa - > ifa_address ;
addr - > u . a4 . mask = ifa - > ifa_mask ;
addr - > type = QETH_IP_TYPE_NORMAL ;
if ( ! qeth_delete_ip ( card , addr ) )
kfree ( addr ) ;
}
}
out :
rcu_read_unlock ( ) ;
}
static void
qeth_free_vlan_addresses6 ( struct qeth_card * card , unsigned short vid )
{
# ifdef CONFIG_QETH_IPV6
struct inet6_dev * in6_dev ;
struct inet6_ifaddr * ifa ;
struct qeth_ipaddr * addr ;
QETH_DBF_TEXT ( trace , 4 , " frvaddr6 " ) ;
2005-11-10 15:49:28 +03:00
2007-03-03 07:44:51 +03:00
in6_dev = in6_dev_get ( vlan_group_get_device ( card - > vlangrp , vid ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! in6_dev )
return ;
for ( ifa = in6_dev - > addr_list ; ifa ; ifa = ifa - > lst_next ) {
addr = qeth_get_addr_buffer ( QETH_PROT_IPV6 ) ;
if ( addr ) {
memcpy ( & addr - > u . a6 . addr , & ifa - > addr ,
sizeof ( struct in6_addr ) ) ;
addr - > u . a6 . pfxlen = ifa - > prefix_len ;
addr - > type = QETH_IP_TYPE_NORMAL ;
if ( ! qeth_delete_ip ( card , addr ) )
kfree ( addr ) ;
}
}
in6_dev_put ( in6_dev ) ;
# endif /* CONFIG_QETH_IPV6 */
}
2005-11-10 15:49:28 +03:00
static void
qeth_free_vlan_addresses ( struct qeth_card * card , unsigned short vid )
{
if ( card - > options . layer2 | | ! card - > vlangrp )
return ;
qeth_free_vlan_addresses4 ( card , vid ) ;
qeth_free_vlan_addresses6 ( card , vid ) ;
}
2005-11-10 15:49:15 +03:00
static int
qeth_layer2_send_setdelvlan_cb ( struct qeth_card * card ,
struct qeth_reply * reply ,
unsigned long data )
{
struct qeth_ipa_cmd * cmd ;
QETH_DBF_TEXT ( trace , 2 , " L2sdvcb " ) ;
cmd = ( struct qeth_ipa_cmd * ) data ;
if ( cmd - > hdr . return_code ) {
PRINT_ERR ( " Error in processing VLAN %i on %s: 0x%x. "
2006-05-27 05:58:38 +04:00
" Continuing \n " , cmd - > data . setdelvlan . vlan_id ,
2005-11-10 15:49:15 +03:00
QETH_CARD_IFNAME ( card ) , cmd - > hdr . return_code ) ;
QETH_DBF_TEXT_ ( trace , 2 , " L2VL%4x " , cmd - > hdr . command ) ;
QETH_DBF_TEXT_ ( trace , 2 , " L2%s " , CARD_BUS_ID ( card ) ) ;
QETH_DBF_TEXT_ ( trace , 2 , " err%d " , cmd - > hdr . return_code ) ;
}
return 0 ;
}
static int
2005-04-17 02:20:36 +04:00
qeth_layer2_send_setdelvlan ( struct qeth_card * card , __u16 i ,
enum qeth_ipa_cmds ipacmd )
{
struct qeth_ipa_cmd * cmd ;
struct qeth_cmd_buffer * iob ;
QETH_DBF_TEXT_ ( trace , 4 , " L2sdv%x " , ipacmd ) ;
iob = qeth_get_ipacmd_buffer ( card , ipacmd , QETH_PROT_IPV4 ) ;
cmd = ( struct qeth_ipa_cmd * ) ( iob - > data + IPA_PDU_HEADER_SIZE ) ;
cmd - > data . setdelvlan . vlan_id = i ;
2006-05-27 05:58:38 +04:00
return qeth_send_ipa_cmd ( card , iob ,
2005-11-10 15:49:15 +03:00
qeth_layer2_send_setdelvlan_cb , NULL ) ;
2005-04-17 02:20:36 +04:00
}
static void
qeth_layer2_process_vlans ( struct qeth_card * card , int clear )
{
unsigned short i ;
QETH_DBF_TEXT ( trace , 3 , " L2prcvln " ) ;
if ( ! card - > vlangrp )
return ;
for ( i = 0 ; i < VLAN_GROUP_ARRAY_LEN ; i + + ) {
2007-03-03 07:44:51 +03:00
if ( vlan_group_get_device ( card - > vlangrp , i ) = = NULL )
2005-04-17 02:20:36 +04:00
continue ;
if ( clear )
qeth_layer2_send_setdelvlan ( card , i , IPA_CMD_DELVLAN ) ;
else
qeth_layer2_send_setdelvlan ( card , i , IPA_CMD_SETVLAN ) ;
}
}
/*add_vid is layer 2 used only ....*/
static void
qeth_vlan_rx_add_vid ( struct net_device * dev , unsigned short vid )
{
struct qeth_card * card ;
QETH_DBF_TEXT_ ( trace , 4 , " aid:%d " , vid ) ;
card = ( struct qeth_card * ) dev - > priv ;
if ( ! card - > options . layer2 )
return ;
qeth_layer2_send_setdelvlan ( card , vid , IPA_CMD_SETVLAN ) ;
}
/*... kill_vid used for both modes*/
static void
qeth_vlan_rx_kill_vid ( struct net_device * dev , unsigned short vid )
{
struct qeth_card * card ;
unsigned long flags ;
QETH_DBF_TEXT_ ( trace , 4 , " kid:%d " , vid ) ;
card = ( struct qeth_card * ) dev - > priv ;
/* free all skbs for the vlan device */
qeth_free_vlan_skbs ( card , vid ) ;
spin_lock_irqsave ( & card - > vlanlock , flags ) ;
/* unregister IP addresses of vlan device */
2005-11-10 15:49:28 +03:00
qeth_free_vlan_addresses ( card , vid ) ;
2007-03-03 07:44:51 +03:00
vlan_group_set_device ( card - > vlangrp , vid , NULL ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & card - > vlanlock , flags ) ;
if ( card - > options . layer2 )
qeth_layer2_send_setdelvlan ( card , vid , IPA_CMD_DELVLAN ) ;
qeth_set_multicast_list ( card - > dev ) ;
}
# endif
2005-11-10 15:51:25 +03:00
/**
* Examine hardware response to SET_PROMISC_MODE
*/
static int
2006-05-27 05:58:38 +04:00
qeth_setadp_promisc_mode_cb ( struct qeth_card * card ,
2005-11-10 15:51:25 +03:00
struct qeth_reply * reply ,
unsigned long data )
{
struct qeth_ipa_cmd * cmd ;
struct qeth_ipacmd_setadpparms * setparms ;
QETH_DBF_TEXT ( trace , 4 , " prmadpcb " ) ;
cmd = ( struct qeth_ipa_cmd * ) data ;
setparms = & ( cmd - > data . setadapterparms ) ;
2006-05-27 05:58:38 +04:00
2005-11-10 15:51:25 +03:00
qeth_default_setadapterparms_cb ( card , reply , ( unsigned long ) cmd ) ;
2006-05-27 05:58:38 +04:00
if ( cmd - > hdr . return_code ) {
QETH_DBF_TEXT_ ( trace , 4 , " prmrc%2.2x " , cmd - > hdr . return_code ) ;
2005-11-10 15:51:25 +03:00
setparms - > data . mode = SET_PROMISC_MODE_OFF ;
}
card - > info . promisc_mode = setparms - > data . mode ;
return 0 ;
}
/*
* Set promiscuous mode ( on or off ) ( SET_PROMISC_MODE command )
*/
static void
qeth_setadp_promisc_mode ( struct qeth_card * card )
{
enum qeth_ipa_promisc_modes mode ;
struct net_device * dev = card - > dev ;
struct qeth_cmd_buffer * iob ;
struct qeth_ipa_cmd * cmd ;
QETH_DBF_TEXT ( trace , 4 , " setprom " ) ;
if ( ( ( dev - > flags & IFF_PROMISC ) & &
( card - > info . promisc_mode = = SET_PROMISC_MODE_ON ) ) | |
( ! ( dev - > flags & IFF_PROMISC ) & &
( card - > info . promisc_mode = = SET_PROMISC_MODE_OFF ) ) )
return ;
mode = SET_PROMISC_MODE_OFF ;
if ( dev - > flags & IFF_PROMISC )
mode = SET_PROMISC_MODE_ON ;
QETH_DBF_TEXT_ ( trace , 4 , " mode:%x " , mode ) ;
iob = qeth_get_adapter_cmd ( card , IPA_SETADP_SET_PROMISC_MODE ,
sizeof ( struct qeth_ipacmd_setadpparms ) ) ;
cmd = ( struct qeth_ipa_cmd * ) ( iob - > data + IPA_PDU_HEADER_SIZE ) ;
cmd - > data . setadapterparms . data . mode = mode ;
qeth_send_ipa_cmd ( card , iob , qeth_setadp_promisc_mode_cb , NULL ) ;
}
2005-04-17 02:20:36 +04:00
/**
* set multicast address on card
*/
static void
qeth_set_multicast_list ( struct net_device * dev )
{
struct qeth_card * card = ( struct qeth_card * ) dev - > priv ;
2005-09-30 12:19:19 +04:00
if ( card - > info . type = = QETH_CARD_TYPE_OSN )
return ;
2006-05-27 05:58:38 +04:00
2006-02-07 19:04:38 +03:00
QETH_DBF_TEXT ( trace , 3 , " setmulti " ) ;
2005-04-17 02:20:36 +04:00
qeth_delete_mc_addresses ( card ) ;
2005-11-10 15:49:28 +03:00
if ( card - > options . layer2 ) {
qeth_layer2_add_multicast ( card ) ;
goto out ;
}
2005-04-17 02:20:36 +04:00
qeth_add_multicast_ipv4 ( card ) ;
# ifdef CONFIG_QETH_IPV6
qeth_add_multicast_ipv6 ( card ) ;
# endif
2005-11-10 15:49:28 +03:00
out :
2007-01-08 19:30:11 +03:00
qeth_set_ip_addr_list ( card ) ;
2005-11-10 15:51:25 +03:00
if ( ! qeth_adp_supported ( card , IPA_SETADP_SET_PROMISC_MODE ) )
return ;
2007-01-08 19:30:11 +03:00
qeth_setadp_promisc_mode ( card ) ;
2005-04-17 02:20:36 +04:00
}
static int
qeth_neigh_setup ( struct net_device * dev , struct neigh_parms * np )
{
return 0 ;
}
static void
qeth_get_mac_for_ipm ( __u32 ipm , char * mac , struct net_device * dev )
{
if ( dev - > type = = ARPHRD_IEEE802_TR )
ip_tr_mc_map ( ipm , mac ) ;
else
ip_eth_mc_map ( ipm , mac ) ;
}
static struct qeth_ipaddr *
qeth_get_addr_buffer ( enum qeth_prot_versions prot )
{
struct qeth_ipaddr * addr ;
2006-03-24 14:15:31 +03:00
addr = kzalloc ( sizeof ( struct qeth_ipaddr ) , GFP_ATOMIC ) ;
2005-04-17 02:20:36 +04:00
if ( addr = = NULL ) {
PRINT_WARN ( " Not enough memory to add address \n " ) ;
return NULL ;
}
addr - > type = QETH_IP_TYPE_NORMAL ;
addr - > proto = prot ;
return addr ;
}
2005-09-30 12:19:19 +04:00
int
qeth_osn_assist ( struct net_device * dev ,
void * data ,
int data_len )
{
struct qeth_cmd_buffer * iob ;
struct qeth_card * card ;
int rc ;
2006-05-27 05:58:38 +04:00
2005-09-30 12:19:19 +04:00
QETH_DBF_TEXT ( trace , 2 , " osnsdmc " ) ;
if ( ! dev )
return - ENODEV ;
card = ( struct qeth_card * ) dev - > priv ;
if ( ! card )
return - ENODEV ;
if ( ( card - > state ! = CARD_STATE_UP ) & &
( card - > state ! = CARD_STATE_SOFTSETUP ) )
return - ENODEV ;
iob = qeth_wait_for_buffer ( & card - > write ) ;
memcpy ( iob - > data + IPA_PDU_HEADER_SIZE , data , data_len ) ;
rc = qeth_osn_send_ipa_cmd ( card , iob , data_len ) ;
return rc ;
}
static struct net_device *
qeth_netdev_by_devno ( unsigned char * read_dev_no )
{
struct qeth_card * card ;
struct net_device * ndev ;
unsigned char * readno ;
__u16 temp_dev_no , card_dev_no ;
char * endp ;
unsigned long flags ;
ndev = NULL ;
memcpy ( & temp_dev_no , read_dev_no , 2 ) ;
read_lock_irqsave ( & qeth_card_list . rwlock , flags ) ;
list_for_each_entry ( card , & qeth_card_list . list , list ) {
readno = CARD_RDEV_ID ( card ) ;
readno + = ( strlen ( readno ) - 4 ) ;
card_dev_no = simple_strtoul ( readno , & endp , 16 ) ;
if ( card_dev_no = = temp_dev_no ) {
ndev = card - > dev ;
break ;
}
}
read_unlock_irqrestore ( & qeth_card_list . rwlock , flags ) ;
return ndev ;
}
int
qeth_osn_register ( unsigned char * read_dev_no ,
struct net_device * * dev ,
int ( * assist_cb ) ( struct net_device * , void * ) ,
int ( * data_cb ) ( struct sk_buff * ) )
{
struct qeth_card * card ;
QETH_DBF_TEXT ( trace , 2 , " osnreg " ) ;
* dev = qeth_netdev_by_devno ( read_dev_no ) ;
if ( * dev = = NULL )
return - ENODEV ;
card = ( struct qeth_card * ) ( * dev ) - > priv ;
if ( ! card )
return - ENODEV ;
if ( ( assist_cb = = NULL ) | | ( data_cb = = NULL ) )
return - EINVAL ;
card - > osn_info . assist_cb = assist_cb ;
card - > osn_info . data_cb = data_cb ;
return 0 ;
}
void
qeth_osn_deregister ( struct net_device * dev )
{
struct qeth_card * card ;
QETH_DBF_TEXT ( trace , 2 , " osndereg " ) ;
if ( ! dev )
return ;
card = ( struct qeth_card * ) dev - > priv ;
if ( ! card )
return ;
card - > osn_info . assist_cb = NULL ;
card - > osn_info . data_cb = NULL ;
return ;
}
2006-05-27 05:58:38 +04:00
2005-04-17 02:20:36 +04:00
static void
qeth_delete_mc_addresses ( struct qeth_card * card )
{
struct qeth_ipaddr * iptodo ;
unsigned long flags ;
QETH_DBF_TEXT ( trace , 4 , " delmc " ) ;
iptodo = qeth_get_addr_buffer ( QETH_PROT_IPV4 ) ;
if ( ! iptodo ) {
QETH_DBF_TEXT ( trace , 2 , " dmcnomem " ) ;
return ;
}
iptodo - > type = QETH_IP_TYPE_DEL_ALL_MC ;
spin_lock_irqsave ( & card - > ip_lock , flags ) ;
if ( ! __qeth_insert_ip_todo ( card , iptodo , 0 ) )
kfree ( iptodo ) ;
spin_unlock_irqrestore ( & card - > ip_lock , flags ) ;
}
2007-02-05 23:18:53 +03:00
static void
2005-04-17 02:20:36 +04:00
qeth_add_mc ( struct qeth_card * card , struct in_device * in4_dev )
{
struct qeth_ipaddr * ipm ;
struct ip_mc_list * im4 ;
char buf [ MAX_ADDR_LEN ] ;
QETH_DBF_TEXT ( trace , 4 , " addmc " ) ;
for ( im4 = in4_dev - > mc_list ; im4 ; im4 = im4 - > next ) {
qeth_get_mac_for_ipm ( im4 - > multiaddr , buf , in4_dev - > dev ) ;
ipm = qeth_get_addr_buffer ( QETH_PROT_IPV4 ) ;
if ( ! ipm )
continue ;
ipm - > u . a4 . addr = im4 - > multiaddr ;
memcpy ( ipm - > mac , buf , OSA_ADDR_LEN ) ;
ipm - > is_multicast = 1 ;
if ( ! qeth_add_ip ( card , ipm ) )
kfree ( ipm ) ;
}
}
static inline void
qeth_add_vlan_mc ( struct qeth_card * card )
{
# ifdef CONFIG_QETH_VLAN
struct in_device * in_dev ;
struct vlan_group * vg ;
int i ;
QETH_DBF_TEXT ( trace , 4 , " addmcvl " ) ;
if ( ( ( card - > options . layer2 = = 0 ) & &
( ! qeth_is_supported ( card , IPA_FULL_VLAN ) ) ) | |
( card - > vlangrp = = NULL ) )
return ;
vg = card - > vlangrp ;
for ( i = 0 ; i < VLAN_GROUP_ARRAY_LEN ; i + + ) {
2007-03-03 07:44:51 +03:00
struct net_device * netdev = vlan_group_get_device ( vg , i ) ;
if ( netdev = = NULL | |
! ( netdev - > flags & IFF_UP ) )
2005-04-17 02:20:36 +04:00
continue ;
2007-03-03 07:44:51 +03:00
in_dev = in_dev_get ( netdev ) ;
2005-04-17 02:20:36 +04:00
if ( ! in_dev )
continue ;
read_lock ( & in_dev - > mc_list_lock ) ;
qeth_add_mc ( card , in_dev ) ;
read_unlock ( & in_dev - > mc_list_lock ) ;
in_dev_put ( in_dev ) ;
}
# endif
}
static void
qeth_add_multicast_ipv4 ( struct qeth_card * card )
{
struct in_device * in4_dev ;
QETH_DBF_TEXT ( trace , 4 , " chkmcv4 " ) ;
in4_dev = in_dev_get ( card - > dev ) ;
if ( in4_dev = = NULL )
return ;
read_lock ( & in4_dev - > mc_list_lock ) ;
qeth_add_mc ( card , in4_dev ) ;
qeth_add_vlan_mc ( card ) ;
read_unlock ( & in4_dev - > mc_list_lock ) ;
in_dev_put ( in4_dev ) ;
}
2005-11-10 15:49:28 +03:00
static void
qeth_layer2_add_multicast ( struct qeth_card * card )
{
struct qeth_ipaddr * ipm ;
struct dev_mc_list * dm ;
QETH_DBF_TEXT ( trace , 4 , " L2addmc " ) ;
for ( dm = card - > dev - > mc_list ; dm ; dm = dm - > next ) {
ipm = qeth_get_addr_buffer ( QETH_PROT_IPV4 ) ;
if ( ! ipm )
continue ;
memcpy ( ipm - > mac , dm - > dmi_addr , MAX_ADDR_LEN ) ;
ipm - > is_multicast = 1 ;
if ( ! qeth_add_ip ( card , ipm ) )
kfree ( ipm ) ;
}
}
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_QETH_IPV6
2007-02-05 23:18:53 +03:00
static void
2005-04-17 02:20:36 +04:00
qeth_add_mc6 ( struct qeth_card * card , struct inet6_dev * in6_dev )
{
struct qeth_ipaddr * ipm ;
struct ifmcaddr6 * im6 ;
char buf [ MAX_ADDR_LEN ] ;
QETH_DBF_TEXT ( trace , 4 , " addmc6 " ) ;
for ( im6 = in6_dev - > mc_list ; im6 ! = NULL ; im6 = im6 - > next ) {
ndisc_mc_map ( & im6 - > mca_addr , buf , in6_dev - > dev , 0 ) ;
ipm = qeth_get_addr_buffer ( QETH_PROT_IPV6 ) ;
if ( ! ipm )
continue ;
ipm - > is_multicast = 1 ;
memcpy ( ipm - > mac , buf , OSA_ADDR_LEN ) ;
memcpy ( & ipm - > u . a6 . addr , & im6 - > mca_addr . s6_addr ,
sizeof ( struct in6_addr ) ) ;
if ( ! qeth_add_ip ( card , ipm ) )
kfree ( ipm ) ;
}
}
static inline void
qeth_add_vlan_mc6 ( struct qeth_card * card )
{
# ifdef CONFIG_QETH_VLAN
struct inet6_dev * in_dev ;
struct vlan_group * vg ;
int i ;
QETH_DBF_TEXT ( trace , 4 , " admc6vl " ) ;
if ( ( ( card - > options . layer2 = = 0 ) & &
( ! qeth_is_supported ( card , IPA_FULL_VLAN ) ) ) | |
( card - > vlangrp = = NULL ) )
return ;
vg = card - > vlangrp ;
for ( i = 0 ; i < VLAN_GROUP_ARRAY_LEN ; i + + ) {
2007-03-03 07:44:51 +03:00
struct net_device * netdev = vlan_group_get_device ( vg , i ) ;
if ( netdev = = NULL | |
! ( netdev - > flags & IFF_UP ) )
2005-04-17 02:20:36 +04:00
continue ;
2007-03-03 07:44:51 +03:00
in_dev = in6_dev_get ( netdev ) ;
2005-04-17 02:20:36 +04:00
if ( ! in_dev )
continue ;
read_lock ( & in_dev - > lock ) ;
qeth_add_mc6 ( card , in_dev ) ;
read_unlock ( & in_dev - > lock ) ;
in6_dev_put ( in_dev ) ;
}
# endif /* CONFIG_QETH_VLAN */
}
static void
qeth_add_multicast_ipv6 ( struct qeth_card * card )
{
struct inet6_dev * in6_dev ;
QETH_DBF_TEXT ( trace , 4 , " chkmcv6 " ) ;
2006-05-27 05:58:38 +04:00
if ( ! qeth_is_supported ( card , IPA_IPV6 ) )
2005-04-17 02:20:36 +04:00
return ;
in6_dev = in6_dev_get ( card - > dev ) ;
if ( in6_dev = = NULL )
return ;
read_lock ( & in6_dev - > lock ) ;
qeth_add_mc6 ( card , in6_dev ) ;
qeth_add_vlan_mc6 ( card ) ;
read_unlock ( & in6_dev - > lock ) ;
in6_dev_put ( in6_dev ) ;
}
# endif /* CONFIG_QETH_IPV6 */
static int
qeth_layer2_send_setdelmac ( struct qeth_card * card , __u8 * mac ,
enum qeth_ipa_cmds ipacmd ,
int ( * reply_cb ) ( struct qeth_card * ,
struct qeth_reply * ,
unsigned long ) )
{
struct qeth_ipa_cmd * cmd ;
struct qeth_cmd_buffer * iob ;
QETH_DBF_TEXT ( trace , 2 , " L2sdmac " ) ;
iob = qeth_get_ipacmd_buffer ( card , ipacmd , QETH_PROT_IPV4 ) ;
cmd = ( struct qeth_ipa_cmd * ) ( iob - > data + IPA_PDU_HEADER_SIZE ) ;
cmd - > data . setdelmac . mac_length = OSA_ADDR_LEN ;
memcpy ( & cmd - > data . setdelmac . mac , mac , OSA_ADDR_LEN ) ;
return qeth_send_ipa_cmd ( card , iob , reply_cb , NULL ) ;
}
static int
qeth_layer2_send_setgroupmac_cb ( struct qeth_card * card ,
struct qeth_reply * reply ,
unsigned long data )
{
struct qeth_ipa_cmd * cmd ;
__u8 * mac ;
QETH_DBF_TEXT ( trace , 2 , " L2Sgmacb " ) ;
cmd = ( struct qeth_ipa_cmd * ) data ;
mac = & cmd - > data . setdelmac . mac [ 0 ] ;
/* MAC already registered, needed in couple/uncouple case */
if ( cmd - > hdr . return_code = = 0x2005 ) {
PRINT_WARN ( " Group MAC %02x:%02x:%02x:%02x:%02x:%02x " \
" already existing on %s \n " ,
mac [ 0 ] , mac [ 1 ] , mac [ 2 ] , mac [ 3 ] , mac [ 4 ] , mac [ 5 ] ,
QETH_CARD_IFNAME ( card ) ) ;
cmd - > hdr . return_code = 0 ;
}
if ( cmd - > hdr . return_code )
PRINT_ERR ( " Could not set group MAC " \
" %02x:%02x:%02x:%02x:%02x:%02x on %s: %x \n " ,
mac [ 0 ] , mac [ 1 ] , mac [ 2 ] , mac [ 3 ] , mac [ 4 ] , mac [ 5 ] ,
QETH_CARD_IFNAME ( card ) , cmd - > hdr . return_code ) ;
return 0 ;
}
static int
qeth_layer2_send_setgroupmac ( struct qeth_card * card , __u8 * mac )
{
QETH_DBF_TEXT ( trace , 2 , " L2Sgmac " ) ;
return qeth_layer2_send_setdelmac ( card , mac , IPA_CMD_SETGMAC ,
qeth_layer2_send_setgroupmac_cb ) ;
}
static int
qeth_layer2_send_delgroupmac_cb ( struct qeth_card * card ,
struct qeth_reply * reply ,
unsigned long data )
{
struct qeth_ipa_cmd * cmd ;
__u8 * mac ;
QETH_DBF_TEXT ( trace , 2 , " L2Dgmacb " ) ;
cmd = ( struct qeth_ipa_cmd * ) data ;
mac = & cmd - > data . setdelmac . mac [ 0 ] ;
if ( cmd - > hdr . return_code )
PRINT_ERR ( " Could not delete group MAC " \
" %02x:%02x:%02x:%02x:%02x:%02x on %s: %x \n " ,
mac [ 0 ] , mac [ 1 ] , mac [ 2 ] , mac [ 3 ] , mac [ 4 ] , mac [ 5 ] ,
QETH_CARD_IFNAME ( card ) , cmd - > hdr . return_code ) ;
return 0 ;
}
static int
qeth_layer2_send_delgroupmac ( struct qeth_card * card , __u8 * mac )
{
QETH_DBF_TEXT ( trace , 2 , " L2Dgmac " ) ;
return qeth_layer2_send_setdelmac ( card , mac , IPA_CMD_DELGMAC ,
qeth_layer2_send_delgroupmac_cb ) ;
}
static int
qeth_layer2_send_setmac_cb ( struct qeth_card * card ,
struct qeth_reply * reply ,
unsigned long data )
{
struct qeth_ipa_cmd * cmd ;
QETH_DBF_TEXT ( trace , 2 , " L2Smaccb " ) ;
cmd = ( struct qeth_ipa_cmd * ) data ;
if ( cmd - > hdr . return_code ) {
QETH_DBF_TEXT_ ( trace , 2 , " L2er%x " , cmd - > hdr . return_code ) ;
2005-11-10 15:51:17 +03:00
card - > info . mac_bits & = ~ QETH_LAYER2_MAC_REGISTERED ;
2005-04-17 02:20:36 +04:00
cmd - > hdr . return_code = - EIO ;
} else {
2005-11-10 15:51:17 +03:00
card - > info . mac_bits | = QETH_LAYER2_MAC_REGISTERED ;
2005-04-17 02:20:36 +04:00
memcpy ( card - > dev - > dev_addr , cmd - > data . setdelmac . mac ,
OSA_ADDR_LEN ) ;
PRINT_INFO ( " MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x "
" successfully registered on device %s \n " ,
card - > dev - > dev_addr [ 0 ] , card - > dev - > dev_addr [ 1 ] ,
card - > dev - > dev_addr [ 2 ] , card - > dev - > dev_addr [ 3 ] ,
card - > dev - > dev_addr [ 4 ] , card - > dev - > dev_addr [ 5 ] ,
card - > dev - > name ) ;
}
return 0 ;
}
static int
qeth_layer2_send_setmac ( struct qeth_card * card , __u8 * mac )
{
QETH_DBF_TEXT ( trace , 2 , " L2Setmac " ) ;
return qeth_layer2_send_setdelmac ( card , mac , IPA_CMD_SETVMAC ,
qeth_layer2_send_setmac_cb ) ;
}
static int
qeth_layer2_send_delmac_cb ( struct qeth_card * card ,
struct qeth_reply * reply ,
unsigned long data )
{
struct qeth_ipa_cmd * cmd ;
QETH_DBF_TEXT ( trace , 2 , " L2Dmaccb " ) ;
cmd = ( struct qeth_ipa_cmd * ) data ;
if ( cmd - > hdr . return_code ) {
QETH_DBF_TEXT_ ( trace , 2 , " err%d " , cmd - > hdr . return_code ) ;
cmd - > hdr . return_code = - EIO ;
return 0 ;
}
2005-11-10 15:51:17 +03:00
card - > info . mac_bits & = ~ QETH_LAYER2_MAC_REGISTERED ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static int
qeth_layer2_send_delmac ( struct qeth_card * card , __u8 * mac )
{
QETH_DBF_TEXT ( trace , 2 , " L2Delmac " ) ;
2005-11-10 15:51:17 +03:00
if ( ! ( card - > info . mac_bits & QETH_LAYER2_MAC_REGISTERED ) )
2005-04-17 02:20:36 +04:00
return 0 ;
return qeth_layer2_send_setdelmac ( card , mac , IPA_CMD_DELVMAC ,
qeth_layer2_send_delmac_cb ) ;
}
static int
qeth_layer2_set_mac_address ( struct net_device * dev , void * p )
{
struct sockaddr * addr = p ;
struct qeth_card * card ;
int rc = 0 ;
QETH_DBF_TEXT ( trace , 3 , " setmac " ) ;
if ( qeth_verify_dev ( dev ) ! = QETH_REAL_CARD ) {
QETH_DBF_TEXT ( trace , 3 , " setmcINV " ) ;
return - EOPNOTSUPP ;
}
card = ( struct qeth_card * ) dev - > priv ;
if ( ! card - > options . layer2 ) {
2005-11-10 15:51:17 +03:00
PRINT_WARN ( " Setting MAC address on %s is not supported "
2005-04-17 02:20:36 +04:00
" in Layer 3 mode. \n " , dev - > name ) ;
QETH_DBF_TEXT ( trace , 3 , " setmcLY3 " ) ;
return - EOPNOTSUPP ;
}
2005-09-30 12:19:19 +04:00
if ( card - > info . type = = QETH_CARD_TYPE_OSN ) {
PRINT_WARN ( " Setting MAC address on %s is not supported. \n " ,
dev - > name ) ;
QETH_DBF_TEXT ( trace , 3 , " setmcOSN " ) ;
return - EOPNOTSUPP ;
}
2005-04-17 02:20:36 +04:00
QETH_DBF_TEXT_ ( trace , 3 , " %s " , CARD_BUS_ID ( card ) ) ;
QETH_DBF_HEX ( trace , 3 , addr - > sa_data , OSA_ADDR_LEN ) ;
rc = qeth_layer2_send_delmac ( card , & card - > dev - > dev_addr [ 0 ] ) ;
if ( ! rc )
rc = qeth_layer2_send_setmac ( card , addr - > sa_data ) ;
return rc ;
}
static void
qeth_fill_ipacmd_header ( struct qeth_card * card , struct qeth_ipa_cmd * cmd ,
__u8 command , enum qeth_prot_versions prot )
{
memset ( cmd , 0 , sizeof ( struct qeth_ipa_cmd ) ) ;
cmd - > hdr . command = command ;
cmd - > hdr . initiator = IPA_CMD_INITIATOR_HOST ;
cmd - > hdr . seqno = card - > seqno . ipa ;
cmd - > hdr . adapter_type = qeth_get_ipa_adp_type ( card - > info . link_type ) ;
cmd - > hdr . rel_adapter_no = ( __u8 ) card - > info . portno ;
if ( card - > options . layer2 )
cmd - > hdr . prim_version_no = 2 ;
else
cmd - > hdr . prim_version_no = 1 ;
cmd - > hdr . param_count = 1 ;
cmd - > hdr . prot_version = prot ;
cmd - > hdr . ipa_supported = 0 ;
cmd - > hdr . ipa_enabled = 0 ;
}
static struct qeth_cmd_buffer *
qeth_get_ipacmd_buffer ( struct qeth_card * card , enum qeth_ipa_cmds ipacmd ,
enum qeth_prot_versions prot )
{
struct qeth_cmd_buffer * iob ;
struct qeth_ipa_cmd * cmd ;
iob = qeth_wait_for_buffer ( & card - > write ) ;
cmd = ( struct qeth_ipa_cmd * ) ( iob - > data + IPA_PDU_HEADER_SIZE ) ;
qeth_fill_ipacmd_header ( card , cmd , ipacmd , prot ) ;
return iob ;
}
static int
qeth_send_setdelmc ( struct qeth_card * card , struct qeth_ipaddr * addr , int ipacmd )
{
int rc ;
struct qeth_cmd_buffer * iob ;
struct qeth_ipa_cmd * cmd ;
QETH_DBF_TEXT ( trace , 4 , " setdelmc " ) ;
iob = qeth_get_ipacmd_buffer ( card , ipacmd , addr - > proto ) ;
cmd = ( struct qeth_ipa_cmd * ) ( iob - > data + IPA_PDU_HEADER_SIZE ) ;
memcpy ( & cmd - > data . setdelipm . mac , addr - > mac , OSA_ADDR_LEN ) ;
if ( addr - > proto = = QETH_PROT_IPV6 )
memcpy ( cmd - > data . setdelipm . ip6 , & addr - > u . a6 . addr ,
sizeof ( struct in6_addr ) ) ;
else
memcpy ( & cmd - > data . setdelipm . ip4 , & addr - > u . a4 . addr , 4 ) ;
rc = qeth_send_ipa_cmd ( card , iob , NULL , NULL ) ;
return rc ;
}
2007-02-05 23:18:53 +03:00
static void
2005-04-17 02:20:36 +04:00
qeth_fill_netmask ( u8 * netmask , unsigned int len )
{
int i , j ;
for ( i = 0 ; i < 16 ; i + + ) {
j = ( len ) - ( i * 8 ) ;
if ( j > = 8 )
netmask [ i ] = 0xff ;
else if ( j > 0 )
netmask [ i ] = ( u8 ) ( 0xFF00 > > j ) ;
else
netmask [ i ] = 0 ;
}
}
static int
qeth_send_setdelip ( struct qeth_card * card , struct qeth_ipaddr * addr ,
int ipacmd , unsigned int flags )
{
int rc ;
struct qeth_cmd_buffer * iob ;
struct qeth_ipa_cmd * cmd ;
__u8 netmask [ 16 ] ;
QETH_DBF_TEXT ( trace , 4 , " setdelip " ) ;
QETH_DBF_TEXT_ ( trace , 4 , " flags%02X " , flags ) ;
iob = qeth_get_ipacmd_buffer ( card , ipacmd , addr - > proto ) ;
cmd = ( struct qeth_ipa_cmd * ) ( iob - > data + IPA_PDU_HEADER_SIZE ) ;
if ( addr - > proto = = QETH_PROT_IPV6 ) {
memcpy ( cmd - > data . setdelip6 . ip_addr , & addr - > u . a6 . addr ,
sizeof ( struct in6_addr ) ) ;
qeth_fill_netmask ( netmask , addr - > u . a6 . pfxlen ) ;
memcpy ( cmd - > data . setdelip6 . mask , netmask ,
sizeof ( struct in6_addr ) ) ;
cmd - > data . setdelip6 . flags = flags ;
} else {
memcpy ( cmd - > data . setdelip4 . ip_addr , & addr - > u . a4 . addr , 4 ) ;
memcpy ( cmd - > data . setdelip4 . mask , & addr - > u . a4 . mask , 4 ) ;
cmd - > data . setdelip4 . flags = flags ;
}
rc = qeth_send_ipa_cmd ( card , iob , NULL , NULL ) ;
return rc ;
}
static int
qeth_layer2_register_addr_entry ( struct qeth_card * card ,
struct qeth_ipaddr * addr )
{
if ( ! addr - > is_multicast )
return 0 ;
QETH_DBF_TEXT ( trace , 2 , " setgmac " ) ;
QETH_DBF_HEX ( trace , 3 , & addr - > mac [ 0 ] , OSA_ADDR_LEN ) ;
return qeth_layer2_send_setgroupmac ( card , & addr - > mac [ 0 ] ) ;
}
static int
qeth_layer2_deregister_addr_entry ( struct qeth_card * card ,
struct qeth_ipaddr * addr )
{
if ( ! addr - > is_multicast )
return 0 ;
QETH_DBF_TEXT ( trace , 2 , " delgmac " ) ;
QETH_DBF_HEX ( trace , 3 , & addr - > mac [ 0 ] , OSA_ADDR_LEN ) ;
return qeth_layer2_send_delgroupmac ( card , & addr - > mac [ 0 ] ) ;
}
static int
qeth_layer3_register_addr_entry ( struct qeth_card * card ,
struct qeth_ipaddr * addr )
{
char buf [ 50 ] ;
int rc ;
int cnt = 3 ;
if ( addr - > proto = = QETH_PROT_IPV4 ) {
QETH_DBF_TEXT ( trace , 2 , " setaddr4 " ) ;
QETH_DBF_HEX ( trace , 3 , & addr - > u . a4 . addr , sizeof ( int ) ) ;
} else if ( addr - > proto = = QETH_PROT_IPV6 ) {
QETH_DBF_TEXT ( trace , 2 , " setaddr6 " ) ;
QETH_DBF_HEX ( trace , 3 , & addr - > u . a6 . addr , 8 ) ;
QETH_DBF_HEX ( trace , 3 , ( ( char * ) & addr - > u . a6 . addr ) + 8 , 8 ) ;
} else {
QETH_DBF_TEXT ( trace , 2 , " setaddr? " ) ;
QETH_DBF_HEX ( trace , 3 , addr , sizeof ( struct qeth_ipaddr ) ) ;
}
do {
if ( addr - > is_multicast )
rc = qeth_send_setdelmc ( card , addr , IPA_CMD_SETIPM ) ;
else
rc = qeth_send_setdelip ( card , addr , IPA_CMD_SETIP ,
addr - > set_flags ) ;
if ( rc )
QETH_DBF_TEXT ( trace , 2 , " failed " ) ;
} while ( ( - - cnt > 0 ) & & rc ) ;
if ( rc ) {
QETH_DBF_TEXT ( trace , 2 , " FAILED " ) ;
qeth_ipaddr_to_string ( addr - > proto , ( u8 * ) & addr - > u , buf ) ;
PRINT_WARN ( " Could not register IP address %s (rc=0x%x/%d) \n " ,
buf , rc , rc ) ;
}
return rc ;
}
static int
qeth_layer3_deregister_addr_entry ( struct qeth_card * card ,
struct qeth_ipaddr * addr )
{
//char buf[50];
int rc ;
if ( addr - > proto = = QETH_PROT_IPV4 ) {
QETH_DBF_TEXT ( trace , 2 , " deladdr4 " ) ;
QETH_DBF_HEX ( trace , 3 , & addr - > u . a4 . addr , sizeof ( int ) ) ;
} else if ( addr - > proto = = QETH_PROT_IPV6 ) {
QETH_DBF_TEXT ( trace , 2 , " deladdr6 " ) ;
QETH_DBF_HEX ( trace , 3 , & addr - > u . a6 . addr , 8 ) ;
QETH_DBF_HEX ( trace , 3 , ( ( char * ) & addr - > u . a6 . addr ) + 8 , 8 ) ;
} else {
QETH_DBF_TEXT ( trace , 2 , " deladdr? " ) ;
QETH_DBF_HEX ( trace , 3 , addr , sizeof ( struct qeth_ipaddr ) ) ;
}
if ( addr - > is_multicast )
rc = qeth_send_setdelmc ( card , addr , IPA_CMD_DELIPM ) ;
else
rc = qeth_send_setdelip ( card , addr , IPA_CMD_DELIP ,
addr - > del_flags ) ;
if ( rc ) {
QETH_DBF_TEXT ( trace , 2 , " failed " ) ;
/* TODO: re-activate this warning as soon as we have a
* clean mirco code
qeth_ipaddr_to_string ( addr - > proto , ( u8 * ) & addr - > u , buf ) ;
PRINT_WARN ( " Could not deregister IP address %s (rc=%x) \n " ,
buf , rc ) ;
*/
}
return rc ;
}
static int
qeth_register_addr_entry ( struct qeth_card * card , struct qeth_ipaddr * addr )
{
if ( card - > options . layer2 )
return qeth_layer2_register_addr_entry ( card , addr ) ;
return qeth_layer3_register_addr_entry ( card , addr ) ;
}
static int
qeth_deregister_addr_entry ( struct qeth_card * card , struct qeth_ipaddr * addr )
{
if ( card - > options . layer2 )
return qeth_layer2_deregister_addr_entry ( card , addr ) ;
return qeth_layer3_deregister_addr_entry ( card , addr ) ;
}
static u32
qeth_ethtool_get_tx_csum ( struct net_device * dev )
{
/* We may need to say that we support tx csum offload if
* we do EDDP or TSO . There are discussions going on to
* enforce rules in the stack and in ethtool that make
* SG and TSO depend on HW_CSUM . At the moment there are
* no such rules . . . .
* If we say yes here , we have to checksum outbound packets
* any time . */
return 0 ;
}
static int
qeth_ethtool_set_tx_csum ( struct net_device * dev , u32 data )
{
return - EINVAL ;
}
static u32
qeth_ethtool_get_rx_csum ( struct net_device * dev )
{
struct qeth_card * card = ( struct qeth_card * ) dev - > priv ;
return ( card - > options . checksum_type = = HW_CHECKSUMMING ) ;
}
static int
qeth_ethtool_set_rx_csum ( struct net_device * dev , u32 data )
{
struct qeth_card * card = ( struct qeth_card * ) dev - > priv ;
if ( ( card - > state ! = CARD_STATE_DOWN ) & &
( card - > state ! = CARD_STATE_RECOVER ) )
return - EPERM ;
if ( data )
card - > options . checksum_type = HW_CHECKSUMMING ;
else
card - > options . checksum_type = SW_CHECKSUMMING ;
return 0 ;
}
static u32
qeth_ethtool_get_sg ( struct net_device * dev )
{
struct qeth_card * card = ( struct qeth_card * ) dev - > priv ;
return ( ( card - > options . large_send ! = QETH_LARGE_SEND_NO ) & &
( dev - > features & NETIF_F_SG ) ) ;
}
static int
qeth_ethtool_set_sg ( struct net_device * dev , u32 data )
{
struct qeth_card * card = ( struct qeth_card * ) dev - > priv ;
if ( data ) {
if ( card - > options . large_send ! = QETH_LARGE_SEND_NO )
dev - > features | = NETIF_F_SG ;
else {
dev - > features & = ~ NETIF_F_SG ;
return - EINVAL ;
}
} else
dev - > features & = ~ NETIF_F_SG ;
return 0 ;
}
static u32
qeth_ethtool_get_tso ( struct net_device * dev )
{
struct qeth_card * card = ( struct qeth_card * ) dev - > priv ;
return ( ( card - > options . large_send ! = QETH_LARGE_SEND_NO ) & &
( dev - > features & NETIF_F_TSO ) ) ;
}
static int
qeth_ethtool_set_tso ( struct net_device * dev , u32 data )
{
struct qeth_card * card = ( struct qeth_card * ) dev - > priv ;
if ( data ) {
if ( card - > options . large_send ! = QETH_LARGE_SEND_NO )
dev - > features | = NETIF_F_TSO ;
else {
dev - > features & = ~ NETIF_F_TSO ;
return - EINVAL ;
}
} else
dev - > features & = ~ NETIF_F_TSO ;
return 0 ;
}
static struct ethtool_ops qeth_ethtool_ops = {
. get_tx_csum = qeth_ethtool_get_tx_csum ,
. set_tx_csum = qeth_ethtool_set_tx_csum ,
. get_rx_csum = qeth_ethtool_get_rx_csum ,
. set_rx_csum = qeth_ethtool_set_rx_csum ,
. get_sg = qeth_ethtool_get_sg ,
. set_sg = qeth_ethtool_set_sg ,
. get_tso = qeth_ethtool_get_tso ,
. set_tso = qeth_ethtool_set_tso ,
} ;
2007-01-08 19:29:58 +03:00
static int
qeth_hard_header_parse ( struct sk_buff * skb , unsigned char * haddr )
{
struct qeth_card * card ;
struct ethhdr * eth ;
card = qeth_get_card_from_dev ( skb - > dev ) ;
if ( card - > options . layer2 )
goto haveheader ;
# ifdef CONFIG_QETH_IPV6
/* cause of the manipulated arp constructor and the ARP
flag for OSAE devices we have some nasty exceptions */
if ( card - > info . type = = QETH_CARD_TYPE_OSAE ) {
if ( ! card - > options . fake_ll ) {
if ( ( skb - > pkt_type = = PACKET_OUTGOING ) & &
( skb - > protocol = = ETH_P_IPV6 ) )
goto haveheader ;
else
return 0 ;
} else {
if ( ( skb - > pkt_type = = PACKET_OUTGOING ) & &
( skb - > protocol = = ETH_P_IP ) )
return 0 ;
else
goto haveheader ;
}
}
# endif
if ( ! card - > options . fake_ll )
return 0 ;
haveheader :
eth = eth_hdr ( skb ) ;
memcpy ( haddr , eth - > h_source , ETH_ALEN ) ;
return ETH_ALEN ;
}
2005-04-17 02:20:36 +04:00
static int
qeth_netdev_init ( struct net_device * dev )
{
struct qeth_card * card ;
card = ( struct qeth_card * ) dev - > priv ;
QETH_DBF_TEXT ( trace , 3 , " initdev " ) ;
dev - > tx_timeout = & qeth_tx_timeout ;
dev - > watchdog_timeo = QETH_TX_TIMEOUT ;
dev - > open = qeth_open ;
dev - > stop = qeth_stop ;
dev - > hard_start_xmit = qeth_hard_start_xmit ;
dev - > do_ioctl = qeth_do_ioctl ;
dev - > get_stats = qeth_get_stats ;
dev - > change_mtu = qeth_change_mtu ;
dev - > neigh_setup = qeth_neigh_setup ;
dev - > set_multicast_list = qeth_set_multicast_list ;
# ifdef CONFIG_QETH_VLAN
dev - > vlan_rx_register = qeth_vlan_rx_register ;
dev - > vlan_rx_kill_vid = qeth_vlan_rx_kill_vid ;
dev - > vlan_rx_add_vid = qeth_vlan_rx_add_vid ;
# endif
if ( qeth_get_netdev_flags ( card ) & IFF_NOARP ) {
dev - > rebuild_header = NULL ;
dev - > hard_header = NULL ;
dev - > header_cache_update = NULL ;
dev - > hard_header_cache = NULL ;
}
# ifdef CONFIG_QETH_IPV6
/*IPv6 address autoconfiguration stuff*/
if ( ! ( card - > info . unique_id & UNIQUE_ID_NOT_BY_CARD ) )
card - > dev - > dev_id = card - > info . unique_id & 0xffff ;
# endif
2006-05-27 05:58:38 +04:00
if ( card - > options . fake_ll & &
2006-05-24 11:51:13 +04:00
( qeth_get_netdev_flags ( card ) & IFF_NOARP ) )
dev - > hard_header = qeth_fake_header ;
2007-01-08 19:29:58 +03:00
if ( dev - > type = = ARPHRD_IEEE802_TR )
dev - > hard_header_parse = NULL ;
else
dev - > hard_header_parse = qeth_hard_header_parse ;
2005-04-17 02:20:36 +04:00
dev - > set_mac_address = qeth_layer2_set_mac_address ;
dev - > flags | = qeth_get_netdev_flags ( card ) ;
if ( ( card - > options . fake_broadcast ) | |
( card - > info . broadcast_capable ) )
dev - > flags | = IFF_BROADCAST ;
dev - > hard_header_len =
qeth_get_hlen ( card - > info . link_type ) + card - > options . add_hhlen ;
dev - > addr_len = OSA_ADDR_LEN ;
dev - > mtu = card - > info . initial_mtu ;
2005-09-30 12:19:19 +04:00
if ( card - > info . type ! = QETH_CARD_TYPE_OSN )
SET_ETHTOOL_OPS ( dev , & qeth_ethtool_ops ) ;
2005-04-17 02:20:36 +04:00
SET_MODULE_OWNER ( dev ) ;
return 0 ;
}
static void
qeth_init_func_level ( struct qeth_card * card )
{
if ( card - > ipato . enabled ) {
if ( card - > info . type = = QETH_CARD_TYPE_IQD )
card - > info . func_level =
QETH_IDX_FUNC_LEVEL_IQD_ENA_IPAT ;
else
card - > info . func_level =
QETH_IDX_FUNC_LEVEL_OSAE_ENA_IPAT ;
} else {
if ( card - > info . type = = QETH_CARD_TYPE_IQD )
2005-09-30 12:19:19 +04:00
/*FIXME:why do we have same values for dis and ena for osae??? */
2005-04-17 02:20:36 +04:00
card - > info . func_level =
QETH_IDX_FUNC_LEVEL_IQD_DIS_IPAT ;
else
card - > info . func_level =
QETH_IDX_FUNC_LEVEL_OSAE_DIS_IPAT ;
}
}
/**
* hardsetup card , initialize MPC and QDIO stuff
*/
static int
qeth_hardsetup_card ( struct qeth_card * card )
{
int retries = 3 ;
int rc ;
QETH_DBF_TEXT ( setup , 2 , " hrdsetup " ) ;
retry :
if ( retries < 3 ) {
PRINT_WARN ( " Retrying to do IDX activates. \n " ) ;
ccw_device_set_offline ( CARD_DDEV ( card ) ) ;
ccw_device_set_offline ( CARD_WDEV ( card ) ) ;
ccw_device_set_offline ( CARD_RDEV ( card ) ) ;
ccw_device_set_online ( CARD_RDEV ( card ) ) ;
ccw_device_set_online ( CARD_WDEV ( card ) ) ;
ccw_device_set_online ( CARD_DDEV ( card ) ) ;
}
2005-09-30 12:19:19 +04:00
rc = qeth_qdio_clear_card ( card , card - > info . type ! = QETH_CARD_TYPE_IQD ) ;
2005-04-17 02:20:36 +04:00
if ( rc = = - ERESTARTSYS ) {
QETH_DBF_TEXT ( setup , 2 , " break1 " ) ;
return rc ;
} else if ( rc ) {
QETH_DBF_TEXT_ ( setup , 2 , " 1err%d " , rc ) ;
if ( - - retries < 0 )
goto out ;
else
goto retry ;
}
if ( ( rc = qeth_get_unitaddr ( card ) ) ) {
QETH_DBF_TEXT_ ( setup , 2 , " 2err%d " , rc ) ;
return rc ;
}
qeth_init_tokens ( card ) ;
qeth_init_func_level ( card ) ;
rc = qeth_idx_activate_channel ( & card - > read , qeth_idx_read_cb ) ;
if ( rc = = - ERESTARTSYS ) {
QETH_DBF_TEXT ( setup , 2 , " break2 " ) ;
return rc ;
} else if ( rc ) {
QETH_DBF_TEXT_ ( setup , 2 , " 3err%d " , rc ) ;
if ( - - retries < 0 )
goto out ;
else
goto retry ;
}
rc = qeth_idx_activate_channel ( & card - > write , qeth_idx_write_cb ) ;
if ( rc = = - ERESTARTSYS ) {
QETH_DBF_TEXT ( setup , 2 , " break3 " ) ;
return rc ;
} else if ( rc ) {
QETH_DBF_TEXT_ ( setup , 2 , " 4err%d " , rc ) ;
if ( - - retries < 0 )
goto out ;
else
goto retry ;
}
if ( ( rc = qeth_mpc_initialize ( card ) ) ) {
QETH_DBF_TEXT_ ( setup , 2 , " 5err%d " , rc ) ;
goto out ;
}
/*network device will be recovered*/
if ( card - > dev ) {
card - > dev - > hard_header = card - > orig_hard_header ;
2006-05-27 05:58:38 +04:00
if ( card - > options . fake_ll & &
2006-05-24 11:51:11 +04:00
( qeth_get_netdev_flags ( card ) & IFF_NOARP ) )
card - > dev - > hard_header = qeth_fake_header ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/* at first set_online allocate netdev */
card - > dev = qeth_get_netdevice ( card - > info . type ,
card - > info . link_type ) ;
if ( ! card - > dev ) {
2005-09-30 12:19:19 +04:00
qeth_qdio_clear_card ( card , card - > info . type ! =
QETH_CARD_TYPE_IQD ) ;
2005-04-17 02:20:36 +04:00
rc = - ENODEV ;
QETH_DBF_TEXT_ ( setup , 2 , " 6err%d " , rc ) ;
goto out ;
}
card - > dev - > priv = card ;
card - > orig_hard_header = card - > dev - > hard_header ;
card - > dev - > type = qeth_get_arphdr_type ( card - > info . type ,
card - > info . link_type ) ;
card - > dev - > init = qeth_netdev_init ;
return 0 ;
out :
PRINT_ERR ( " Initialization in hardsetup failed! rc=%d \n " , rc ) ;
return rc ;
}
static int
qeth_default_setassparms_cb ( struct qeth_card * card , struct qeth_reply * reply ,
unsigned long data )
{
struct qeth_ipa_cmd * cmd ;
QETH_DBF_TEXT ( trace , 4 , " defadpcb " ) ;
cmd = ( struct qeth_ipa_cmd * ) data ;
if ( cmd - > hdr . return_code = = 0 ) {
cmd - > hdr . return_code = cmd - > data . setassparms . hdr . return_code ;
if ( cmd - > hdr . prot_version = = QETH_PROT_IPV4 )
card - > options . ipa4 . enabled_funcs = cmd - > hdr . ipa_enabled ;
# ifdef CONFIG_QETH_IPV6
if ( cmd - > hdr . prot_version = = QETH_PROT_IPV6 )
card - > options . ipa6 . enabled_funcs = cmd - > hdr . ipa_enabled ;
# endif
}
if ( cmd - > data . setassparms . hdr . assist_no = = IPA_INBOUND_CHECKSUM & &
cmd - > data . setassparms . hdr . command_code = = IPA_CMD_ASS_START ) {
card - > info . csum_mask = cmd - > data . setassparms . data . flags_32bit ;
QETH_DBF_TEXT_ ( trace , 3 , " csum:%d " , card - > info . csum_mask ) ;
}
return 0 ;
}
static int
qeth_default_setadapterparms_cb ( struct qeth_card * card ,
struct qeth_reply * reply ,
unsigned long data )
{
struct qeth_ipa_cmd * cmd ;
QETH_DBF_TEXT ( trace , 4 , " defadpcb " ) ;
cmd = ( struct qeth_ipa_cmd * ) data ;
if ( cmd - > hdr . return_code = = 0 )
cmd - > hdr . return_code = cmd - > data . setadapterparms . hdr . return_code ;
return 0 ;
}
2005-11-10 15:51:25 +03:00
2005-04-17 02:20:36 +04:00
static int
qeth_query_setadapterparms_cb ( struct qeth_card * card , struct qeth_reply * reply ,
unsigned long data )
{
struct qeth_ipa_cmd * cmd ;
QETH_DBF_TEXT ( trace , 3 , " quyadpcb " ) ;
cmd = ( struct qeth_ipa_cmd * ) data ;
if ( cmd - > data . setadapterparms . data . query_cmds_supp . lan_type & 0x7f )
card - > info . link_type =
cmd - > data . setadapterparms . data . query_cmds_supp . lan_type ;
card - > options . adp . supported_funcs =
cmd - > data . setadapterparms . data . query_cmds_supp . supported_cmds ;
return qeth_default_setadapterparms_cb ( card , reply , ( unsigned long ) cmd ) ;
}
static int
qeth_query_setadapterparms ( struct qeth_card * card )
{
int rc ;
struct qeth_cmd_buffer * iob ;
QETH_DBF_TEXT ( trace , 3 , " queryadp " ) ;
iob = qeth_get_adapter_cmd ( card , IPA_SETADP_QUERY_COMMANDS_SUPPORTED ,
sizeof ( struct qeth_ipacmd_setadpparms ) ) ;
rc = qeth_send_ipa_cmd ( card , iob , qeth_query_setadapterparms_cb , NULL ) ;
return rc ;
}
static int
qeth_setadpparms_change_macaddr_cb ( struct qeth_card * card ,
struct qeth_reply * reply ,
unsigned long data )
{
struct qeth_ipa_cmd * cmd ;
QETH_DBF_TEXT ( trace , 4 , " chgmaccb " ) ;
cmd = ( struct qeth_ipa_cmd * ) data ;
2007-05-02 17:17:11 +04:00
if ( ! card - > options . layer2 | |
2006-05-27 05:58:38 +04:00
! ( card - > info . mac_bits & QETH_LAYER2_MAC_READ ) ) {
2005-11-10 15:51:17 +03:00
memcpy ( card - > dev - > dev_addr ,
& cmd - > data . setadapterparms . data . change_addr . addr ,
OSA_ADDR_LEN ) ;
card - > info . mac_bits | = QETH_LAYER2_MAC_READ ;
}
2005-04-17 02:20:36 +04:00
qeth_default_setadapterparms_cb ( card , reply , ( unsigned long ) cmd ) ;
return 0 ;
}
static int
qeth_setadpparms_change_macaddr ( struct qeth_card * card )
{
int rc ;
struct qeth_cmd_buffer * iob ;
struct qeth_ipa_cmd * cmd ;
QETH_DBF_TEXT ( trace , 4 , " chgmac " ) ;
iob = qeth_get_adapter_cmd ( card , IPA_SETADP_ALTER_MAC_ADDRESS ,
sizeof ( struct qeth_ipacmd_setadpparms ) ) ;
cmd = ( struct qeth_ipa_cmd * ) ( iob - > data + IPA_PDU_HEADER_SIZE ) ;
cmd - > data . setadapterparms . data . change_addr . cmd = CHANGE_ADDR_READ_MAC ;
cmd - > data . setadapterparms . data . change_addr . addr_size = OSA_ADDR_LEN ;
memcpy ( & cmd - > data . setadapterparms . data . change_addr . addr ,
card - > dev - > dev_addr , OSA_ADDR_LEN ) ;
rc = qeth_send_ipa_cmd ( card , iob , qeth_setadpparms_change_macaddr_cb ,
NULL ) ;
return rc ;
}
static int
qeth_send_setadp_mode ( struct qeth_card * card , __u32 command , __u32 mode )
{
int rc ;
struct qeth_cmd_buffer * iob ;
struct qeth_ipa_cmd * cmd ;
QETH_DBF_TEXT ( trace , 4 , " adpmode " ) ;
iob = qeth_get_adapter_cmd ( card , command ,
sizeof ( struct qeth_ipacmd_setadpparms ) ) ;
cmd = ( struct qeth_ipa_cmd * ) ( iob - > data + IPA_PDU_HEADER_SIZE ) ;
cmd - > data . setadapterparms . data . mode = mode ;
rc = qeth_send_ipa_cmd ( card , iob , qeth_default_setadapterparms_cb ,
NULL ) ;
return rc ;
}
2007-02-05 23:18:53 +03:00
static int
2005-04-17 02:20:36 +04:00
qeth_setadapter_hstr ( struct qeth_card * card )
{
int rc ;
QETH_DBF_TEXT ( trace , 4 , " adphstr " ) ;
if ( qeth_adp_supported ( card , IPA_SETADP_SET_BROADCAST_MODE ) ) {
rc = qeth_send_setadp_mode ( card , IPA_SETADP_SET_BROADCAST_MODE ,
card - > options . broadcast_mode ) ;
if ( rc )
PRINT_WARN ( " couldn't set broadcast mode on "
" device %s: x%x \n " ,
CARD_BUS_ID ( card ) , rc ) ;
rc = qeth_send_setadp_mode ( card , IPA_SETADP_ALTER_MAC_ADDRESS ,
card - > options . macaddr_mode ) ;
if ( rc )
PRINT_WARN ( " couldn't set macaddr mode on "
" device %s: x%x \n " , CARD_BUS_ID ( card ) , rc ) ;
return rc ;
}
if ( card - > options . broadcast_mode = = QETH_TR_BROADCAST_LOCAL )
PRINT_WARN ( " set adapter parameters not available "
" to set broadcast mode, using ALLRINGS "
" on device %s: \n " , CARD_BUS_ID ( card ) ) ;
if ( card - > options . macaddr_mode = = QETH_TR_MACADDR_CANONICAL )
PRINT_WARN ( " set adapter parameters not available "
" to set macaddr mode, using NONCANONICAL "
" on device %s: \n " , CARD_BUS_ID ( card ) ) ;
return 0 ;
}
static int
qeth_setadapter_parms ( struct qeth_card * card )
{
int rc ;
QETH_DBF_TEXT ( setup , 2 , " setadprm " ) ;
if ( ! qeth_is_supported ( card , IPA_SETADAPTERPARMS ) ) {
PRINT_WARN ( " set adapter parameters not supported "
" on device %s. \n " ,
CARD_BUS_ID ( card ) ) ;
QETH_DBF_TEXT ( setup , 2 , " notsupp " ) ;
return 0 ;
}
rc = qeth_query_setadapterparms ( card ) ;
if ( rc ) {
PRINT_WARN ( " couldn't set adapter parameters on device %s: "
" x%x \n " , CARD_BUS_ID ( card ) , rc ) ;
return rc ;
}
if ( qeth_adp_supported ( card , IPA_SETADP_ALTER_MAC_ADDRESS ) ) {
rc = qeth_setadpparms_change_macaddr ( card ) ;
if ( rc )
PRINT_WARN ( " couldn't get MAC address on "
" device %s: x%x \n " ,
CARD_BUS_ID ( card ) , rc ) ;
}
if ( ( card - > info . link_type = = QETH_LINK_TYPE_HSTR ) | |
( card - > info . link_type = = QETH_LINK_TYPE_LANE_TR ) )
rc = qeth_setadapter_hstr ( card ) ;
return rc ;
}
static int
qeth_layer2_initialize ( struct qeth_card * card )
{
int rc = 0 ;
QETH_DBF_TEXT ( setup , 2 , " doL2init " ) ;
QETH_DBF_TEXT_ ( setup , 2 , " doL2%s " , CARD_BUS_ID ( card ) ) ;
2005-11-10 15:51:25 +03:00
rc = qeth_query_setadapterparms ( card ) ;
if ( rc ) {
PRINT_WARN ( " could not query adapter parameters on device %s: "
" x%x \n " , CARD_BUS_ID ( card ) , rc ) ;
}
2005-04-17 02:20:36 +04:00
rc = qeth_setadpparms_change_macaddr ( card ) ;
if ( rc ) {
PRINT_WARN ( " couldn't get MAC address on "
" device %s: x%x \n " ,
CARD_BUS_ID ( card ) , rc ) ;
QETH_DBF_TEXT_ ( setup , 2 , " 1err%d " , rc ) ;
return rc ;
}
QETH_DBF_HEX ( setup , 2 , card - > dev - > dev_addr , OSA_ADDR_LEN ) ;
rc = qeth_layer2_send_setmac ( card , & card - > dev - > dev_addr [ 0 ] ) ;
if ( rc )
QETH_DBF_TEXT_ ( setup , 2 , " 2err%d " , rc ) ;
return 0 ;
}
static int
qeth_send_startstoplan ( struct qeth_card * card , enum qeth_ipa_cmds ipacmd ,
enum qeth_prot_versions prot )
{
int rc ;
struct qeth_cmd_buffer * iob ;
iob = qeth_get_ipacmd_buffer ( card , ipacmd , prot ) ;
rc = qeth_send_ipa_cmd ( card , iob , NULL , NULL ) ;
return rc ;
}
static int
qeth_send_startlan ( struct qeth_card * card , enum qeth_prot_versions prot )
{
int rc ;
QETH_DBF_TEXT_ ( setup , 2 , " strtlan%i " , prot ) ;
rc = qeth_send_startstoplan ( card , IPA_CMD_STARTLAN , prot ) ;
return rc ;
}
static int
qeth_send_stoplan ( struct qeth_card * card )
{
int rc = 0 ;
/*
* TODO : according to the IPA format document page 14 ,
* TCP / IP ( we ! ) never issue a STOPLAN
* is this right ? ! ?
*/
QETH_DBF_TEXT ( trace , 2 , " stoplan " ) ;
rc = qeth_send_startstoplan ( card , IPA_CMD_STOPLAN , QETH_PROT_IPV4 ) ;
return rc ;
}
static int
qeth_query_ipassists_cb ( struct qeth_card * card , struct qeth_reply * reply ,
unsigned long data )
{
struct qeth_ipa_cmd * cmd ;
QETH_DBF_TEXT ( setup , 2 , " qipasscb " ) ;
cmd = ( struct qeth_ipa_cmd * ) data ;
if ( cmd - > hdr . prot_version = = QETH_PROT_IPV4 ) {
card - > options . ipa4 . supported_funcs = cmd - > hdr . ipa_supported ;
card - > options . ipa4 . enabled_funcs = cmd - > hdr . ipa_enabled ;
2005-09-30 12:17:24 +04:00
/* Disable IPV6 support hard coded for Hipersockets */
if ( card - > info . type = = QETH_CARD_TYPE_IQD )
card - > options . ipa4 . supported_funcs & = ~ IPA_IPV6 ;
2005-04-17 02:20:36 +04:00
} else {
# ifdef CONFIG_QETH_IPV6
card - > options . ipa6 . supported_funcs = cmd - > hdr . ipa_supported ;
card - > options . ipa6 . enabled_funcs = cmd - > hdr . ipa_enabled ;
# endif
}
QETH_DBF_TEXT ( setup , 2 , " suppenbl " ) ;
QETH_DBF_TEXT_ ( setup , 2 , " %x " , cmd - > hdr . ipa_supported ) ;
QETH_DBF_TEXT_ ( setup , 2 , " %x " , cmd - > hdr . ipa_enabled ) ;
return 0 ;
}
static int
qeth_query_ipassists ( struct qeth_card * card , enum qeth_prot_versions prot )
{
int rc ;
struct qeth_cmd_buffer * iob ;
QETH_DBF_TEXT_ ( setup , 2 , " qipassi%i " , prot ) ;
if ( card - > options . layer2 ) {
QETH_DBF_TEXT ( setup , 2 , " noprmly2 " ) ;
return - EPERM ;
}
iob = qeth_get_ipacmd_buffer ( card , IPA_CMD_QIPASSIST , prot ) ;
rc = qeth_send_ipa_cmd ( card , iob , qeth_query_ipassists_cb , NULL ) ;
return rc ;
}
static struct qeth_cmd_buffer *
qeth_get_setassparms_cmd ( struct qeth_card * card , enum qeth_ipa_funcs ipa_func ,
__u16 cmd_code , __u16 len ,
enum qeth_prot_versions prot )
{
struct qeth_cmd_buffer * iob ;
struct qeth_ipa_cmd * cmd ;
QETH_DBF_TEXT ( trace , 4 , " getasscm " ) ;
iob = qeth_get_ipacmd_buffer ( card , IPA_CMD_SETASSPARMS , prot ) ;
cmd = ( struct qeth_ipa_cmd * ) ( iob - > data + IPA_PDU_HEADER_SIZE ) ;
cmd - > data . setassparms . hdr . assist_no = ipa_func ;
cmd - > data . setassparms . hdr . length = 8 + len ;
cmd - > data . setassparms . hdr . command_code = cmd_code ;
cmd - > data . setassparms . hdr . return_code = 0 ;
cmd - > data . setassparms . hdr . seq_no = 0 ;
return iob ;
}
static int
qeth_send_setassparms ( struct qeth_card * card , struct qeth_cmd_buffer * iob ,
__u16 len , long data ,
int ( * reply_cb )
( struct qeth_card * , struct qeth_reply * , unsigned long ) ,
void * reply_param )
{
int rc ;
struct qeth_ipa_cmd * cmd ;
QETH_DBF_TEXT ( trace , 4 , " sendassp " ) ;
cmd = ( struct qeth_ipa_cmd * ) ( iob - > data + IPA_PDU_HEADER_SIZE ) ;
if ( len < = sizeof ( __u32 ) )
cmd - > data . setassparms . data . flags_32bit = ( __u32 ) data ;
2006-03-22 18:03:41 +03:00
else /* (len > sizeof(__u32)) */
2005-04-17 02:20:36 +04:00
memcpy ( & cmd - > data . setassparms . data , ( void * ) data , len ) ;
rc = qeth_send_ipa_cmd ( card , iob , reply_cb , reply_param ) ;
return rc ;
}
# ifdef CONFIG_QETH_IPV6
static int
qeth_send_simple_setassparms_ipv6 ( struct qeth_card * card ,
enum qeth_ipa_funcs ipa_func , __u16 cmd_code )
{
int rc ;
struct qeth_cmd_buffer * iob ;
QETH_DBF_TEXT ( trace , 4 , " simassp6 " ) ;
iob = qeth_get_setassparms_cmd ( card , ipa_func , cmd_code ,
0 , QETH_PROT_IPV6 ) ;
rc = qeth_send_setassparms ( card , iob , 0 , 0 ,
qeth_default_setassparms_cb , NULL ) ;
return rc ;
}
# endif
static int
qeth_send_simple_setassparms ( struct qeth_card * card ,
enum qeth_ipa_funcs ipa_func ,
__u16 cmd_code , long data )
{
int rc ;
int length = 0 ;
struct qeth_cmd_buffer * iob ;
QETH_DBF_TEXT ( trace , 4 , " simassp4 " ) ;
if ( data )
length = sizeof ( __u32 ) ;
iob = qeth_get_setassparms_cmd ( card , ipa_func , cmd_code ,
length , QETH_PROT_IPV4 ) ;
rc = qeth_send_setassparms ( card , iob , length , data ,
qeth_default_setassparms_cb , NULL ) ;
return rc ;
}
2007-02-05 23:18:53 +03:00
static int
2005-04-17 02:20:36 +04:00
qeth_start_ipa_arp_processing ( struct qeth_card * card )
{
int rc ;
QETH_DBF_TEXT ( trace , 3 , " ipaarp " ) ;
if ( ! qeth_is_supported ( card , IPA_ARP_PROCESSING ) ) {
PRINT_WARN ( " ARP processing not supported "
" on %s! \n " , QETH_CARD_IFNAME ( card ) ) ;
return 0 ;
}
rc = qeth_send_simple_setassparms ( card , IPA_ARP_PROCESSING ,
IPA_CMD_ASS_START , 0 ) ;
if ( rc ) {
PRINT_WARN ( " Could not start ARP processing "
" assist on %s: 0x%x \n " ,
QETH_CARD_IFNAME ( card ) , rc ) ;
}
return rc ;
}
static int
qeth_start_ipa_ip_fragmentation ( struct qeth_card * card )
{
int rc ;
QETH_DBF_TEXT ( trace , 3 , " ipaipfrg " ) ;
if ( ! qeth_is_supported ( card , IPA_IP_FRAGMENTATION ) ) {
PRINT_INFO ( " Hardware IP fragmentation not supported on %s \n " ,
QETH_CARD_IFNAME ( card ) ) ;
return - EOPNOTSUPP ;
}
rc = qeth_send_simple_setassparms ( card , IPA_IP_FRAGMENTATION ,
IPA_CMD_ASS_START , 0 ) ;
if ( rc ) {
PRINT_WARN ( " Could not start Hardware IP fragmentation "
" assist on %s: 0x%x \n " ,
QETH_CARD_IFNAME ( card ) , rc ) ;
} else
PRINT_INFO ( " Hardware IP fragmentation enabled \n " ) ;
return rc ;
}
static int
qeth_start_ipa_source_mac ( struct qeth_card * card )
{
int rc ;
QETH_DBF_TEXT ( trace , 3 , " stsrcmac " ) ;
if ( ! card - > options . fake_ll )
return - EOPNOTSUPP ;
if ( ! qeth_is_supported ( card , IPA_SOURCE_MAC ) ) {
PRINT_INFO ( " Inbound source address not "
" supported on %s \n " , QETH_CARD_IFNAME ( card ) ) ;
return - EOPNOTSUPP ;
}
rc = qeth_send_simple_setassparms ( card , IPA_SOURCE_MAC ,
IPA_CMD_ASS_START , 0 ) ;
if ( rc )
PRINT_WARN ( " Could not start inbound source "
" assist on %s: 0x%x \n " ,
QETH_CARD_IFNAME ( card ) , rc ) ;
return rc ;
}
static int
qeth_start_ipa_vlan ( struct qeth_card * card )
{
int rc = 0 ;
QETH_DBF_TEXT ( trace , 3 , " strtvlan " ) ;
# ifdef CONFIG_QETH_VLAN
if ( ! qeth_is_supported ( card , IPA_FULL_VLAN ) ) {
PRINT_WARN ( " VLAN not supported on %s \n " , QETH_CARD_IFNAME ( card ) ) ;
return - EOPNOTSUPP ;
}
rc = qeth_send_simple_setassparms ( card , IPA_VLAN_PRIO ,
IPA_CMD_ASS_START , 0 ) ;
if ( rc ) {
PRINT_WARN ( " Could not start vlan "
" assist on %s: 0x%x \n " ,
QETH_CARD_IFNAME ( card ) , rc ) ;
} else {
PRINT_INFO ( " VLAN enabled \n " ) ;
card - > dev - > features | =
NETIF_F_HW_VLAN_FILTER |
NETIF_F_HW_VLAN_TX |
NETIF_F_HW_VLAN_RX ;
}
# endif /* QETH_VLAN */
return rc ;
}
static int
qeth_start_ipa_multicast ( struct qeth_card * card )
{
int rc ;
QETH_DBF_TEXT ( trace , 3 , " stmcast " ) ;
if ( ! qeth_is_supported ( card , IPA_MULTICASTING ) ) {
PRINT_WARN ( " Multicast not supported on %s \n " ,
QETH_CARD_IFNAME ( card ) ) ;
return - EOPNOTSUPP ;
}
rc = qeth_send_simple_setassparms ( card , IPA_MULTICASTING ,
IPA_CMD_ASS_START , 0 ) ;
if ( rc ) {
PRINT_WARN ( " Could not start multicast "
" assist on %s: rc=%i \n " ,
QETH_CARD_IFNAME ( card ) , rc ) ;
} else {
PRINT_INFO ( " Multicast enabled \n " ) ;
card - > dev - > flags | = IFF_MULTICAST ;
}
return rc ;
}
# ifdef CONFIG_QETH_IPV6
static int
qeth_softsetup_ipv6 ( struct qeth_card * card )
{
int rc ;
QETH_DBF_TEXT ( trace , 3 , " softipv6 " ) ;
rc = qeth_send_startlan ( card , QETH_PROT_IPV6 ) ;
if ( rc ) {
PRINT_ERR ( " IPv6 startlan failed on %s \n " ,
QETH_CARD_IFNAME ( card ) ) ;
return rc ;
}
rc = qeth_query_ipassists ( card , QETH_PROT_IPV6 ) ;
if ( rc ) {
PRINT_ERR ( " IPv6 query ipassist failed on %s \n " ,
QETH_CARD_IFNAME ( card ) ) ;
return rc ;
}
rc = qeth_send_simple_setassparms ( card , IPA_IPV6 ,
IPA_CMD_ASS_START , 3 ) ;
if ( rc ) {
PRINT_WARN ( " IPv6 start assist (version 4) failed "
" on %s: 0x%x \n " ,
QETH_CARD_IFNAME ( card ) , rc ) ;
return rc ;
}
rc = qeth_send_simple_setassparms_ipv6 ( card , IPA_IPV6 ,
IPA_CMD_ASS_START ) ;
if ( rc ) {
PRINT_WARN ( " IPV6 start assist (version 6) failed "
" on %s: 0x%x \n " ,
QETH_CARD_IFNAME ( card ) , rc ) ;
return rc ;
}
rc = qeth_send_simple_setassparms_ipv6 ( card , IPA_PASSTHRU ,
IPA_CMD_ASS_START ) ;
if ( rc ) {
PRINT_WARN ( " Could not enable passthrough "
" on %s: 0x%x \n " ,
QETH_CARD_IFNAME ( card ) , rc ) ;
return rc ;
}
PRINT_INFO ( " IPV6 enabled \n " ) ;
return 0 ;
}
# endif
static int
qeth_start_ipa_ipv6 ( struct qeth_card * card )
{
int rc = 0 ;
# ifdef CONFIG_QETH_IPV6
QETH_DBF_TEXT ( trace , 3 , " strtipv6 " ) ;
if ( ! qeth_is_supported ( card , IPA_IPV6 ) ) {
PRINT_WARN ( " IPv6 not supported on %s \n " ,
QETH_CARD_IFNAME ( card ) ) ;
return 0 ;
}
rc = qeth_softsetup_ipv6 ( card ) ;
# endif
return rc ;
}
static int
qeth_start_ipa_broadcast ( struct qeth_card * card )
{
int rc ;
QETH_DBF_TEXT ( trace , 3 , " stbrdcst " ) ;
card - > info . broadcast_capable = 0 ;
if ( ! qeth_is_supported ( card , IPA_FILTERING ) ) {
PRINT_WARN ( " Broadcast not supported on %s \n " ,
QETH_CARD_IFNAME ( card ) ) ;
rc = - EOPNOTSUPP ;
goto out ;
}
rc = qeth_send_simple_setassparms ( card , IPA_FILTERING ,
IPA_CMD_ASS_START , 0 ) ;
if ( rc ) {
PRINT_WARN ( " Could not enable broadcasting filtering "
" on %s: 0x%x \n " ,
QETH_CARD_IFNAME ( card ) , rc ) ;
goto out ;
}
rc = qeth_send_simple_setassparms ( card , IPA_FILTERING ,
IPA_CMD_ASS_CONFIGURE , 1 ) ;
if ( rc ) {
PRINT_WARN ( " Could not set up broadcast filtering on %s: 0x%x \n " ,
QETH_CARD_IFNAME ( card ) , rc ) ;
goto out ;
}
card - > info . broadcast_capable = QETH_BROADCAST_WITH_ECHO ;
PRINT_INFO ( " Broadcast enabled \n " ) ;
rc = qeth_send_simple_setassparms ( card , IPA_FILTERING ,
IPA_CMD_ASS_ENABLE , 1 ) ;
if ( rc ) {
PRINT_WARN ( " Could not set up broadcast echo filtering on "
" %s: 0x%x \n " , QETH_CARD_IFNAME ( card ) , rc ) ;
goto out ;
}
card - > info . broadcast_capable = QETH_BROADCAST_WITHOUT_ECHO ;
out :
if ( card - > info . broadcast_capable )
card - > dev - > flags | = IFF_BROADCAST ;
else
card - > dev - > flags & = ~ IFF_BROADCAST ;
return rc ;
}
static int
qeth_send_checksum_command ( struct qeth_card * card )
{
int rc ;
rc = qeth_send_simple_setassparms ( card , IPA_INBOUND_CHECKSUM ,
IPA_CMD_ASS_START , 0 ) ;
if ( rc ) {
PRINT_WARN ( " Starting Inbound HW Checksumming failed on %s: "
" 0x%x, \n continuing using Inbound SW Checksumming \n " ,
QETH_CARD_IFNAME ( card ) , rc ) ;
return rc ;
}
rc = qeth_send_simple_setassparms ( card , IPA_INBOUND_CHECKSUM ,
IPA_CMD_ASS_ENABLE ,
card - > info . csum_mask ) ;
if ( rc ) {
PRINT_WARN ( " Enabling Inbound HW Checksumming failed on %s: "
" 0x%x, \n continuing using Inbound SW Checksumming \n " ,
QETH_CARD_IFNAME ( card ) , rc ) ;
return rc ;
}
return 0 ;
}
static int
qeth_start_ipa_checksum ( struct qeth_card * card )
{
int rc = 0 ;
QETH_DBF_TEXT ( trace , 3 , " strtcsum " ) ;
if ( card - > options . checksum_type = = NO_CHECKSUMMING ) {
PRINT_WARN ( " Using no checksumming on %s. \n " ,
QETH_CARD_IFNAME ( card ) ) ;
return 0 ;
}
if ( card - > options . checksum_type = = SW_CHECKSUMMING ) {
PRINT_WARN ( " Using SW checksumming on %s. \n " ,
QETH_CARD_IFNAME ( card ) ) ;
return 0 ;
}
if ( ! qeth_is_supported ( card , IPA_INBOUND_CHECKSUM ) ) {
PRINT_WARN ( " Inbound HW Checksumming not "
" supported on %s, \n continuing "
" using Inbound SW Checksumming \n " ,
QETH_CARD_IFNAME ( card ) ) ;
card - > options . checksum_type = SW_CHECKSUMMING ;
return 0 ;
}
rc = qeth_send_checksum_command ( card ) ;
if ( ! rc ) {
PRINT_INFO ( " HW Checksumming (inbound) enabled \n " ) ;
}
return rc ;
}
static int
qeth_start_ipa_tso ( struct qeth_card * card )
{
int rc ;
QETH_DBF_TEXT ( trace , 3 , " sttso " ) ;
if ( ! qeth_is_supported ( card , IPA_OUTBOUND_TSO ) ) {
PRINT_WARN ( " Outbound TSO not supported on %s \n " ,
QETH_CARD_IFNAME ( card ) ) ;
rc = - EOPNOTSUPP ;
} else {
rc = qeth_send_simple_setassparms ( card , IPA_OUTBOUND_TSO ,
IPA_CMD_ASS_START , 0 ) ;
if ( rc )
PRINT_WARN ( " Could not start outbound TSO "
" assist on %s: rc=%i \n " ,
QETH_CARD_IFNAME ( card ) , rc ) ;
else
PRINT_INFO ( " Outbound TSO enabled \n " ) ;
}
if ( rc & & ( card - > options . large_send = = QETH_LARGE_SEND_TSO ) ) {
card - > options . large_send = QETH_LARGE_SEND_NO ;
card - > dev - > features & = ~ ( NETIF_F_TSO | NETIF_F_SG ) ;
}
return rc ;
}
static int
qeth_start_ipassists ( struct qeth_card * card )
{
QETH_DBF_TEXT ( trace , 3 , " strtipas " ) ;
qeth_start_ipa_arp_processing ( card ) ; /* go on*/
qeth_start_ipa_ip_fragmentation ( card ) ; /* go on*/
qeth_start_ipa_source_mac ( card ) ; /* go on*/
qeth_start_ipa_vlan ( card ) ; /* go on*/
qeth_start_ipa_multicast ( card ) ; /* go on*/
qeth_start_ipa_ipv6 ( card ) ; /* go on*/
qeth_start_ipa_broadcast ( card ) ; /* go on*/
qeth_start_ipa_checksum ( card ) ; /* go on*/
qeth_start_ipa_tso ( card ) ; /* go on*/
return 0 ;
}
static int
qeth_send_setrouting ( struct qeth_card * card , enum qeth_routing_types type ,
enum qeth_prot_versions prot )
{
int rc ;
struct qeth_ipa_cmd * cmd ;
struct qeth_cmd_buffer * iob ;
QETH_DBF_TEXT ( trace , 4 , " setroutg " ) ;
iob = qeth_get_ipacmd_buffer ( card , IPA_CMD_SETRTG , prot ) ;
cmd = ( struct qeth_ipa_cmd * ) ( iob - > data + IPA_PDU_HEADER_SIZE ) ;
cmd - > data . setrtg . type = ( type ) ;
rc = qeth_send_ipa_cmd ( card , iob , NULL , NULL ) ;
return rc ;
}
static void
qeth_correct_routing_type ( struct qeth_card * card , enum qeth_routing_types * type ,
enum qeth_prot_versions prot )
{
if ( card - > info . type = = QETH_CARD_TYPE_IQD ) {
switch ( * type ) {
case NO_ROUTER :
case PRIMARY_CONNECTOR :
case SECONDARY_CONNECTOR :
case MULTICAST_ROUTER :
return ;
default :
goto out_inval ;
}
} else {
switch ( * type ) {
case NO_ROUTER :
case PRIMARY_ROUTER :
case SECONDARY_ROUTER :
return ;
case MULTICAST_ROUTER :
if ( qeth_is_ipafunc_supported ( card , prot ,
IPA_OSA_MC_ROUTER ) )
return ;
default :
goto out_inval ;
}
}
out_inval :
PRINT_WARN ( " Routing type '%s' not supported for interface %s. \n "
" Router status set to 'no router'. \n " ,
( ( * type = = PRIMARY_ROUTER ) ? " primary router " :
( * type = = SECONDARY_ROUTER ) ? " secondary router " :
( * type = = PRIMARY_CONNECTOR ) ? " primary connector " :
( * type = = SECONDARY_CONNECTOR ) ? " secondary connector " :
( * type = = MULTICAST_ROUTER ) ? " multicast router " :
" unknown " ) ,
card - > dev - > name ) ;
* type = NO_ROUTER ;
}
int
qeth_setrouting_v4 ( struct qeth_card * card )
{
int rc ;
QETH_DBF_TEXT ( trace , 3 , " setrtg4 " ) ;
qeth_correct_routing_type ( card , & card - > options . route4 . type ,
QETH_PROT_IPV4 ) ;
rc = qeth_send_setrouting ( card , card - > options . route4 . type ,
QETH_PROT_IPV4 ) ;
if ( rc ) {
card - > options . route4 . type = NO_ROUTER ;
PRINT_WARN ( " Error (0x%04x) while setting routing type on %s. "
" Type set to 'no router'. \n " ,
rc , QETH_CARD_IFNAME ( card ) ) ;
}
return rc ;
}
int
qeth_setrouting_v6 ( struct qeth_card * card )
{
int rc = 0 ;
QETH_DBF_TEXT ( trace , 3 , " setrtg6 " ) ;
# ifdef CONFIG_QETH_IPV6
2006-09-15 18:27:02 +04:00
if ( ! qeth_is_supported ( card , IPA_IPV6 ) )
return 0 ;
2005-04-17 02:20:36 +04:00
qeth_correct_routing_type ( card , & card - > options . route6 . type ,
QETH_PROT_IPV6 ) ;
rc = qeth_send_setrouting ( card , card - > options . route6 . type ,
QETH_PROT_IPV6 ) ;
if ( rc ) {
card - > options . route6 . type = NO_ROUTER ;
PRINT_WARN ( " Error (0x%04x) while setting routing type on %s. "
" Type set to 'no router'. \n " ,
rc , QETH_CARD_IFNAME ( card ) ) ;
}
# endif
return rc ;
}
int
2005-09-14 20:03:26 +04:00
qeth_set_large_send ( struct qeth_card * card , enum qeth_large_send_types type )
2005-04-17 02:20:36 +04:00
{
int rc = 0 ;
2005-09-14 20:03:26 +04:00
if ( card - > dev = = NULL ) {
card - > options . large_send = type ;
2005-04-17 02:20:36 +04:00
return 0 ;
2005-09-14 20:03:26 +04:00
}
2006-05-24 11:51:11 +04:00
if ( card - > state = = CARD_STATE_UP )
netif_tx_disable ( card - > dev ) ;
2005-09-14 20:03:26 +04:00
card - > options . large_send = type ;
2005-04-17 02:20:36 +04:00
switch ( card - > options . large_send ) {
case QETH_LARGE_SEND_EDDP :
card - > dev - > features | = NETIF_F_TSO | NETIF_F_SG ;
break ;
case QETH_LARGE_SEND_TSO :
if ( qeth_is_supported ( card , IPA_OUTBOUND_TSO ) ) {
card - > dev - > features | = NETIF_F_TSO | NETIF_F_SG ;
} else {
PRINT_WARN ( " TSO not supported on %s. "
" large_send set to 'no'. \n " ,
card - > dev - > name ) ;
card - > dev - > features & = ~ ( NETIF_F_TSO | NETIF_F_SG ) ;
card - > options . large_send = QETH_LARGE_SEND_NO ;
rc = - EOPNOTSUPP ;
}
break ;
default : /* includes QETH_LARGE_SEND_NO */
card - > dev - > features & = ~ ( NETIF_F_TSO | NETIF_F_SG ) ;
break ;
}
2006-05-24 11:51:11 +04:00
if ( card - > state = = CARD_STATE_UP )
netif_wake_queue ( card - > dev ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
/*
* softsetup card : init IPA stuff
*/
static int
qeth_softsetup_card ( struct qeth_card * card )
{
int rc ;
QETH_DBF_TEXT ( setup , 2 , " softsetp " ) ;
if ( ( rc = qeth_send_startlan ( card , QETH_PROT_IPV4 ) ) ) {
QETH_DBF_TEXT_ ( setup , 2 , " 1err%d " , rc ) ;
if ( rc = = 0xe080 ) {
PRINT_WARN ( " LAN on card %s if offline! "
" Continuing softsetup. \n " ,
CARD_BUS_ID ( card ) ) ;
card - > lan_online = 0 ;
} else
return rc ;
} else
card - > lan_online = 1 ;
2005-09-30 12:19:19 +04:00
if ( card - > info . type = = QETH_CARD_TYPE_OSN )
goto out ;
2006-02-07 19:04:38 +03:00
qeth_set_large_send ( card , card - > options . large_send ) ;
2005-04-17 02:20:36 +04:00
if ( card - > options . layer2 ) {
card - > dev - > features | =
NETIF_F_HW_VLAN_FILTER |
NETIF_F_HW_VLAN_TX |
NETIF_F_HW_VLAN_RX ;
card - > dev - > flags | = IFF_MULTICAST | IFF_BROADCAST ;
card - > info . broadcast_capable = 1 ;
if ( ( rc = qeth_layer2_initialize ( card ) ) ) {
QETH_DBF_TEXT_ ( setup , 2 , " L2err%d " , rc ) ;
return rc ;
}
# ifdef CONFIG_QETH_VLAN
qeth_layer2_process_vlans ( card , 0 ) ;
# endif
goto out ;
}
if ( ( rc = qeth_setadapter_parms ( card ) ) )
QETH_DBF_TEXT_ ( setup , 2 , " 2err%d " , rc ) ;
if ( ( rc = qeth_start_ipassists ( card ) ) )
QETH_DBF_TEXT_ ( setup , 2 , " 3err%d " , rc ) ;
if ( ( rc = qeth_setrouting_v4 ( card ) ) )
QETH_DBF_TEXT_ ( setup , 2 , " 4err%d " , rc ) ;
if ( ( rc = qeth_setrouting_v6 ( card ) ) )
QETH_DBF_TEXT_ ( setup , 2 , " 5err%d " , rc ) ;
out :
2006-05-24 11:51:11 +04:00
netif_tx_disable ( card - > dev ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
# ifdef CONFIG_QETH_IPV6
static int
qeth_get_unique_id_cb ( struct qeth_card * card , struct qeth_reply * reply ,
unsigned long data )
{
struct qeth_ipa_cmd * cmd ;
cmd = ( struct qeth_ipa_cmd * ) data ;
if ( cmd - > hdr . return_code = = 0 )
card - > info . unique_id = * ( ( __u16 * )
& cmd - > data . create_destroy_addr . unique_id [ 6 ] ) ;
else {
card - > info . unique_id = UNIQUE_ID_IF_CREATE_ADDR_FAILED |
UNIQUE_ID_NOT_BY_CARD ;
PRINT_WARN ( " couldn't get a unique id from the card on device "
" %s (result=x%x), using default id. ipv6 "
" autoconfig on other lpars may lead to duplicate "
" ip addresses. please use manually "
" configured ones. \n " ,
CARD_BUS_ID ( card ) , cmd - > hdr . return_code ) ;
}
return 0 ;
}
# endif
static int
qeth_put_unique_id ( struct qeth_card * card )
{
int rc = 0 ;
# ifdef CONFIG_QETH_IPV6
struct qeth_cmd_buffer * iob ;
struct qeth_ipa_cmd * cmd ;
QETH_DBF_TEXT ( trace , 2 , " puniqeid " ) ;
if ( ( card - > info . unique_id & UNIQUE_ID_NOT_BY_CARD ) = =
UNIQUE_ID_NOT_BY_CARD )
return - 1 ;
iob = qeth_get_ipacmd_buffer ( card , IPA_CMD_DESTROY_ADDR ,
QETH_PROT_IPV6 ) ;
cmd = ( struct qeth_ipa_cmd * ) ( iob - > data + IPA_PDU_HEADER_SIZE ) ;
* ( ( __u16 * ) & cmd - > data . create_destroy_addr . unique_id [ 6 ] ) =
card - > info . unique_id ;
memcpy ( & cmd - > data . create_destroy_addr . unique_id [ 0 ] ,
card - > dev - > dev_addr , OSA_ADDR_LEN ) ;
rc = qeth_send_ipa_cmd ( card , iob , NULL , NULL ) ;
# else
card - > info . unique_id = UNIQUE_ID_IF_CREATE_ADDR_FAILED |
UNIQUE_ID_NOT_BY_CARD ;
# endif
return rc ;
}
/**
* Clear IP List
*/
static void
qeth_clear_ip_list ( struct qeth_card * card , int clean , int recover )
{
struct qeth_ipaddr * addr , * tmp ;
unsigned long flags ;
QETH_DBF_TEXT ( trace , 4 , " clearip " ) ;
spin_lock_irqsave ( & card - > ip_lock , flags ) ;
/* clear todo list */
list_for_each_entry_safe ( addr , tmp , card - > ip_tbd_list , entry ) {
list_del ( & addr - > entry ) ;
kfree ( addr ) ;
}
while ( ! list_empty ( & card - > ip_list ) ) {
addr = list_entry ( card - > ip_list . next ,
struct qeth_ipaddr , entry ) ;
list_del_init ( & addr - > entry ) ;
if ( clean ) {
spin_unlock_irqrestore ( & card - > ip_lock , flags ) ;
qeth_deregister_addr_entry ( card , addr ) ;
spin_lock_irqsave ( & card - > ip_lock , flags ) ;
}
if ( ! recover | | addr - > is_multicast ) {
kfree ( addr ) ;
continue ;
}
list_add_tail ( & addr - > entry , card - > ip_tbd_list ) ;
}
spin_unlock_irqrestore ( & card - > ip_lock , flags ) ;
}
static void
qeth_set_allowed_threads ( struct qeth_card * card , unsigned long threads ,
int clear_start_mask )
{
unsigned long flags ;
spin_lock_irqsave ( & card - > thread_mask_lock , flags ) ;
card - > thread_allowed_mask = threads ;
if ( clear_start_mask )
card - > thread_start_mask & = threads ;
spin_unlock_irqrestore ( & card - > thread_mask_lock , flags ) ;
wake_up ( & card - > wait_q ) ;
}
2007-02-05 23:18:53 +03:00
static int
2005-04-17 02:20:36 +04:00
qeth_threads_running ( struct qeth_card * card , unsigned long threads )
{
unsigned long flags ;
int rc = 0 ;
spin_lock_irqsave ( & card - > thread_mask_lock , flags ) ;
rc = ( card - > thread_running_mask & threads ) ;
spin_unlock_irqrestore ( & card - > thread_mask_lock , flags ) ;
return rc ;
}
static int
qeth_wait_for_threads ( struct qeth_card * card , unsigned long threads )
{
return wait_event_interruptible ( card - > wait_q ,
qeth_threads_running ( card , threads ) = = 0 ) ;
}
static int
2005-05-12 22:39:09 +04:00
qeth_stop_card ( struct qeth_card * card , int recovery_mode )
2005-04-17 02:20:36 +04:00
{
int rc = 0 ;
QETH_DBF_TEXT ( setup , 2 , " stopcard " ) ;
QETH_DBF_HEX ( setup , 2 , & card , sizeof ( void * ) ) ;
qeth_set_allowed_threads ( card , 0 , 1 ) ;
if ( qeth_wait_for_threads ( card , ~ QETH_RECOVER_THREAD ) )
return - ERESTARTSYS ;
if ( card - > read . state = = CH_STATE_UP & &
card - > write . state = = CH_STATE_UP & &
( card - > state = = CARD_STATE_UP ) ) {
2006-05-27 05:58:38 +04:00
if ( recovery_mode & &
2005-09-30 12:19:19 +04:00
card - > info . type ! = QETH_CARD_TYPE_OSN ) {
2005-05-12 22:39:09 +04:00
qeth_stop ( card - > dev ) ;
} else {
rtnl_lock ( ) ;
dev_close ( card - > dev ) ;
rtnl_unlock ( ) ;
}
2005-04-17 02:20:36 +04:00
if ( ! card - > use_hard_stop ) {
__u8 * mac = & card - > dev - > dev_addr [ 0 ] ;
rc = qeth_layer2_send_delmac ( card , mac ) ;
QETH_DBF_TEXT_ ( setup , 2 , " Lerr%d " , rc ) ;
if ( ( rc = qeth_send_stoplan ( card ) ) )
QETH_DBF_TEXT_ ( setup , 2 , " 1err%d " , rc ) ;
}
card - > state = CARD_STATE_SOFTSETUP ;
}
if ( card - > state = = CARD_STATE_SOFTSETUP ) {
# ifdef CONFIG_QETH_VLAN
if ( card - > options . layer2 )
qeth_layer2_process_vlans ( card , 1 ) ;
# endif
qeth_clear_ip_list ( card , ! card - > use_hard_stop , 1 ) ;
qeth_clear_ipacmd_list ( card ) ;
card - > state = CARD_STATE_HARDSETUP ;
}
if ( card - > state = = CARD_STATE_HARDSETUP ) {
if ( ( ! card - > use_hard_stop ) & &
( ! card - > options . layer2 ) )
if ( ( rc = qeth_put_unique_id ( card ) ) )
QETH_DBF_TEXT_ ( setup , 2 , " 2err%d " , rc ) ;
qeth_qdio_clear_card ( card , 0 ) ;
qeth_clear_qdio_buffers ( card ) ;
qeth_clear_working_pool_list ( card ) ;
card - > state = CARD_STATE_DOWN ;
}
if ( card - > state = = CARD_STATE_DOWN ) {
qeth_clear_cmd_buffers ( & card - > read ) ;
qeth_clear_cmd_buffers ( & card - > write ) ;
}
card - > use_hard_stop = 0 ;
return rc ;
}
static int
qeth_get_unique_id ( struct qeth_card * card )
{
int rc = 0 ;
# ifdef CONFIG_QETH_IPV6
struct qeth_cmd_buffer * iob ;
struct qeth_ipa_cmd * cmd ;
QETH_DBF_TEXT ( setup , 2 , " guniqeid " ) ;
if ( ! qeth_is_supported ( card , IPA_IPV6 ) ) {
card - > info . unique_id = UNIQUE_ID_IF_CREATE_ADDR_FAILED |
UNIQUE_ID_NOT_BY_CARD ;
return 0 ;
}
iob = qeth_get_ipacmd_buffer ( card , IPA_CMD_CREATE_ADDR ,
QETH_PROT_IPV6 ) ;
cmd = ( struct qeth_ipa_cmd * ) ( iob - > data + IPA_PDU_HEADER_SIZE ) ;
* ( ( __u16 * ) & cmd - > data . create_destroy_addr . unique_id [ 6 ] ) =
card - > info . unique_id ;
rc = qeth_send_ipa_cmd ( card , iob , qeth_get_unique_id_cb , NULL ) ;
# else
card - > info . unique_id = UNIQUE_ID_IF_CREATE_ADDR_FAILED |
UNIQUE_ID_NOT_BY_CARD ;
# endif
return rc ;
}
static void
qeth_print_status_with_portname ( struct qeth_card * card )
{
char dbf_text [ 15 ] ;
int i ;
sprintf ( dbf_text , " %s " , card - > info . portname + 1 ) ;
for ( i = 0 ; i < 8 ; i + + )
dbf_text [ i ] =
( char ) _ebcasc [ ( __u8 ) dbf_text [ i ] ] ;
dbf_text [ 8 ] = 0 ;
printk ( " qeth: Device %s/%s/%s is a%s card%s%s%s \n "
" with link type %s (portname: %s) \n " ,
CARD_RDEV_ID ( card ) ,
CARD_WDEV_ID ( card ) ,
CARD_DDEV_ID ( card ) ,
qeth_get_cardname ( card ) ,
( card - > info . mcl_level [ 0 ] ) ? " (level: " : " " ,
( card - > info . mcl_level [ 0 ] ) ? card - > info . mcl_level : " " ,
( card - > info . mcl_level [ 0 ] ) ? " ) " : " " ,
qeth_get_cardname_short ( card ) ,
dbf_text ) ;
}
static void
qeth_print_status_no_portname ( struct qeth_card * card )
{
if ( card - > info . portname [ 0 ] )
printk ( " qeth: Device %s/%s/%s is a%s "
" card%s%s%s \n with link type %s "
" (no portname needed by interface). \n " ,
CARD_RDEV_ID ( card ) ,
CARD_WDEV_ID ( card ) ,
CARD_DDEV_ID ( card ) ,
qeth_get_cardname ( card ) ,
( card - > info . mcl_level [ 0 ] ) ? " (level: " : " " ,
( card - > info . mcl_level [ 0 ] ) ? card - > info . mcl_level : " " ,
( card - > info . mcl_level [ 0 ] ) ? " ) " : " " ,
qeth_get_cardname_short ( card ) ) ;
else
printk ( " qeth: Device %s/%s/%s is a%s "
" card%s%s%s \n with link type %s. \n " ,
CARD_RDEV_ID ( card ) ,
CARD_WDEV_ID ( card ) ,
CARD_DDEV_ID ( card ) ,
qeth_get_cardname ( card ) ,
( card - > info . mcl_level [ 0 ] ) ? " (level: " : " " ,
( card - > info . mcl_level [ 0 ] ) ? card - > info . mcl_level : " " ,
( card - > info . mcl_level [ 0 ] ) ? " ) " : " " ,
qeth_get_cardname_short ( card ) ) ;
}
static void
qeth_print_status_message ( struct qeth_card * card )
{
switch ( card - > info . type ) {
case QETH_CARD_TYPE_OSAE :
/* VM will use a non-zero first character
* to indicate a HiperSockets like reporting
* of the level OSA sets the first character to zero
* */
if ( ! card - > info . mcl_level [ 0 ] ) {
sprintf ( card - > info . mcl_level , " %02x%02x " ,
card - > info . mcl_level [ 2 ] ,
card - > info . mcl_level [ 3 ] ) ;
card - > info . mcl_level [ QETH_MCL_LENGTH ] = 0 ;
break ;
}
/* fallthrough */
case QETH_CARD_TYPE_IQD :
card - > info . mcl_level [ 0 ] = ( char ) _ebcasc [ ( __u8 )
card - > info . mcl_level [ 0 ] ] ;
card - > info . mcl_level [ 1 ] = ( char ) _ebcasc [ ( __u8 )
card - > info . mcl_level [ 1 ] ] ;
card - > info . mcl_level [ 2 ] = ( char ) _ebcasc [ ( __u8 )
card - > info . mcl_level [ 2 ] ] ;
card - > info . mcl_level [ 3 ] = ( char ) _ebcasc [ ( __u8 )
card - > info . mcl_level [ 3 ] ] ;
card - > info . mcl_level [ QETH_MCL_LENGTH ] = 0 ;
break ;
default :
memset ( & card - > info . mcl_level [ 0 ] , 0 , QETH_MCL_LENGTH + 1 ) ;
}
if ( card - > info . portname_required )
qeth_print_status_with_portname ( card ) ;
else
qeth_print_status_no_portname ( card ) ;
}
static int
qeth_register_netdev ( struct qeth_card * card )
{
QETH_DBF_TEXT ( setup , 3 , " regnetd " ) ;
2006-05-27 05:58:38 +04:00
if ( card - > dev - > reg_state ! = NETREG_UNINITIALIZED )
2005-04-17 02:20:36 +04:00
return 0 ;
/* sysfs magic */
SET_NETDEV_DEV ( card - > dev , & card - > gdev - > dev ) ;
return register_netdev ( card - > dev ) ;
}
static void
2005-05-12 22:39:09 +04:00
qeth_start_again ( struct qeth_card * card , int recovery_mode )
2005-04-17 02:20:36 +04:00
{
QETH_DBF_TEXT ( setup , 2 , " startag " ) ;
2006-05-27 05:58:38 +04:00
if ( recovery_mode & &
2005-09-30 12:19:19 +04:00
card - > info . type ! = QETH_CARD_TYPE_OSN ) {
2005-05-12 22:39:09 +04:00
qeth_open ( card - > dev ) ;
} else {
rtnl_lock ( ) ;
dev_open ( card - > dev ) ;
rtnl_unlock ( ) ;
}
2005-04-17 02:20:36 +04:00
/* this also sets saved unicast addresses */
qeth_set_multicast_list ( card - > dev ) ;
}
/* Layer 2 specific stuff */
# define IGNORE_PARAM_EQ(option,value,reset_value,msg) \
if ( card - > options . option = = value ) { \
PRINT_ERR ( " %s not supported with layer 2 " \
" functionality, ignoring option on read " \
" channel device %s . \n " , msg , CARD_RDEV_ID ( card ) ) ; \
card - > options . option = reset_value ; \
}
# define IGNORE_PARAM_NEQ(option,value,reset_value,msg) \
if ( card - > options . option ! = value ) { \
PRINT_ERR ( " %s not supported with layer 2 " \
" functionality, ignoring option on read " \
" channel device %s . \n " , msg , CARD_RDEV_ID ( card ) ) ; \
card - > options . option = reset_value ; \
}
static void qeth_make_parameters_consistent ( struct qeth_card * card )
{
2005-09-30 12:19:19 +04:00
if ( card - > options . layer2 = = 0 )
return ;
if ( card - > info . type = = QETH_CARD_TYPE_OSN )
return ;
if ( card - > info . type = = QETH_CARD_TYPE_IQD ) {
PRINT_ERR ( " Device %s does not support layer 2 functionality. " \
" Ignoring layer2 option. \n " , CARD_BUS_ID ( card ) ) ;
card - > options . layer2 = 0 ;
return ;
}
IGNORE_PARAM_NEQ ( route4 . type , NO_ROUTER , NO_ROUTER ,
" Routing options are " ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_QETH_IPV6
2005-09-30 12:19:19 +04:00
IGNORE_PARAM_NEQ ( route6 . type , NO_ROUTER , NO_ROUTER ,
" Routing options are " ) ;
2005-04-17 02:20:36 +04:00
# endif
2005-09-30 12:19:19 +04:00
IGNORE_PARAM_EQ ( checksum_type , HW_CHECKSUMMING ,
QETH_CHECKSUM_DEFAULT ,
" Checksumming options are " ) ;
IGNORE_PARAM_NEQ ( broadcast_mode , QETH_TR_BROADCAST_ALLRINGS ,
QETH_TR_BROADCAST_ALLRINGS ,
" Broadcast mode options are " ) ;
IGNORE_PARAM_NEQ ( macaddr_mode , QETH_TR_MACADDR_NONCANONICAL ,
QETH_TR_MACADDR_NONCANONICAL ,
" Canonical MAC addr options are " ) ;
IGNORE_PARAM_NEQ ( fake_broadcast , 0 , 0 ,
" Broadcast faking options are " ) ;
IGNORE_PARAM_NEQ ( add_hhlen , DEFAULT_ADD_HHLEN ,
DEFAULT_ADD_HHLEN , " Option add_hhlen is " ) ;
IGNORE_PARAM_NEQ ( fake_ll , 0 , 0 , " Option fake_ll is " ) ;
2005-04-17 02:20:36 +04:00
}
static int
2005-05-12 22:39:09 +04:00
__qeth_set_online ( struct ccwgroup_device * gdev , int recovery_mode )
2005-04-17 02:20:36 +04:00
{
struct qeth_card * card = gdev - > dev . driver_data ;
int rc = 0 ;
enum qeth_card_states recover_flag ;
BUG_ON ( ! card ) ;
QETH_DBF_TEXT ( setup , 2 , " setonlin " ) ;
QETH_DBF_HEX ( setup , 2 , & card , sizeof ( void * ) ) ;
qeth_set_allowed_threads ( card , QETH_RECOVER_THREAD , 1 ) ;
if ( qeth_wait_for_threads ( card , ~ QETH_RECOVER_THREAD ) ) {
PRINT_WARN ( " set_online of card %s interrupted by user! \n " ,
CARD_BUS_ID ( card ) ) ;
return - ERESTARTSYS ;
}
recover_flag = card - > state ;
if ( ( rc = ccw_device_set_online ( CARD_RDEV ( card ) ) ) | |
( rc = ccw_device_set_online ( CARD_WDEV ( card ) ) ) | |
( rc = ccw_device_set_online ( CARD_DDEV ( card ) ) ) ) {
QETH_DBF_TEXT_ ( setup , 2 , " 1err%d " , rc ) ;
return - EIO ;
}
2005-09-30 12:19:19 +04:00
qeth_make_parameters_consistent ( card ) ;
2005-04-17 02:20:36 +04:00
if ( ( rc = qeth_hardsetup_card ( card ) ) ) {
QETH_DBF_TEXT_ ( setup , 2 , " 2err%d " , rc ) ;
goto out_remove ;
}
card - > state = CARD_STATE_HARDSETUP ;
if ( ! ( rc = qeth_query_ipassists ( card , QETH_PROT_IPV4 ) ) )
rc = qeth_get_unique_id ( card ) ;
if ( rc & & card - > options . layer2 = = 0 ) {
QETH_DBF_TEXT_ ( setup , 2 , " 3err%d " , rc ) ;
goto out_remove ;
}
qeth_print_status_message ( card ) ;
if ( ( rc = qeth_register_netdev ( card ) ) ) {
QETH_DBF_TEXT_ ( setup , 2 , " 4err%d " , rc ) ;
goto out_remove ;
}
if ( ( rc = qeth_softsetup_card ( card ) ) ) {
QETH_DBF_TEXT_ ( setup , 2 , " 5err%d " , rc ) ;
goto out_remove ;
}
if ( ( rc = qeth_init_qdio_queues ( card ) ) ) {
QETH_DBF_TEXT_ ( setup , 2 , " 6err%d " , rc ) ;
goto out_remove ;
}
2006-09-15 18:26:34 +04:00
card - > state = CARD_STATE_SOFTSETUP ;
2005-12-13 10:22:30 +03:00
netif_carrier_on ( card - > dev ) ;
2005-04-17 02:20:36 +04:00
qeth_set_allowed_threads ( card , 0xffffffff , 0 ) ;
if ( recover_flag = = CARD_STATE_RECOVER )
2005-05-12 22:39:09 +04:00
qeth_start_again ( card , recovery_mode ) ;
2005-04-17 02:20:36 +04:00
qeth_notify_processes ( ) ;
return 0 ;
out_remove :
card - > use_hard_stop = 1 ;
2005-05-12 22:39:09 +04:00
qeth_stop_card ( card , 0 ) ;
2005-04-17 02:20:36 +04:00
ccw_device_set_offline ( CARD_DDEV ( card ) ) ;
ccw_device_set_offline ( CARD_WDEV ( card ) ) ;
ccw_device_set_offline ( CARD_RDEV ( card ) ) ;
if ( recover_flag = = CARD_STATE_RECOVER )
card - > state = CARD_STATE_RECOVER ;
else
card - > state = CARD_STATE_DOWN ;
return - ENODEV ;
}
2005-05-12 22:39:09 +04:00
static int
qeth_set_online ( struct ccwgroup_device * gdev )
{
return __qeth_set_online ( gdev , 0 ) ;
}
2005-04-17 02:20:36 +04:00
static struct ccw_device_id qeth_ids [ ] = {
2006-07-12 18:41:55 +04:00
{ CCW_DEVICE ( 0x1731 , 0x01 ) , . driver_info = QETH_CARD_TYPE_OSAE } ,
{ CCW_DEVICE ( 0x1731 , 0x05 ) , . driver_info = QETH_CARD_TYPE_IQD } ,
{ CCW_DEVICE ( 0x1731 , 0x06 ) , . driver_info = QETH_CARD_TYPE_OSN } ,
2005-04-17 02:20:36 +04:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( ccw , qeth_ids ) ;
struct device * qeth_root_dev = NULL ;
struct ccwgroup_driver qeth_ccwgroup_driver = {
. owner = THIS_MODULE ,
. name = " qeth " ,
. driver_id = 0xD8C5E3C8 ,
. probe = qeth_probe_device ,
. remove = qeth_remove_device ,
. set_online = qeth_set_online ,
. set_offline = qeth_set_offline ,
} ;
struct ccw_driver qeth_ccw_driver = {
. name = " qeth " ,
. ids = qeth_ids ,
. probe = ccwgroup_probe_ccwdev ,
. remove = ccwgroup_remove_ccwdev ,
} ;
static void
qeth_unregister_dbf_views ( void )
{
if ( qeth_dbf_setup )
debug_unregister ( qeth_dbf_setup ) ;
if ( qeth_dbf_qerr )
debug_unregister ( qeth_dbf_qerr ) ;
if ( qeth_dbf_sense )
debug_unregister ( qeth_dbf_sense ) ;
if ( qeth_dbf_misc )
debug_unregister ( qeth_dbf_misc ) ;
if ( qeth_dbf_data )
debug_unregister ( qeth_dbf_data ) ;
if ( qeth_dbf_control )
debug_unregister ( qeth_dbf_control ) ;
if ( qeth_dbf_trace )
debug_unregister ( qeth_dbf_trace ) ;
}
static int
qeth_register_dbf_views ( void )
{
qeth_dbf_setup = debug_register ( QETH_DBF_SETUP_NAME ,
2005-06-26 01:55:33 +04:00
QETH_DBF_SETUP_PAGES ,
2005-04-17 02:20:36 +04:00
QETH_DBF_SETUP_NR_AREAS ,
QETH_DBF_SETUP_LEN ) ;
qeth_dbf_misc = debug_register ( QETH_DBF_MISC_NAME ,
2005-06-26 01:55:33 +04:00
QETH_DBF_MISC_PAGES ,
2005-04-17 02:20:36 +04:00
QETH_DBF_MISC_NR_AREAS ,
QETH_DBF_MISC_LEN ) ;
qeth_dbf_data = debug_register ( QETH_DBF_DATA_NAME ,
2005-06-26 01:55:33 +04:00
QETH_DBF_DATA_PAGES ,
2005-04-17 02:20:36 +04:00
QETH_DBF_DATA_NR_AREAS ,
QETH_DBF_DATA_LEN ) ;
qeth_dbf_control = debug_register ( QETH_DBF_CONTROL_NAME ,
2005-06-26 01:55:33 +04:00
QETH_DBF_CONTROL_PAGES ,
2005-04-17 02:20:36 +04:00
QETH_DBF_CONTROL_NR_AREAS ,
QETH_DBF_CONTROL_LEN ) ;
qeth_dbf_sense = debug_register ( QETH_DBF_SENSE_NAME ,
2005-06-26 01:55:33 +04:00
QETH_DBF_SENSE_PAGES ,
2005-04-17 02:20:36 +04:00
QETH_DBF_SENSE_NR_AREAS ,
QETH_DBF_SENSE_LEN ) ;
qeth_dbf_qerr = debug_register ( QETH_DBF_QERR_NAME ,
2005-06-26 01:55:33 +04:00
QETH_DBF_QERR_PAGES ,
2005-04-17 02:20:36 +04:00
QETH_DBF_QERR_NR_AREAS ,
QETH_DBF_QERR_LEN ) ;
qeth_dbf_trace = debug_register ( QETH_DBF_TRACE_NAME ,
2005-06-26 01:55:33 +04:00
QETH_DBF_TRACE_PAGES ,
2005-04-17 02:20:36 +04:00
QETH_DBF_TRACE_NR_AREAS ,
QETH_DBF_TRACE_LEN ) ;
if ( ( qeth_dbf_setup = = NULL ) | | ( qeth_dbf_misc = = NULL ) | |
( qeth_dbf_data = = NULL ) | | ( qeth_dbf_control = = NULL ) | |
( qeth_dbf_sense = = NULL ) | | ( qeth_dbf_qerr = = NULL ) | |
( qeth_dbf_trace = = NULL ) ) {
qeth_unregister_dbf_views ( ) ;
return - ENOMEM ;
}
debug_register_view ( qeth_dbf_setup , & debug_hex_ascii_view ) ;
debug_set_level ( qeth_dbf_setup , QETH_DBF_SETUP_LEVEL ) ;
debug_register_view ( qeth_dbf_misc , & debug_hex_ascii_view ) ;
debug_set_level ( qeth_dbf_misc , QETH_DBF_MISC_LEVEL ) ;
debug_register_view ( qeth_dbf_data , & debug_hex_ascii_view ) ;
debug_set_level ( qeth_dbf_data , QETH_DBF_DATA_LEVEL ) ;
debug_register_view ( qeth_dbf_control , & debug_hex_ascii_view ) ;
debug_set_level ( qeth_dbf_control , QETH_DBF_CONTROL_LEVEL ) ;
debug_register_view ( qeth_dbf_sense , & debug_hex_ascii_view ) ;
debug_set_level ( qeth_dbf_sense , QETH_DBF_SENSE_LEVEL ) ;
debug_register_view ( qeth_dbf_qerr , & debug_hex_ascii_view ) ;
debug_set_level ( qeth_dbf_qerr , QETH_DBF_QERR_LEVEL ) ;
debug_register_view ( qeth_dbf_trace , & debug_hex_ascii_view ) ;
debug_set_level ( qeth_dbf_trace , QETH_DBF_TRACE_LEVEL ) ;
return 0 ;
}
# ifdef CONFIG_QETH_IPV6
extern struct neigh_table arp_tbl ;
static struct neigh_ops * arp_direct_ops ;
static int ( * qeth_old_arp_constructor ) ( struct neighbour * ) ;
static struct neigh_ops arp_direct_ops_template = {
. family = AF_INET ,
. solicit = NULL ,
. error_report = NULL ,
. output = dev_queue_xmit ,
. connected_output = dev_queue_xmit ,
. hh_output = dev_queue_xmit ,
. queue_xmit = dev_queue_xmit
} ;
static int
qeth_arp_constructor ( struct neighbour * neigh )
{
struct net_device * dev = neigh - > dev ;
struct in_device * in_dev ;
struct neigh_parms * parms ;
struct qeth_card * card ;
card = qeth_get_card_from_dev ( dev ) ;
if ( card = = NULL )
goto out ;
if ( ( card - > options . layer2 ) | |
( card - > dev - > hard_header = = qeth_fake_header ) )
goto out ;
rcu_read_lock ( ) ;
2005-10-04 01:35:55 +04:00
in_dev = __in_dev_get_rcu ( dev ) ;
2005-04-17 02:20:36 +04:00
if ( in_dev = = NULL ) {
rcu_read_unlock ( ) ;
return - EINVAL ;
}
parms = in_dev - > arp_parms ;
__neigh_parms_put ( neigh - > parms ) ;
neigh - > parms = neigh_parms_clone ( parms ) ;
rcu_read_unlock ( ) ;
2006-09-27 09:17:51 +04:00
neigh - > type = inet_addr_type ( * ( __be32 * ) neigh - > primary_key ) ;
2005-04-17 02:20:36 +04:00
neigh - > nud_state = NUD_NOARP ;
neigh - > ops = arp_direct_ops ;
neigh - > output = neigh - > ops - > queue_xmit ;
return 0 ;
out :
return qeth_old_arp_constructor ( neigh ) ;
}
# endif /*CONFIG_QETH_IPV6*/
/*
* IP address takeover related functions
*/
static void
qeth_clear_ipato_list ( struct qeth_card * card )
{
struct qeth_ipato_entry * ipatoe , * tmp ;
unsigned long flags ;
spin_lock_irqsave ( & card - > ip_lock , flags ) ;
list_for_each_entry_safe ( ipatoe , tmp , & card - > ipato . entries , entry ) {
list_del ( & ipatoe - > entry ) ;
kfree ( ipatoe ) ;
}
spin_unlock_irqrestore ( & card - > ip_lock , flags ) ;
}
int
qeth_add_ipato_entry ( struct qeth_card * card , struct qeth_ipato_entry * new )
{
struct qeth_ipato_entry * ipatoe ;
unsigned long flags ;
int rc = 0 ;
QETH_DBF_TEXT ( trace , 2 , " addipato " ) ;
spin_lock_irqsave ( & card - > ip_lock , flags ) ;
list_for_each_entry ( ipatoe , & card - > ipato . entries , entry ) {
if ( ipatoe - > proto ! = new - > proto )
continue ;
if ( ! memcmp ( ipatoe - > addr , new - > addr ,
( ipatoe - > proto = = QETH_PROT_IPV4 ) ? 4 : 16 ) & &
( ipatoe - > mask_bits = = new - > mask_bits ) ) {
PRINT_WARN ( " ipato entry already exists! \n " ) ;
rc = - EEXIST ;
break ;
}
}
if ( ! rc ) {
list_add_tail ( & new - > entry , & card - > ipato . entries ) ;
}
spin_unlock_irqrestore ( & card - > ip_lock , flags ) ;
return rc ;
}
void
qeth_del_ipato_entry ( struct qeth_card * card , enum qeth_prot_versions proto ,
u8 * addr , int mask_bits )
{
struct qeth_ipato_entry * ipatoe , * tmp ;
unsigned long flags ;
QETH_DBF_TEXT ( trace , 2 , " delipato " ) ;
spin_lock_irqsave ( & card - > ip_lock , flags ) ;
list_for_each_entry_safe ( ipatoe , tmp , & card - > ipato . entries , entry ) {
if ( ipatoe - > proto ! = proto )
continue ;
if ( ! memcmp ( ipatoe - > addr , addr ,
( proto = = QETH_PROT_IPV4 ) ? 4 : 16 ) & &
( ipatoe - > mask_bits = = mask_bits ) ) {
list_del ( & ipatoe - > entry ) ;
kfree ( ipatoe ) ;
}
}
spin_unlock_irqrestore ( & card - > ip_lock , flags ) ;
}
2007-02-05 23:18:53 +03:00
static void
2005-04-17 02:20:36 +04:00
qeth_convert_addr_to_bits ( u8 * addr , u8 * bits , int len )
{
int i , j ;
u8 octet ;
for ( i = 0 ; i < len ; + + i ) {
octet = addr [ i ] ;
for ( j = 7 ; j > = 0 ; - - j ) {
bits [ i * 8 + j ] = octet & 1 ;
octet > > = 1 ;
}
}
}
static int
qeth_is_addr_covered_by_ipato ( struct qeth_card * card , struct qeth_ipaddr * addr )
{
struct qeth_ipato_entry * ipatoe ;
u8 addr_bits [ 128 ] = { 0 , } ;
u8 ipatoe_bits [ 128 ] = { 0 , } ;
int rc = 0 ;
if ( ! card - > ipato . enabled )
return 0 ;
qeth_convert_addr_to_bits ( ( u8 * ) & addr - > u , addr_bits ,
( addr - > proto = = QETH_PROT_IPV4 ) ? 4 : 16 ) ;
list_for_each_entry ( ipatoe , & card - > ipato . entries , entry ) {
if ( addr - > proto ! = ipatoe - > proto )
continue ;
qeth_convert_addr_to_bits ( ipatoe - > addr , ipatoe_bits ,
( ipatoe - > proto = = QETH_PROT_IPV4 ) ?
4 : 16 ) ;
if ( addr - > proto = = QETH_PROT_IPV4 )
rc = ! memcmp ( addr_bits , ipatoe_bits ,
min ( 32 , ipatoe - > mask_bits ) ) ;
else
rc = ! memcmp ( addr_bits , ipatoe_bits ,
min ( 128 , ipatoe - > mask_bits ) ) ;
if ( rc )
break ;
}
/* invert? */
if ( ( addr - > proto = = QETH_PROT_IPV4 ) & & card - > ipato . invert4 )
rc = ! rc ;
else if ( ( addr - > proto = = QETH_PROT_IPV6 ) & & card - > ipato . invert6 )
rc = ! rc ;
return rc ;
}
/*
* VIPA related functions
*/
int
qeth_add_vipa ( struct qeth_card * card , enum qeth_prot_versions proto ,
const u8 * addr )
{
struct qeth_ipaddr * ipaddr ;
unsigned long flags ;
int rc = 0 ;
ipaddr = qeth_get_addr_buffer ( proto ) ;
if ( ipaddr ) {
if ( proto = = QETH_PROT_IPV4 ) {
QETH_DBF_TEXT ( trace , 2 , " addvipa4 " ) ;
memcpy ( & ipaddr - > u . a4 . addr , addr , 4 ) ;
ipaddr - > u . a4 . mask = 0 ;
# ifdef CONFIG_QETH_IPV6
} else if ( proto = = QETH_PROT_IPV6 ) {
QETH_DBF_TEXT ( trace , 2 , " addvipa6 " ) ;
memcpy ( & ipaddr - > u . a6 . addr , addr , 16 ) ;
ipaddr - > u . a6 . pfxlen = 0 ;
# endif
}
ipaddr - > type = QETH_IP_TYPE_VIPA ;
ipaddr - > set_flags = QETH_IPA_SETIP_VIPA_FLAG ;
ipaddr - > del_flags = QETH_IPA_DELIP_VIPA_FLAG ;
} else
return - ENOMEM ;
spin_lock_irqsave ( & card - > ip_lock , flags ) ;
if ( __qeth_address_exists_in_list ( & card - > ip_list , ipaddr , 0 ) | |
__qeth_address_exists_in_list ( card - > ip_tbd_list , ipaddr , 0 ) )
rc = - EEXIST ;
spin_unlock_irqrestore ( & card - > ip_lock , flags ) ;
if ( rc ) {
PRINT_WARN ( " Cannot add VIPA. Address already exists! \n " ) ;
return rc ;
}
if ( ! qeth_add_ip ( card , ipaddr ) )
kfree ( ipaddr ) ;
2007-01-08 19:30:11 +03:00
qeth_set_ip_addr_list ( card ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
void
qeth_del_vipa ( struct qeth_card * card , enum qeth_prot_versions proto ,
const u8 * addr )
{
struct qeth_ipaddr * ipaddr ;
ipaddr = qeth_get_addr_buffer ( proto ) ;
if ( ipaddr ) {
if ( proto = = QETH_PROT_IPV4 ) {
QETH_DBF_TEXT ( trace , 2 , " delvipa4 " ) ;
memcpy ( & ipaddr - > u . a4 . addr , addr , 4 ) ;
ipaddr - > u . a4 . mask = 0 ;
# ifdef CONFIG_QETH_IPV6
} else if ( proto = = QETH_PROT_IPV6 ) {
QETH_DBF_TEXT ( trace , 2 , " delvipa6 " ) ;
memcpy ( & ipaddr - > u . a6 . addr , addr , 16 ) ;
ipaddr - > u . a6 . pfxlen = 0 ;
# endif
}
ipaddr - > type = QETH_IP_TYPE_VIPA ;
} else
return ;
if ( ! qeth_delete_ip ( card , ipaddr ) )
kfree ( ipaddr ) ;
2007-01-08 19:30:11 +03:00
qeth_set_ip_addr_list ( card ) ;
2005-04-17 02:20:36 +04:00
}
/*
* proxy ARP related functions
*/
int
qeth_add_rxip ( struct qeth_card * card , enum qeth_prot_versions proto ,
const u8 * addr )
{
struct qeth_ipaddr * ipaddr ;
unsigned long flags ;
int rc = 0 ;
ipaddr = qeth_get_addr_buffer ( proto ) ;
if ( ipaddr ) {
if ( proto = = QETH_PROT_IPV4 ) {
QETH_DBF_TEXT ( trace , 2 , " addrxip4 " ) ;
memcpy ( & ipaddr - > u . a4 . addr , addr , 4 ) ;
ipaddr - > u . a4 . mask = 0 ;
# ifdef CONFIG_QETH_IPV6
} else if ( proto = = QETH_PROT_IPV6 ) {
QETH_DBF_TEXT ( trace , 2 , " addrxip6 " ) ;
memcpy ( & ipaddr - > u . a6 . addr , addr , 16 ) ;
ipaddr - > u . a6 . pfxlen = 0 ;
# endif
}
ipaddr - > type = QETH_IP_TYPE_RXIP ;
ipaddr - > set_flags = QETH_IPA_SETIP_TAKEOVER_FLAG ;
ipaddr - > del_flags = 0 ;
} else
return - ENOMEM ;
spin_lock_irqsave ( & card - > ip_lock , flags ) ;
if ( __qeth_address_exists_in_list ( & card - > ip_list , ipaddr , 0 ) | |
__qeth_address_exists_in_list ( card - > ip_tbd_list , ipaddr , 0 ) )
rc = - EEXIST ;
spin_unlock_irqrestore ( & card - > ip_lock , flags ) ;
if ( rc ) {
PRINT_WARN ( " Cannot add RXIP. Address already exists! \n " ) ;
return rc ;
}
if ( ! qeth_add_ip ( card , ipaddr ) )
kfree ( ipaddr ) ;
2007-01-08 19:30:11 +03:00
qeth_set_ip_addr_list ( card ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
void
qeth_del_rxip ( struct qeth_card * card , enum qeth_prot_versions proto ,
const u8 * addr )
{
struct qeth_ipaddr * ipaddr ;
ipaddr = qeth_get_addr_buffer ( proto ) ;
if ( ipaddr ) {
if ( proto = = QETH_PROT_IPV4 ) {
QETH_DBF_TEXT ( trace , 2 , " addrxip4 " ) ;
memcpy ( & ipaddr - > u . a4 . addr , addr , 4 ) ;
ipaddr - > u . a4 . mask = 0 ;
# ifdef CONFIG_QETH_IPV6
} else if ( proto = = QETH_PROT_IPV6 ) {
QETH_DBF_TEXT ( trace , 2 , " addrxip6 " ) ;
memcpy ( & ipaddr - > u . a6 . addr , addr , 16 ) ;
ipaddr - > u . a6 . pfxlen = 0 ;
# endif
}
ipaddr - > type = QETH_IP_TYPE_RXIP ;
} else
return ;
if ( ! qeth_delete_ip ( card , ipaddr ) )
kfree ( ipaddr ) ;
2007-01-08 19:30:11 +03:00
qeth_set_ip_addr_list ( card ) ;
2005-04-17 02:20:36 +04:00
}
/**
* IP event handler
*/
static int
qeth_ip_event ( struct notifier_block * this ,
unsigned long event , void * ptr )
{
struct in_ifaddr * ifa = ( struct in_ifaddr * ) ptr ;
struct net_device * dev = ( struct net_device * ) ifa - > ifa_dev - > dev ;
struct qeth_ipaddr * addr ;
struct qeth_card * card ;
QETH_DBF_TEXT ( trace , 3 , " ipevent " ) ;
card = qeth_get_card_from_dev ( dev ) ;
if ( ! card )
return NOTIFY_DONE ;
if ( card - > options . layer2 )
return NOTIFY_DONE ;
addr = qeth_get_addr_buffer ( QETH_PROT_IPV4 ) ;
if ( addr ! = NULL ) {
addr - > u . a4 . addr = ifa - > ifa_address ;
addr - > u . a4 . mask = ifa - > ifa_mask ;
addr - > type = QETH_IP_TYPE_NORMAL ;
} else
goto out ;
switch ( event ) {
case NETDEV_UP :
if ( ! qeth_add_ip ( card , addr ) )
kfree ( addr ) ;
break ;
case NETDEV_DOWN :
if ( ! qeth_delete_ip ( card , addr ) )
kfree ( addr ) ;
break ;
default :
break ;
}
2007-01-08 19:30:11 +03:00
qeth_set_ip_addr_list ( card ) ;
2005-04-17 02:20:36 +04:00
out :
return NOTIFY_DONE ;
}
static struct notifier_block qeth_ip_notifier = {
qeth_ip_event ,
2006-07-12 18:41:55 +04:00
NULL ,
2005-04-17 02:20:36 +04:00
} ;
# ifdef CONFIG_QETH_IPV6
/**
* IPv6 event handler
*/
static int
qeth_ip6_event ( struct notifier_block * this ,
unsigned long event , void * ptr )
{
struct inet6_ifaddr * ifa = ( struct inet6_ifaddr * ) ptr ;
struct net_device * dev = ( struct net_device * ) ifa - > idev - > dev ;
struct qeth_ipaddr * addr ;
struct qeth_card * card ;
QETH_DBF_TEXT ( trace , 3 , " ip6event " ) ;
card = qeth_get_card_from_dev ( dev ) ;
if ( ! card )
return NOTIFY_DONE ;
if ( ! qeth_is_supported ( card , IPA_IPV6 ) )
return NOTIFY_DONE ;
addr = qeth_get_addr_buffer ( QETH_PROT_IPV6 ) ;
if ( addr ! = NULL ) {
memcpy ( & addr - > u . a6 . addr , & ifa - > addr , sizeof ( struct in6_addr ) ) ;
addr - > u . a6 . pfxlen = ifa - > prefix_len ;
addr - > type = QETH_IP_TYPE_NORMAL ;
} else
goto out ;
switch ( event ) {
case NETDEV_UP :
if ( ! qeth_add_ip ( card , addr ) )
kfree ( addr ) ;
break ;
case NETDEV_DOWN :
if ( ! qeth_delete_ip ( card , addr ) )
kfree ( addr ) ;
break ;
default :
break ;
}
2007-01-08 19:30:11 +03:00
qeth_set_ip_addr_list ( card ) ;
2005-04-17 02:20:36 +04:00
out :
return NOTIFY_DONE ;
}
static struct notifier_block qeth_ip6_notifier = {
qeth_ip6_event ,
2006-07-12 18:41:55 +04:00
NULL ,
2005-04-17 02:20:36 +04:00
} ;
# endif
static int
2005-08-08 20:22:36 +04:00
__qeth_reboot_event_card ( struct device * dev , void * data )
2005-04-17 02:20:36 +04:00
{
struct qeth_card * card ;
2005-08-08 20:22:36 +04:00
card = ( struct qeth_card * ) dev - > driver_data ;
qeth_clear_ip_list ( card , 0 , 0 ) ;
qeth_qdio_clear_card ( card , 0 ) ;
2007-05-02 17:18:07 +04:00
qeth_clear_qdio_buffers ( card ) ;
2005-08-08 20:22:36 +04:00
return 0 ;
}
static int
qeth_reboot_event ( struct notifier_block * this , unsigned long event , void * ptr )
{
2006-07-18 15:46:58 +04:00
int ret ;
2005-08-08 20:22:36 +04:00
2006-07-18 15:46:58 +04:00
ret = driver_for_each_device ( & qeth_ccwgroup_driver . driver , NULL , NULL ,
__qeth_reboot_event_card ) ;
return ret ? NOTIFY_BAD : NOTIFY_DONE ;
2005-04-17 02:20:36 +04:00
}
static struct notifier_block qeth_reboot_notifier = {
qeth_reboot_event ,
2006-07-12 18:41:55 +04:00
NULL ,
2005-04-17 02:20:36 +04:00
} ;
static int
qeth_register_notifiers ( void )
{
int r ;
QETH_DBF_TEXT ( trace , 5 , " regnotif " ) ;
if ( ( r = register_reboot_notifier ( & qeth_reboot_notifier ) ) )
return r ;
if ( ( r = register_inetaddr_notifier ( & qeth_ip_notifier ) ) )
goto out_reboot ;
# ifdef CONFIG_QETH_IPV6
if ( ( r = register_inet6addr_notifier ( & qeth_ip6_notifier ) ) )
goto out_ipv4 ;
# endif
return 0 ;
# ifdef CONFIG_QETH_IPV6
out_ipv4 :
unregister_inetaddr_notifier ( & qeth_ip_notifier ) ;
# endif
out_reboot :
unregister_reboot_notifier ( & qeth_reboot_notifier ) ;
return r ;
}
/**
* unregister all event notifiers
*/
static void
qeth_unregister_notifiers ( void )
{
QETH_DBF_TEXT ( trace , 5 , " unregnot " ) ;
BUG_ON ( unregister_reboot_notifier ( & qeth_reboot_notifier ) ) ;
BUG_ON ( unregister_inetaddr_notifier ( & qeth_ip_notifier ) ) ;
# ifdef CONFIG_QETH_IPV6
BUG_ON ( unregister_inet6addr_notifier ( & qeth_ip6_notifier ) ) ;
# endif /* QETH_IPV6 */
}
# ifdef CONFIG_QETH_IPV6
static int
qeth_ipv6_init ( void )
{
qeth_old_arp_constructor = arp_tbl . constructor ;
2006-08-15 10:00:27 +04:00
write_lock_bh ( & arp_tbl . lock ) ;
2005-04-17 02:20:36 +04:00
arp_tbl . constructor = qeth_arp_constructor ;
2006-08-15 10:00:27 +04:00
write_unlock_bh ( & arp_tbl . lock ) ;
2005-04-17 02:20:36 +04:00
arp_direct_ops = ( struct neigh_ops * )
kmalloc ( sizeof ( struct neigh_ops ) , GFP_KERNEL ) ;
if ( ! arp_direct_ops )
return - ENOMEM ;
memcpy ( arp_direct_ops , & arp_direct_ops_template ,
sizeof ( struct neigh_ops ) ) ;
return 0 ;
}
static void
qeth_ipv6_uninit ( void )
{
2006-08-15 10:00:27 +04:00
write_lock_bh ( & arp_tbl . lock ) ;
2005-04-17 02:20:36 +04:00
arp_tbl . constructor = qeth_old_arp_constructor ;
2006-08-15 10:00:27 +04:00
write_unlock_bh ( & arp_tbl . lock ) ;
2005-04-17 02:20:36 +04:00
kfree ( arp_direct_ops ) ;
}
# endif /* CONFIG_QETH_IPV6 */
static void
qeth_sysfs_unregister ( void )
{
2006-09-15 18:27:02 +04:00
s390_root_dev_unregister ( qeth_root_dev ) ;
2005-04-17 02:20:36 +04:00
qeth_remove_driver_attributes ( ) ;
ccw_driver_unregister ( & qeth_ccw_driver ) ;
ccwgroup_driver_unregister ( & qeth_ccwgroup_driver ) ;
}
2006-09-15 18:27:02 +04:00
2005-04-17 02:20:36 +04:00
/**
* register qeth at sysfs
*/
static int
qeth_sysfs_register ( void )
{
2006-09-15 18:27:02 +04:00
int rc ;
2005-04-17 02:20:36 +04:00
rc = ccwgroup_driver_register ( & qeth_ccwgroup_driver ) ;
if ( rc )
2006-09-15 18:27:02 +04:00
goto out ;
2005-04-17 02:20:36 +04:00
rc = ccw_driver_register ( & qeth_ccw_driver ) ;
if ( rc )
2006-09-15 18:27:02 +04:00
goto out_ccw_driver ;
2005-04-17 02:20:36 +04:00
rc = qeth_create_driver_attributes ( ) ;
if ( rc )
2006-09-15 18:27:02 +04:00
goto out_qeth_attr ;
2005-04-17 02:20:36 +04:00
qeth_root_dev = s390_root_dev_register ( " qeth " ) ;
2006-09-15 18:27:02 +04:00
rc = IS_ERR ( qeth_root_dev ) ? PTR_ERR ( qeth_root_dev ) : 0 ;
if ( ! rc )
goto out ;
qeth_remove_driver_attributes ( ) ;
out_qeth_attr :
ccw_driver_unregister ( & qeth_ccw_driver ) ;
out_ccw_driver :
ccwgroup_driver_unregister ( & qeth_ccwgroup_driver ) ;
out :
return rc ;
2005-04-17 02:20:36 +04:00
}
/***
* init function
*/
static int __init
qeth_init ( void )
{
2006-09-15 18:27:02 +04:00
int rc ;
2005-04-17 02:20:36 +04:00
2006-02-01 14:06:31 +03:00
PRINT_INFO ( " loading %s \n " , version ) ;
2005-04-17 02:20:36 +04:00
INIT_LIST_HEAD ( & qeth_card_list . list ) ;
INIT_LIST_HEAD ( & qeth_notify_list ) ;
spin_lock_init ( & qeth_notify_lock ) ;
rwlock_init ( & qeth_card_list . rwlock ) ;
2006-09-15 18:27:02 +04:00
rc = qeth_register_dbf_views ( ) ;
if ( rc )
2005-04-17 02:20:36 +04:00
goto out_err ;
2006-09-15 18:27:02 +04:00
rc = qeth_sysfs_register ( ) ;
if ( rc )
goto out_dbf ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_QETH_IPV6
2006-09-15 18:27:02 +04:00
rc = qeth_ipv6_init ( ) ;
if ( rc ) {
PRINT_ERR ( " Out of memory during ipv6 init code = %d \n " , rc ) ;
2005-04-17 02:20:36 +04:00
goto out_sysfs ;
}
# endif /* QETH_IPV6 */
2006-09-15 18:27:02 +04:00
rc = qeth_register_notifiers ( ) ;
if ( rc )
2005-04-17 02:20:36 +04:00
goto out_ipv6 ;
2006-09-15 18:27:02 +04:00
rc = qeth_create_procfs_entries ( ) ;
if ( rc )
2005-04-17 02:20:36 +04:00
goto out_notifiers ;
return rc ;
out_notifiers :
qeth_unregister_notifiers ( ) ;
out_ipv6 :
# ifdef CONFIG_QETH_IPV6
qeth_ipv6_uninit ( ) ;
out_sysfs :
2006-09-15 18:27:02 +04:00
# endif /* QETH_IPV6 */
2005-04-17 02:20:36 +04:00
qeth_sysfs_unregister ( ) ;
2006-09-15 18:27:02 +04:00
out_dbf :
2005-04-17 02:20:36 +04:00
qeth_unregister_dbf_views ( ) ;
out_err :
2006-09-15 18:27:02 +04:00
PRINT_ERR ( " Initialization failed with code %d \n " , rc ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
static void
__exit qeth_exit ( void )
{
struct qeth_card * card , * tmp ;
unsigned long flags ;
QETH_DBF_TEXT ( trace , 1 , " cleanup. " ) ;
/*
* Weed would not need to clean up our devices here , because the
* common device layer calls qeth_remove_device for each device
* as soon as we unregister our driver ( done in qeth_sysfs_unregister ) .
* But we do cleanup here so we can do a " soft " shutdown of our cards .
* qeth_remove_device called by the common device layer would otherwise
* do a " hard " shutdown ( card - > use_hard_stop is set to one in
* qeth_remove_device ) .
*/
again :
read_lock_irqsave ( & qeth_card_list . rwlock , flags ) ;
list_for_each_entry_safe ( card , tmp , & qeth_card_list . list , list ) {
read_unlock_irqrestore ( & qeth_card_list . rwlock , flags ) ;
qeth_set_offline ( card - > gdev ) ;
qeth_remove_device ( card - > gdev ) ;
goto again ;
}
read_unlock_irqrestore ( & qeth_card_list . rwlock , flags ) ;
# ifdef CONFIG_QETH_IPV6
qeth_ipv6_uninit ( ) ;
# endif
qeth_unregister_notifiers ( ) ;
qeth_remove_procfs_entries ( ) ;
qeth_sysfs_unregister ( ) ;
qeth_unregister_dbf_views ( ) ;
printk ( " qeth: removed \n " ) ;
}
2005-09-30 12:19:19 +04:00
EXPORT_SYMBOL ( qeth_osn_register ) ;
EXPORT_SYMBOL ( qeth_osn_deregister ) ;
EXPORT_SYMBOL ( qeth_osn_assist ) ;
2005-04-17 02:20:36 +04:00
module_init ( qeth_init ) ;
module_exit ( qeth_exit ) ;
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_DESCRIPTION ( " Linux on zSeries OSA Express and HiperSockets support \n " \
" Copyright 2000,2003 IBM Corporation \n " ) ;
MODULE_LICENSE ( " GPL " ) ;