Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
d2a03c2f
Commit
d2a03c2f
authored
Dec 06, 2002
by
Martin Schwidefsky
Committed by
Linus Torvalds
Dec 06, 2002
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[PATCH] s390: chandev.
Remove channel device layer.
parent
c7d8d8ce
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
1 addition
and
3345 deletions
+1
-3345
drivers/s390/Kconfig
drivers/s390/Kconfig
+0
-39
drivers/s390/misc/Makefile
drivers/s390/misc/Makefile
+1
-2
drivers/s390/misc/chandev.c
drivers/s390/misc/chandev.c
+0
-3304
No files found.
drivers/s390/Kconfig
View file @
d2a03c2f
...
@@ -524,47 +524,8 @@ config FDDI
...
@@ -524,47 +524,8 @@ config FDDI
comment "S/390 network device drivers"
comment "S/390 network device drivers"
depends on NETDEVICES
depends on NETDEVICES
config CHANDEV
bool "Channel Device Configuration"
depends on NETDEVICES
---help---
The channel device layer is a layer to provide a consistent
interface for configuration & default machine check (devices
appearing & disappearing) handling on Linux for s/390 & z/Series
channel devices.
s/390 & z/Series channel devices include among others
lcs (the most common ethernet/token ring/fddi standard on
zSeries)
ctc/escon hi speed like serial link standard on zSeries
claw used to talk to cisco routers.
qeth gigabit ethernet.
These devices use two channels one read & one write for
configuration & communication (& a third channel, the data
channel the case of gigabit ethernet). The motivation
behind developing this layer was that there was a lot of
duplicate code among the channel device drivers for
configuration.
Also the lcs & ctc drivers tended to fight over
3088/08's & 3088/1F's which could be either 2216/3172
channel attached lcs compatible devices or escon/ctc pipes
had to be configured separately as they couldn't autodetect,
this is now simplified by doing the configuration in a single
place (the channel device layer).
This layer isn't invasive & it is quite okay to use channel
drivers which don't use the channel device layer in
conjunction with drivers which do.
For more info see the chandev manpage usually distributed in
<file:Documentation/s390/chandev.8> in the Linux source tree.
config HOTPLUG
config HOTPLUG
bool
bool
depends on CHANDEV
default y
default y
---help---
---help---
Say Y here if you want to plug devices into your computer while
Say Y here if you want to plug devices into your computer while
...
...
drivers/s390/misc/Makefile
View file @
d2a03c2f
...
@@ -2,7 +2,6 @@
...
@@ -2,7 +2,6 @@
# S/390 miscellaneous devices
# S/390 miscellaneous devices
#
#
obj-$(CONFIG_CHANDEV)
+=
chandev.o
# placeholder for stuff to come...
export-objs
+=
chandev.o
include
$(TOPDIR)/Rules.make
include
$(TOPDIR)/Rules.make
drivers/s390/misc/chandev.c
deleted
100644 → 0
View file @
c7d8d8ce
/*
* drivers/s390/misc/chandev.c
*
* Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
*
* Generic channel device initialisation support.
*/
#define TRUE 1
#define FALSE 0
#define __KERNEL_SYSCALLS__
#include <linux/module.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/ctype.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <asm/irq.h>
#include <linux/init.h>
#include <linux/unistd.h>
#include <asm/chandev.h>
#include <linux/proc_fs.h>
#include <linux/vmalloc.h>
#include <asm/s390dyn.h>
#include <asm/queue.h>
#include <linux/kmod.h>
#include <linux/workqueue.h>
#ifndef MIN
#define MIN(a,b) ((a<b)?a:b)
#endif
#ifndef MAX
#define MAX(a,b) ((a>b)?a:b)
#endif
typedef
struct
chandev_model_info
chandev_model_info
;
struct
chandev_model_info
{
struct
chandev_model_info
*
next
;
chandev_type
chan_type
;
s32
cu_type
;
/* control unit type -1 = don't care */
s16
cu_model
;
/* control unit model -1 = don't care */
s32
dev_type
;
/* device type -1 = don't care */
s16
dev_model
;
/* device model -1 = don't care */
u8
max_port_no
;
int
auto_msck_recovery
;
u8
default_checksum_received_ip_pkts
;
u8
default_use_hw_stats
;
/* where available e.g. lcs */
devreg_t
drinfo
;
};
typedef
struct
chandev
chandev
;
struct
chandev
{
struct
chandev
*
next
;
chandev_model_info
*
model_info
;
chandev_subchannel_info
sch
;
int
owned
;
};
typedef
struct
chandev_noauto_range
chandev_noauto_range
;
struct
chandev_noauto_range
{
struct
chandev_noauto_range
*
next
;
u16
lo_devno
;
u16
hi_devno
;
};
typedef
struct
chandev_force
chandev_force
;
struct
chandev_force
{
struct
chandev_force
*
next
;
chandev_type
chan_type
;
s32
devif_num
;
/* -1 don't care, -2 we are forcing a range e.g. tr0 implies 0 */
u16
read_lo_devno
;
u16
write_hi_devno
;
u16
data_devno
;
/* only used by gigabit ethernet */
s32
memory_usage_in_k
;
s16
port_protocol_no
;
/* where available e.g. lcs,-1 don't care */
u8
checksum_received_ip_pkts
;
u8
use_hw_stats
;
/* where available e.g. lcs */
/* claw specific stuff */
chandev_claw_info
claw
;
};
typedef
struct
chandev_probelist
chandev_probelist
;
struct
chandev_probelist
{
struct
chandev_probelist
*
next
;
chandev_probefunc
probefunc
;
chandev_shutdownfunc
shutdownfunc
;
chandev_msck_notification_func
msck_notfunc
;
chandev_type
chan_type
;
int
devices_found
;
};
#define default_msck_bits ((1<<(chandev_status_not_oper-1))|(1<<(chandev_status_no_path-1))|(1<<(chandev_status_revalidate-1))|(1<<(chandev_status_gone-1)))
static
char
*
msck_status_strs
[]
=
{
"good"
,
"not_operational"
,
"no_path"
,
"revalidate"
,
"device_gone"
};
typedef
struct
chandev_msck_range
chandev_msck_range
;
struct
chandev_msck_range
{
struct
chandev_msck_range
*
next
;
u16
lo_devno
;
u16
hi_devno
;
int
auto_msck_recovery
;
};
static
chandev_msck_range
*
chandev_msck_range_head
=
NULL
;
typedef
struct
chandev_irqinfo
chandev_irqinfo
;
struct
chandev_irqinfo
{
chandev_irqinfo
*
next
;
chandev_subchannel_info
sch
;
chandev_msck_status
msck_status
;
void
(
*
handler
)(
int
,
void
*
,
struct
pt_regs
*
);
unsigned
long
irqflags
;
void
*
dev_id
;
char
devname
[
0
];
};
static
chandev_irqinfo
*
chandev_irqinfo_head
=
NULL
;
typedef
struct
chandev_parms
chandev_parms
;
struct
chandev_parms
{
chandev_parms
*
next
;
chandev_type
chan_type
;
u16
lo_devno
;
u16
hi_devno
;
char
parmstr
[
0
];
};
static
chandev_type
chandev_persistent
=
0
;
static
chandev_parms
*
chandev_parms_head
=
NULL
;
typedef
struct
chandev_activelist
chandev_activelist
;
struct
chandev_activelist
{
struct
chandev_activelist
*
next
;
chandev_irqinfo
*
read_irqinfo
;
chandev_irqinfo
*
write_irqinfo
;
chandev_irqinfo
*
data_irqinfo
;
chandev_probefunc
probefunc
;
chandev_shutdownfunc
shutdownfunc
;
chandev_msck_notification_func
msck_notfunc
;
chandev_unregfunc
unreg_dev
;
chandev_type
chan_type
;
u8
port_no
;
chandev_category
category
;
s32
memory_usage_in_k
;
void
*
dev_ptr
;
char
devname
[
0
];
};
static
chandev_model_info
*
chandev_models_head
=
NULL
;
/* The only reason chandev_head is a queue is so that net devices */
/* will be by default named in the order of their irqs */
static
qheader
chandev_head
=
{
NULL
,
NULL
};
static
chandev_noauto_range
*
chandev_noauto_head
=
NULL
;
static
chandev_force
*
chandev_force_head
=
NULL
;
static
chandev_probelist
*
chandev_probelist_head
=
NULL
;
static
chandev_activelist
*
chandev_activelist_head
=
NULL
;
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
/* not static, see comment in chandev.h */
int
chandev_use_devno_names
=
FALSE
;
#endif
static
int
chandev_cautious_auto_detect
=
TRUE
;
static
atomic_t
chandev_conf_read
=
ATOMIC_INIT
(
FALSE
);
static
atomic_t
chandev_initialised
=
ATOMIC_INIT
(
FALSE
);
static
unsigned
long
chandev_last_machine_check
;
static
void
chandev_msck_task
(
void
*
unused
);
static
DECLARE_WORK
(
chandev_msck_work
,
chandev_msck_task
,
0
);
static
atomic_t
chandev_msck_thread_lock
;
static
atomic_t
chandev_new_msck
;
static
unsigned
long
chandev_last_startmsck_list_update
;
typedef
enum
{
chandev_start
,
chandev_first_tag
=
chandev_start
,
chandev_msck
,
chandev_num_notify_tags
}
chandev_userland_notify_tag
;
static
char
*
userland_notify_strs
[]
=
{
"start"
,
"machine_check"
};
typedef
struct
chandev_userland_notify_list
chandev_userland_notify_list
;
struct
chandev_userland_notify_list
{
chandev_userland_notify_list
*
next
;
chandev_userland_notify_tag
tag
;
chandev_msck_status
prev_status
;
chandev_msck_status
curr_status
;
char
devname
[
0
];
};
static
chandev_userland_notify_list
*
chandev_userland_notify_head
=
NULL
;
static
void
chandev_read_conf_if_necessary
(
void
);
static
void
chandev_read_conf
(
void
);
#if LINUX_VERSION_CODE >=KERNEL_VERSION(2,3,0)
typedef
struct
net_device
net_device
;
#else
typedef
struct
device
net_device
;
static
inline
void
init_waitqueue_head
(
wait_queue_head_t
*
q
)
{
*
q
=
NULL
;
}
#endif
#if LINUX_VERSION_CODE<KERNEL_VERSION(2,3,45)
static
__inline__
void
netif_stop_queue
(
net_device
*
dev
)
{
dev
->
tbusy
=
1
;
}
static
__inline__
void
netif_start_queue
(
net_device
*
dev
)
{
dev
->
tbusy
=
0
;
}
#endif
#define CHANDEV_INVALID_LOCK_OWNER -1
static
long
chandev_lock_owner
;
static
int
chandev_lock_cnt
;
static
spinlock_t
chandev_spinlock
;
#define CHANDEV_LOCK_DEBUG 0
#if CHANDEV_LOCK_DEBUG && !defined(CONFIG_ARCH_S390X)
#define CHANDEV_BACKTRACE_LOOPCNT 10
void
*
chandev_first_lock_addr
[
CHANDEV_BACKTRACE_LOOPCNT
],
*
chandev_last_lock_addr
[
CHANDEV_BACKTRACE_LOOPCNT
],
*
chandev_last_unlock_addr
[
CHANDEV_BACKTRACE_LOOPCNT
];
#define CHANDEV_BACKTRACE(variable) \
memset((variable),0,sizeof(void *)*CHANDEV_BACKTRACE_LOOPCNT); \
(variable)[0]=__builtin_return_address(0); \
if(((long)variable[0])&0x80000000) \
{ \
(variable)[1]=__builtin_return_address(1); \
if(((long)variable[1])&0x80000000) \
{ \
(variable)[2]=__builtin_return_address(2); \
if(((long)variable[2])&0x80000000) \
{ \
(variable)[3]=__builtin_return_address(3); \
if(((long)variable[3])&0x80000000) \
{ \
(variable)[4]=__builtin_return_address(4); \
if(((long)variable[4])&0x80000000) \
{ \
(variable)[5]=__builtin_return_address(5); \
if(((long)variable[5])&0x80000000) \
{ \
(variable)[6]=__builtin_return_address(6); \
if(((long)variable[6])&0x80000000) \
{ \
(variable)[7]=__builtin_return_address(7); \
if(((long)variable[7])&0x80000000) \
{ \
(variable)[8]=__builtin_return_address(8); \
if(((long)variable[8])&0x80000000) \
{ \
(variable)[9]=__builtin_return_address(9); \
} \
} \
} \
} \
} \
} \
} \
} \
}
#else
#define CHANDEV_BACKTRACE(variable)
#endif
typedef
struct
chandev_not_oper_struct
chandev_not_oper_struct
;
struct
chandev_not_oper_struct
{
chandev_not_oper_struct
*
next
;
int
irq
;
int
status
;
};
/* May as well try to keep machine checks in the order they happen so
* we use qheader for chandev_not_oper_head instead of list.
*/
static
qheader
chandev_not_oper_head
=
{
NULL
,
NULL
};
static
spinlock_t
chandev_not_oper_spinlock
;
#define chandev_interrupt_check(name) \
if(in_interrupt()) \
printk(KERN_WARNING name " called under interrupt this shouldn't happen\n")
#define for_each(variable,head) \
for((variable)=(head);(variable)!=NULL;(variable)=(variable)->next)
#define for_each_allow_delete(variable,nextmember,head) \
for((variable)=(head),(nextmember)=((head) ? (head)->next:NULL); \
(variable)!=NULL; (variable)=(nextmember),(nextmember)=((nextmember) ? (nextmember->next) : NULL))
#define for_each_allow_delete2(variable,nextmember,head) \
for((variable)=(head);(variable)!=NULL;(variable)=(nextmember))
static
void
chandev_lock
(
void
)
{
eieio
();
chandev_interrupt_check
(
"chandev_lock"
);
if
(
chandev_lock_owner
!=
(
long
)
current
)
{
while
(
!
spin_trylock
(
&
chandev_spinlock
))
schedule
();
chandev_lock_cnt
=
1
;
chandev_lock_owner
=
(
long
)
current
;
CHANDEV_BACKTRACE
(
chandev_first_lock_addr
)
}
else
{
chandev_lock_cnt
++
;
CHANDEV_BACKTRACE
(
chandev_last_lock_addr
)
}
if
(
chandev_lock_cnt
<
0
||
chandev_lock_cnt
>
100
)
{
printk
(
"odd lock_cnt %d lcs_chan_lock"
,
chandev_lock_cnt
);
chandev_lock_cnt
=
1
;
}
}
static
int
chandev_full_unlock
(
void
)
{
int
ret_lock_cnt
=
chandev_lock_cnt
;
chandev_lock_cnt
=
0
;
chandev_lock_owner
=
CHANDEV_INVALID_LOCK_OWNER
;
spin_unlock
(
&
chandev_spinlock
);
return
(
ret_lock_cnt
);
}
static
void
chandev_unlock
(
void
)
{
if
(
chandev_lock_owner
!=
(
long
)
current
)
printk
(
"chandev_unlock: current=%lx"
" chandev_lock_owner=%lx chandev_lock_cnt=%d
\n
"
,
(
long
)
current
,
chandev_lock_owner
,
chandev_lock_cnt
);
CHANDEV_BACKTRACE
(
chandev_last_unlock_addr
)
if
(
--
chandev_lock_cnt
==
0
)
{
chandev_lock_owner
=
CHANDEV_INVALID_LOCK_OWNER
;
spin_unlock
(
&
chandev_spinlock
);
}
if
(
chandev_lock_cnt
<
0
)
{
printk
(
"odd lock_cnt=%d in chan_unlock"
,
chandev_lock_cnt
);
chandev_full_unlock
();
}
}
static
void
*
chandev_alloc
(
size_t
size
)
{
void
*
mem
=
kmalloc
(
size
,
GFP_ATOMIC
);
if
(
mem
)
memset
(
mem
,
0
,
size
);
return
(
mem
);
}
static
void
chandev_add_to_list
(
list
**
listhead
,
void
*
member
)
{
chandev_lock
();
add_to_list
(
listhead
,
member
);
chandev_unlock
();
}
static
void
chandev_queuemember
(
qheader
*
qhead
,
void
*
member
)
{
chandev_lock
();
enqueue_tail
(
qhead
,(
queue
*
)
member
);
chandev_unlock
();
}
static
int
chandev_remove_from_list
(
list
**
listhead
,
list
*
member
)
{
int
retval
;
chandev_lock
();
retval
=
remove_from_list
(
listhead
,
member
);
chandev_unlock
();
return
(
retval
);
}
static
int
chandev_remove_from_queue
(
qheader
*
qhead
,
queue
*
member
)
{
int
retval
;
chandev_lock
();
retval
=
remove_from_queue
(
qhead
,
member
);
chandev_unlock
();
return
(
retval
);
}
static
void
chandev_free_listmember
(
list
**
listhead
,
list
*
member
)
{
chandev_lock
();
if
(
member
)
{
if
(
chandev_remove_from_list
(
listhead
,
member
))
kfree
(
member
);
else
printk
(
KERN_CRIT
"chandev_free_listmember detected nonexistant"
"listmember listhead=%p member %p
\n
"
,
listhead
,
member
);
}
chandev_unlock
();
}
static
void
chandev_free_queuemember
(
qheader
*
qhead
,
queue
*
member
)
{
chandev_lock
();
if
(
member
)
{
if
(
chandev_remove_from_queue
(
qhead
,
member
))
kfree
(
member
);
else
printk
(
KERN_CRIT
"chandev_free_queuemember detected nonexistant"
"queuemember qhead=%p member %p
\n
"
,
qhead
,
member
);
}
chandev_unlock
();
}
static
void
chandev_free_all_list
(
list
**
listhead
)
{
list
*
head
;
chandev_lock
();
while
((
head
=
remove_listhead
(
listhead
)))
kfree
(
head
);
chandev_unlock
();
}
static
void
chandev_free_all_queue
(
qheader
*
qhead
)
{
chandev_lock
();
while
(
qhead
->
head
)
chandev_free_queuemember
(
qhead
,
qhead
->
head
);
chandev_unlock
();
}
static
void
chandev_wait_for_root_fs
(
void
)
{
wait_queue_head_t
wait
;
init_waitqueue_head
(
&
wait
);
/* We need to wait till there is a root filesystem */
while
(
init_task
.
fs
->
root
==
NULL
)
{
sleep_on_timeout
(
&
wait
,
HZ
);
}
}
/* We are now hotplug compliant i.e. */
/* we typically get called in /sbin/hotplug chandev our parameters */
static
int
chandev_exec_start_script
(
void
*
unused
)
{
char
**
argv
,
*
tempname
;
int
retval
=-
ENOMEM
;
int
argc
,
loopcnt
;
size_t
allocsize
;
chandev_userland_notify_list
*
member
;
wait_queue_head_t
wait
;
int
have_tag
[
chandev_num_notify_tags
]
=
{
FALSE
,};
chandev_userland_notify_tag
tagidx
;
static
char
*
envp
[]
=
{
"HOME=/"
,
"TERM=linux"
,
"PATH=/sbin:/usr/sbin:/bin:/usr/bin"
,
NULL
};
init_waitqueue_head
(
&
wait
);
strcpy
(
current
->
comm
,
"chandev_script"
);
for
(
loopcnt
=
0
;
loopcnt
<
10
&&
(
jiffies
-
chandev_last_startmsck_list_update
)
<
HZ
;
loopcnt
++
)
{
sleep_on_timeout
(
&
wait
,
HZ
);
}
if
(
!
chandev_userland_notify_head
)
return
(
0
);
chandev_lock
();
argc
=
2
;
for
(
tagidx
=
chandev_first_tag
;
tagidx
<
chandev_num_notify_tags
;
tagidx
++
)
{
for_each
(
member
,
chandev_userland_notify_head
)
{
if
(
member
->
tag
==
tagidx
)
{
switch
(
tagidx
)
{
case
chandev_start
:
argc
++
;
break
;
case
chandev_msck
:
argc
+=
3
;
break
;
default:
break
;
}
if
(
have_tag
[
tagidx
]
==
FALSE
)
argc
++
;
have_tag
[
tagidx
]
=
TRUE
;
}
}
}
allocsize
=
(
argc
+
1
)
*
sizeof
(
char
*
);
/* Warning possible stack overflow */
/* We can't kmalloc the parameters here as execve will */
/* not return if successful */
argv
=
alloca
(
allocsize
);
if
(
argv
)
{
memset
(
argv
,
0
,
allocsize
);
argv
[
0
]
=
hotplug_path
;
argv
[
1
]
=
"chandev"
;
argc
=
2
;
for
(
tagidx
=
chandev_first_tag
;
tagidx
<
chandev_num_notify_tags
;
tagidx
++
)
{
if
(
have_tag
[
tagidx
])
{
argv
[
argc
++
]
=
userland_notify_strs
[
tagidx
];
for_each
(
member
,
chandev_userland_notify_head
)
{
if
(
member
->
tag
==
tagidx
)
{
tempname
=
alloca
(
strlen
(
member
->
devname
)
+
1
);
if
(
tempname
)
{
strcpy
(
tempname
,
member
->
devname
);
argv
[
argc
++
]
=
tempname
;
}
else
goto
Fail
;
if
(
member
->
tag
==
chandev_msck
)
{
argv
[
argc
++
]
=
msck_status_strs
[
member
->
prev_status
];
argv
[
argc
++
]
=
msck_status_strs
[
member
->
curr_status
];
}
}
}
}
}
chandev_free_all_list
((
list
**
)
&
chandev_userland_notify_head
);
chandev_unlock
();
chandev_wait_for_root_fs
();
/* We are basically execve'ing here there normally is no */
/* return */
retval
=
exec_usermodehelper
(
hotplug_path
,
argv
,
envp
);
goto
Fail2
;
}
Fail:
chandev_unlock
();
Fail2:
return
(
retval
);
}
static
void
*
chandev_allocstr
(
const
char
*
str
,
size_t
offset
)
{
char
*
member
;
if
((
member
=
chandev_alloc
(
offset
+
strlen
(
str
)
+
1
)))
{
strcpy
(
&
member
[
offset
],
str
);
}
return
((
void
*
)
member
);
}
static
int
chandev_add_to_userland_notify_list
(
chandev_userland_notify_tag
tag
,
char
*
devname
,
chandev_msck_status
prev_status
,
chandev_msck_status
curr_status
)
{
chandev_userland_notify_list
*
member
,
*
nextmember
;
int
pid
;
chandev_lock
();
/* remove operations still outstanding for this device */
for_each_allow_delete
(
member
,
nextmember
,
chandev_userland_notify_head
)
if
(
strcmp
(
member
->
devname
,
devname
)
==
0
)
chandev_free_listmember
((
list
**
)
&
chandev_userland_notify_head
,(
list
*
)
member
);
if
((
member
=
chandev_allocstr
(
devname
,
offsetof
(
chandev_userland_notify_list
,
devname
))))
{
member
->
tag
=
tag
;
member
->
prev_status
=
prev_status
;
member
->
curr_status
=
curr_status
;
add_to_list
((
list
**
)
&
chandev_userland_notify_head
,(
list
*
)
member
);
chandev_last_startmsck_list_update
=
jiffies
;
chandev_unlock
();
pid
=
kernel_thread
(
chandev_exec_start_script
,
NULL
,
SIGCHLD
);
if
(
pid
<
0
)
{
printk
(
"error making kernel thread for chandev_exec_start_script
\n
"
);
return
(
pid
);
}
else
return
(
0
);
}
else
{
chandev_unlock
();
printk
(
"chandev_add_to_startmscklist memory allocation failed devname=%s
\n
"
,
devname
);
return
(
-
ENOMEM
);
}
}
static
int
chandev_oper_func
(
int
irq
,
devreg_t
*
dreg
)
{
chandev_last_machine_check
=
jiffies
;
if
(
atomic_dec_and_test
(
&
chandev_msck_thread_lock
))
{
schedule_work
(
&
chandev_msck_work
);
}
atomic_set
(
&
chandev_new_msck
,
TRUE
);
return
(
0
);
}
static
void
chandev_not_oper_handler
(
int
irq
,
int
status
)
{
chandev_not_oper_struct
*
new_not_oper
;
chandev_last_machine_check
=
jiffies
;
if
((
new_not_oper
=
kmalloc
(
sizeof
(
chandev_not_oper_struct
),
GFP_ATOMIC
)))
{
new_not_oper
->
irq
=
irq
;
new_not_oper
->
status
=
status
;
spin_lock
(
&
chandev_not_oper_spinlock
);
enqueue_tail
(
&
chandev_not_oper_head
,(
queue
*
)
new_not_oper
);
spin_unlock
(
&
chandev_not_oper_spinlock
);
if
(
atomic_dec_and_test
(
&
chandev_msck_thread_lock
))
{
schedule_work
(
&
chandev_msck_work
);
}
}
else
printk
(
"chandev_not_oper_handler failed to allocate memory & "
"lost a not operational interrupt %d %x"
,
irq
,
status
);
}
static
chandev_irqinfo
*
chandev_get_irqinfo_by_irq
(
int
irq
)
{
chandev_irqinfo
*
curr_irqinfo
;
for_each
(
curr_irqinfo
,
chandev_irqinfo_head
)
if
(
irq
==
curr_irqinfo
->
sch
.
irq
)
return
(
curr_irqinfo
);
return
(
NULL
);
}
static
chandev
*
chandev_get_by_irq
(
int
irq
)
{
chandev
*
curr_chandev
;
for_each
(
curr_chandev
,(
chandev
*
)
chandev_head
.
head
)
if
(
curr_chandev
->
sch
.
irq
==
irq
)
{
return
(
curr_chandev
);
}
return
(
NULL
);
}
static
chandev_activelist
*
chandev_get_activelist_by_irq
(
int
irq
)
{
chandev_activelist
*
curr_device
;
for_each
(
curr_device
,
chandev_activelist_head
)
{
if
(
curr_device
->
read_irqinfo
->
sch
.
irq
==
irq
||
curr_device
->
write_irqinfo
->
sch
.
irq
==
irq
||
(
curr_device
->
data_irqinfo
&&
curr_device
->
data_irqinfo
->
sch
.
irq
==
irq
))
return
(
curr_device
);
}
return
(
NULL
);
}
static
void
chandev_remove_irqinfo_by_irq
(
unsigned
int
irq
)
{
chandev_irqinfo
*
remove_irqinfo
;
chandev_activelist
*
curr_device
;
chandev_lock
();
/* remove any orphan irqinfo left lying around. */
if
((
remove_irqinfo
=
chandev_get_irqinfo_by_irq
(
irq
)))
{
for_each
(
curr_device
,
chandev_activelist_head
)
{
if
(
curr_device
->
read_irqinfo
==
remove_irqinfo
)
{
curr_device
->
read_irqinfo
=
NULL
;
break
;
}
if
(
curr_device
->
write_irqinfo
==
remove_irqinfo
)
{
curr_device
->
write_irqinfo
=
NULL
;
break
;
}
if
(
curr_device
->
data_irqinfo
&&
curr_device
->
data_irqinfo
==
remove_irqinfo
)
{
curr_device
->
data_irqinfo
=
NULL
;
break
;
}
}
chandev_free_listmember
((
list
**
)
&
chandev_irqinfo_head
,
(
list
*
)
remove_irqinfo
);
}
chandev_unlock
();
}
static
int
chandev_add_schib_info
(
int
irq
,
chandev_subchannel_info
*
sch
)
{
schib_t
*
new_schib
;
if
((
new_schib
=
s390_get_schib
(
irq
)))
{
sch
->
pim
=
new_schib
->
pmcw
.
pim
;
memcpy
(
&
sch
->
chpid
,
&
new_schib
->
pmcw
.
chpid
,
sizeof
(
sch
->
chpid
));
return
(
0
);
}
return
(
-
ENODEV
);
}
int
chandev_request_irq
(
unsigned
int
irq
,
void
(
*
handler
)(
int
,
void
*
,
struct
pt_regs
*
),
unsigned
long
irqflags
,
const
char
*
devname
,
void
*
dev_id
)
{
chandev_irqinfo
*
new_irqinfo
;
chandev_activelist
*
curr_device
;
s390_dev_info_t
devinfo
;
int
retval
;
chandev_lock
();
if
((
curr_device
=
chandev_get_activelist_by_irq
(
irq
)))
{
printk
(
"chandev_request_irq failed devname=%s irq=%d "
"it already belongs to %s shutdown this device first.
\n
"
,
devname
,
irq
,
curr_device
->
devname
);
chandev_unlock
();
return
(
-
EPERM
);
}
/* remove any orphan irqinfo left lying around. */
chandev_remove_irqinfo_by_irq
(
irq
);
chandev_unlock
();
if
((
new_irqinfo
=
chandev_allocstr
(
devname
,
offsetof
(
chandev_irqinfo
,
devname
))))
{
if
((
retval
=
get_dev_info_by_irq
(
irq
,
&
devinfo
))
||
(
retval
=
s390_request_irq_special
(
irq
,
handler
,
chandev_not_oper_handler
,
irqflags
,
devname
,
dev_id
)))
kfree
(
new_irqinfo
);
else
{
new_irqinfo
->
msck_status
=
chandev_status_good
;
new_irqinfo
->
sch
.
devno
=
devinfo
.
devno
;
new_irqinfo
->
sch
.
irq
=
irq
;
new_irqinfo
->
sch
.
cu_type
=
devinfo
.
sid_data
.
cu_type
;
/* control unit type */
new_irqinfo
->
sch
.
cu_model
=
devinfo
.
sid_data
.
cu_model
;
/* control unit model */
new_irqinfo
->
sch
.
dev_type
=
devinfo
.
sid_data
.
dev_type
;
/* device type */
new_irqinfo
->
sch
.
dev_model
=
devinfo
.
sid_data
.
dev_model
;
/* device model */
chandev_add_schib_info
(
irq
,
&
new_irqinfo
->
sch
);
new_irqinfo
->
handler
=
handler
;
new_irqinfo
->
dev_id
=
dev_id
;
chandev_add_to_list
((
list
**
)
&
chandev_irqinfo_head
,
new_irqinfo
);
}
}
else
{
printk
(
"chandev_request_irq memory allocation failed devname=%s irq=%d
\n
"
,
devname
,
irq
);
retval
=-
ENOMEM
;
}
return
(
retval
);
}
/* This should be safe to call even multiple times. */
void
chandev_free_irq
(
unsigned
int
irq
,
void
*
dev_id
)
{
s390_dev_info_t
devinfo
;
int
err
;
/* remove any orphan irqinfo left lying around. */
chandev_remove_irqinfo_by_irq
(
irq
);
if
((
err
=
get_dev_info_by_irq
(
irq
,
&
devinfo
)))
{
printk
(
"chandev_free_irq get_dev_info_by_irq reported err=%X on irq %d
\n
"
"should not happen
\n
"
,
err
,
irq
);
return
;
}
if
(
devinfo
.
status
&
DEVSTAT_DEVICE_OWNED
)
free_irq
(
irq
,
dev_id
);
}
/* This should be safe even if chandev_free_irq is already called by the device */
static
void
chandev_free_irq_by_irqinfo
(
chandev_irqinfo
*
irqinfo
)
{
if
(
irqinfo
)
chandev_free_irq
(
irqinfo
->
sch
.
irq
,
irqinfo
->
dev_id
);
}
static
void
chandev_sprint_type_model
(
char
*
buff
,
s32
type
,
s16
model
)
{
if
(
type
==-
1
)
strcpy
(
buff
,
" * "
);
else
sprintf
(
buff
,
" 0x%04x "
,(
int
)
type
);
buff
+=
strlen
(
buff
);
if
(
model
==-
1
)
strcpy
(
buff
,
" * "
);
else
sprintf
(
buff
,
" 0x%02x "
,(
int
)
model
);
}
static
void
chandev_sprint_devinfo
(
char
*
buff
,
s32
cu_type
,
s16
cu_model
,
s32
dev_type
,
s16
dev_model
)
{
chandev_sprint_type_model
(
buff
,
cu_type
,
cu_model
);
chandev_sprint_type_model
(
&
buff
[
strlen
(
buff
)],
dev_type
,
dev_model
);
}
static
void
chandev_remove_parms
(
chandev_type
chan_type
,
int
exact_match
,
int
lo_devno
)
{
chandev_parms
*
curr_parms
,
*
next_parms
;
chandev_lock
();
for_each_allow_delete
(
curr_parms
,
next_parms
,
chandev_parms_head
)
{
if
(((
chan_type
&
(
curr_parms
->
chan_type
)
&&!
exact_match
)
||
(
chan_type
==
(
curr_parms
->
chan_type
)
&&
exact_match
))
&&
(
lo_devno
==-
1
||
lo_devno
==
curr_parms
->
lo_devno
))
chandev_free_listmember
((
list
**
)
&
chandev_parms_head
,(
list
*
)
curr_parms
);
}
chandev_unlock
();
}
static
void
chandev_add_parms
(
chandev_type
chan_type
,
u16
lo_devno
,
u16
hi_devno
,
char
*
parmstr
)
{
chandev_parms
*
parms
;
if
(
lo_devno
>
hi_devno
)
{
printk
(
"chandev_add_parms detected bad device range lo_devno=0x%04x hi_devno=0x%04x
\n
,"
,
(
int
)
lo_devno
,(
int
)
hi_devno
);
return
;
}
chandev_lock
();
for_each
(
parms
,
chandev_parms_head
)
{
if
(
chan_type
&
(
parms
->
chan_type
))
{
u16
lomax
=
MAX
(
parms
->
lo_devno
,
lo_devno
),
himin
=
MIN
(
parms
->
hi_devno
,
lo_devno
);
if
(
lomax
<=
himin
)
{
chandev_unlock
();
printk
(
"chandev_add_parms detected overlapping "
"parameter definitions for chan_type=0x%02x"
" lo_devno=0x%04x hi_devno=0x%04x
\n
,"
" do a del_parms."
,
chan_type
,(
int
)
lo_devno
,(
int
)
hi_devno
);
return
;
}
}
}
chandev_unlock
();
if
((
parms
=
chandev_allocstr
(
parmstr
,
offsetof
(
chandev_parms
,
parmstr
))))
{
parms
->
chan_type
=
chan_type
;
parms
->
lo_devno
=
lo_devno
;
parms
->
hi_devno
=
hi_devno
;
chandev_add_to_list
((
list
**
)
&
chandev_parms_head
,(
void
*
)
parms
);
}
else
printk
(
"chandev_add_parms memory request failed
\n
"
);
}
void
chandev_add_model
(
chandev_type
chan_type
,
s32
cu_type
,
s16
cu_model
,
s32
dev_type
,
s16
dev_model
,
u8
max_port_no
,
int
auto_msck_recovery
,
u8
default_checksum_received_ip_pkts
,
u8
default_use_hw_stats
)
{
chandev_model_info
*
newmodel
;
int
err
;
char
buff
[
40
];
if
((
newmodel
=
chandev_alloc
(
sizeof
(
chandev_model_info
))))
{
devreg_t
*
drinfo
=&
newmodel
->
drinfo
;
newmodel
->
chan_type
=
chan_type
;
newmodel
->
cu_type
=
cu_type
;
newmodel
->
cu_model
=
cu_model
;
newmodel
->
dev_type
=
dev_type
;
newmodel
->
dev_model
=
dev_model
;
newmodel
->
max_port_no
=
max_port_no
;
newmodel
->
auto_msck_recovery
=
auto_msck_recovery
;
newmodel
->
default_checksum_received_ip_pkts
=
default_checksum_received_ip_pkts
;
newmodel
->
default_use_hw_stats
=
default_use_hw_stats
;
/* where available e.g. lcs */
if
(
cu_type
==-
1
&&
dev_type
==-
1
)
{
chandev_sprint_devinfo
(
buff
,
newmodel
->
cu_type
,
newmodel
->
cu_model
,
newmodel
->
dev_type
,
newmodel
->
dev_model
);
printk
(
KERN_INFO
"can't call s390_device_register for this device chan_type/chan_model/dev_type/dev_model %s
\n
"
,
buff
);
kfree
(
newmodel
);
return
;
}
drinfo
->
flag
=
DEVREG_TYPE_DEVCHARS
;
if
(
cu_type
!=-
1
)
drinfo
->
flag
|=
DEVREG_MATCH_CU_TYPE
;
if
(
cu_model
!=-
1
)
drinfo
->
flag
|=
DEVREG_MATCH_CU_MODEL
;
if
(
dev_type
!=-
1
)
drinfo
->
flag
|=
DEVREG_MATCH_DEV_TYPE
;
if
(
dev_model
!=-
1
)
drinfo
->
flag
|=
DEVREG_MATCH_DEV_MODEL
;
drinfo
->
ci
.
hc
.
ctype
=
cu_type
;
drinfo
->
ci
.
hc
.
cmode
=
cu_model
;
drinfo
->
ci
.
hc
.
dtype
=
dev_type
;
drinfo
->
ci
.
hc
.
dmode
=
dev_model
;
drinfo
->
oper_func
=
chandev_oper_func
;
if
((
err
=
s390_device_register
(
&
newmodel
->
drinfo
)))
{
chandev_sprint_devinfo
(
buff
,
newmodel
->
cu_type
,
newmodel
->
cu_model
,
newmodel
->
dev_type
,
newmodel
->
dev_model
);
printk
(
"s390_device_register failed in chandev_add_model"
" this is nothing to worry about chan_type/chan_model/dev_type/dev_model %s
\n
"
,
buff
);
drinfo
->
oper_func
=
NULL
;
}
chandev_add_to_list
((
list
**
)
&
chandev_models_head
,
newmodel
);
}
}
static
void
chandev_remove
(
chandev
*
member
)
{
chandev_free_queuemember
(
&
chandev_head
,(
queue
*
)
member
);
}
static
void
chandev_remove_all
(
void
)
{
chandev_free_all_queue
(
&
chandev_head
);
}
static
void
chandev_remove_model
(
chandev_model_info
*
model
)
{
chandev
*
curr_chandev
,
*
next_chandev
;
chandev_lock
();
for_each_allow_delete
(
curr_chandev
,
next_chandev
,(
chandev
*
)
chandev_head
.
head
)
if
(
curr_chandev
->
model_info
==
model
)
chandev_remove
(
curr_chandev
);
if
(
model
->
drinfo
.
oper_func
)
s390_device_unregister
(
&
model
->
drinfo
);
chandev_free_listmember
((
list
**
)
&
chandev_models_head
,(
list
*
)
model
);
chandev_unlock
();
}
static
void
chandev_remove_all_models
(
void
)
{
chandev_lock
();
while
(
chandev_models_head
)
chandev_remove_model
(
chandev_models_head
);
chandev_unlock
();
}
void
chandev_del_model
(
s32
cu_type
,
s16
cu_model
,
s32
dev_type
,
s16
dev_model
)
{
chandev_model_info
*
curr_model
,
*
next_model
;
chandev_lock
();
for_each_allow_delete
(
curr_model
,
next_model
,
chandev_models_head
)
if
((
curr_model
->
cu_type
==
cu_type
||
cu_type
==-
1
)
&&
(
curr_model
->
cu_model
==
cu_model
||
cu_model
==-
1
)
&&
(
curr_model
->
dev_type
==
dev_type
||
dev_type
==-
1
)
&&
(
curr_model
->
dev_model
==
dev_model
||
dev_model
==-
1
))
chandev_remove_model
(
curr_model
);
chandev_unlock
();
}
static
void
chandev_init_default_models
(
void
)
{
/* Usually P390/Planter 3172 emulation assume maximum 16 to be safe. */
chandev_add_model
(
chandev_type_lcs
,
0x3088
,
0x1
,
-
1
,
-
1
,
15
,
default_msck_bits
,
FALSE
,
FALSE
);
/* 3172/2216 Paralell the 2216 allows 16 ports per card the */
/* the original 3172 only allows 4 we will assume the max of 16 */
chandev_add_model
(
chandev_type_lcs
|
chandev_type_ctc
,
0x3088
,
0x8
,
-
1
,
-
1
,
15
,
default_msck_bits
,
FALSE
,
FALSE
);
/* 3172/2216 Escon serial the 2216 allows 16 ports per card the */
/* the original 3172 only allows 4 we will assume the max of 16 */
chandev_add_model
(
chandev_type_lcs
|
chandev_type_escon
,
0x3088
,
0x1F
,
-
1
,
-
1
,
15
,
default_msck_bits
,
FALSE
,
FALSE
);
/* Only 2 ports allowed on OSA2 cards model 0x60 */
chandev_add_model
(
chandev_type_lcs
,
0x3088
,
0x60
,
-
1
,
-
1
,
1
,
default_msck_bits
,
FALSE
,
FALSE
);
/* qeth gigabit ethernet */
chandev_add_model
(
chandev_type_qeth
,
0x1731
,
0x1
,
0x1732
,
0x1
,
0
,
default_msck_bits
,
FALSE
,
FALSE
);
chandev_add_model
(
chandev_type_qeth
,
0x1731
,
0x5
,
0x1732
,
0x5
,
0
,
default_msck_bits
,
FALSE
,
FALSE
);
/* Osa-D we currently aren't too emotionally involved with this */
chandev_add_model
(
chandev_type_osad
,
0x3088
,
0x62
,
-
1
,
-
1
,
0
,
default_msck_bits
,
FALSE
,
FALSE
);
/* claw */
chandev_add_model
(
chandev_type_claw
,
0x3088
,
0x61
,
-
1
,
-
1
,
0
,
default_msck_bits
,
FALSE
,
FALSE
);
/* ficon attached ctc */
chandev_add_model
(
chandev_type_escon
,
0x3088
,
0x1E
,
-
1
,
-
1
,
0
,
default_msck_bits
,
FALSE
,
FALSE
);
}
static
void
chandev_del_noauto
(
u16
devno
)
{
chandev_noauto_range
*
curr_noauto
,
*
next_noauto
;
chandev_lock
();
for_each_allow_delete
(
curr_noauto
,
next_noauto
,
chandev_noauto_head
)
if
(
curr_noauto
->
lo_devno
<=
devno
&&
curr_noauto
->
hi_devno
>=
devno
)
chandev_free_listmember
((
list
**
)
&
chandev_noauto_head
,(
list
*
)
curr_noauto
);
chandev_unlock
();
}
static
void
chandev_del_msck
(
u16
devno
)
{
chandev_msck_range
*
curr_msck_range
,
*
next_msck_range
;
chandev_lock
();
for_each_allow_delete
(
curr_msck_range
,
next_msck_range
,
chandev_msck_range_head
)
if
(
curr_msck_range
->
lo_devno
<=
devno
&&
curr_msck_range
->
hi_devno
>=
devno
)
chandev_free_listmember
((
list
**
)
&
chandev_msck_range_head
,(
list
*
)
curr_msck_range
);
chandev_unlock
();
}
static
void
chandev_add
(
s390_dev_info_t
*
newdevinfo
,
chandev_model_info
*
newmodelinfo
)
{
chandev
*
new_chandev
=
NULL
;
if
((
new_chandev
=
chandev_alloc
(
sizeof
(
chandev
))))
{
new_chandev
->
model_info
=
newmodelinfo
;
new_chandev
->
sch
.
devno
=
newdevinfo
->
devno
;
new_chandev
->
sch
.
irq
=
newdevinfo
->
irq
;
new_chandev
->
sch
.
cu_type
=
newdevinfo
->
sid_data
.
cu_type
;
/* control unit type */
new_chandev
->
sch
.
cu_model
=
newdevinfo
->
sid_data
.
cu_model
;
/* control unit model */
new_chandev
->
sch
.
dev_type
=
newdevinfo
->
sid_data
.
dev_type
;
/* device type */
new_chandev
->
sch
.
dev_model
=
newdevinfo
->
sid_data
.
dev_model
;
/* device model */
chandev_add_schib_info
(
newdevinfo
->
irq
,
&
new_chandev
->
sch
);
new_chandev
->
owned
=
(
newdevinfo
->
status
&
DEVSTAT_DEVICE_OWNED
?
TRUE
:
FALSE
);
chandev_queuemember
(
&
chandev_head
,
new_chandev
);
}
}
static
void
chandev_unregister_probe
(
chandev_probefunc
probefunc
)
{
chandev_probelist
*
curr_probe
,
*
next_probe
;
chandev_lock
();
for_each_allow_delete
(
curr_probe
,
next_probe
,
chandev_probelist_head
)
if
(
curr_probe
->
probefunc
==
probefunc
)
chandev_free_listmember
((
list
**
)
&
chandev_probelist_head
,
(
list
*
)
curr_probe
);
chandev_unlock
();
}
static
void
chandev_unregister_probe_by_chan_type
(
chandev_type
chan_type
)
{
chandev_probelist
*
curr_probe
,
*
next_probe
;
chandev_lock
();
for_each_allow_delete
(
curr_probe
,
next_probe
,
chandev_probelist_head
)
if
(
curr_probe
->
chan_type
==
chan_type
)
chandev_free_listmember
((
list
**
)
&
chandev_probelist_head
,
(
list
*
)
curr_probe
);
chandev_unlock
();
}
static
void
chandev_reset
(
void
)
{
chandev_lock
();
chandev_remove_all_models
();
chandev_free_all_list
((
list
**
)
&
chandev_noauto_head
);
chandev_free_all_list
((
list
**
)
&
chandev_msck_range_head
);
chandev_free_all_list
((
list
**
)
&
chandev_force_head
);
chandev_remove_parms
(
-
1
,
FALSE
,
-
1
);
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
chandev_use_devno_names
=
FALSE
;
#endif
chandev_persistent
=
0
;
chandev_unlock
();
}
static
int
chandev_is_chandev
(
int
irq
,
s390_dev_info_t
*
devinfo
,
chandev_force
**
forceinfo
,
chandev_model_info
**
ret_model
)
{
chandev_force
*
curr_force
;
chandev_model_info
*
curr_model
=
NULL
;
int
err
;
int
retval
=
FALSE
;
if
(
forceinfo
)
*
forceinfo
=
NULL
;
if
(
ret_model
)
*
ret_model
=
NULL
;
if
((
err
=
get_dev_info_by_irq
(
irq
,
devinfo
)))
{
printk
(
"chandev_is_chandev get_dev_info_by_irq reported err=%X on irq %d
\n
"
"should not happen
\n
"
,
err
,
irq
);
return
FALSE
;
}
chandev_lock
();
for_each
(
curr_model
,
chandev_models_head
)
{
if
(((
curr_model
->
cu_type
==
devinfo
->
sid_data
.
cu_type
)
||
(
curr_model
->
cu_type
==-
1
))
&&
((
curr_model
->
cu_model
==
devinfo
->
sid_data
.
cu_model
)
||
(
curr_model
->
cu_model
==-
1
))
&&
((
curr_model
->
dev_type
==
devinfo
->
sid_data
.
dev_type
)
||
(
curr_model
->
dev_type
==-
1
))
&&
((
curr_model
->
dev_model
==
devinfo
->
sid_data
.
dev_model
)
||
(
curr_model
->
dev_model
==-
1
)))
{
retval
=
TRUE
;
if
(
ret_model
)
*
ret_model
=
curr_model
;
break
;
}
}
for_each
(
curr_force
,
chandev_force_head
)
{
if
(((
curr_force
->
read_lo_devno
==
devinfo
->
devno
)
&&
(
curr_force
->
write_hi_devno
==
devinfo
->
devno
)
&&
(
curr_force
->
devif_num
!=-
2
))
||
((
curr_force
->
read_lo_devno
>=
devinfo
->
devno
)
&&
(
curr_force
->
write_hi_devno
<=
devinfo
->
devno
)
&&
(
curr_force
->
devif_num
==-
2
)))
{
if
(
forceinfo
)
*
forceinfo
=
curr_force
;
break
;
}
}
chandev_unlock
();
return
(
retval
);
}
static
void
chandev_collect_devices
(
void
)
{
int
curr_irq
,
loopcnt
=
0
;
s390_dev_info_t
curr_devinfo
;
chandev_model_info
*
curr_model
;
for
(
curr_irq
=
get_irq_first
();
curr_irq
>=
0
;
curr_irq
=
get_irq_next
(
curr_irq
))
{
/* check read chandev
* we had to do the cu_model check also because ctc devices
* have the same cutype & after asking some people
* the model numbers are given out pseudo randomly so
* we can't just take a range of them also the dev_type & models are 0
*/
loopcnt
++
;
if
(
loopcnt
>
0x10000
)
{
printk
(
KERN_ERR
"chandev_collect_devices detected infinite loop bug in get_irq_next
\n
"
);
break
;
}
chandev_lock
();
if
(
chandev_is_chandev
(
curr_irq
,
&
curr_devinfo
,
NULL
,
&
curr_model
))
chandev_add
(
&
curr_devinfo
,
curr_model
);
chandev_unlock
();
}
}
static
int
chandev_add_force
(
chandev_type
chan_type
,
s32
devif_num
,
u16
read_lo_devno
,
u16
write_hi_devno
,
u16
data_devno
,
s32
memory_usage_in_k
,
s16
port_protocol_no
,
u8
checksum_received_ip_pkts
,
u8
use_hw_stats
,
char
*
host_name
,
char
*
adapter_name
,
char
*
api_type
)
{
chandev_force
*
new_chandev_force
;
if
(
devif_num
==-
2
&&
read_lo_devno
>
write_hi_devno
)
{
printk
(
"chandev_add_force detected bad device range lo_devno=0x%04x hi_devno=0x%04x
\n
,"
,
(
int
)
read_lo_devno
,(
int
)
write_hi_devno
);
return
(
-
1
);
}
if
(
memory_usage_in_k
<
0
)
{
printk
(
"chandev_add_force memory_usage_in_k is bad
\n
"
);
return
(
-
1
);
}
if
(
chan_type
==
chandev_type_claw
)
{
int
host_name_len
=
strlen
(
host_name
),
adapter_name_len
=
strlen
(
adapter_name
),
api_type_len
=
strlen
(
api_type
);
if
(
host_name_len
>=
CLAW_NAMELEN
||
host_name_len
==
0
||
adapter_name_len
>=
CLAW_NAMELEN
||
adapter_name_len
==
0
||
api_type_len
>=
CLAW_NAMELEN
||
api_type_len
==
0
)
return
(
-
1
);
}
if
((
new_chandev_force
=
chandev_alloc
(
sizeof
(
chandev_force
))))
{
new_chandev_force
->
chan_type
=
chan_type
;
new_chandev_force
->
devif_num
=
devif_num
;
new_chandev_force
->
read_lo_devno
=
read_lo_devno
;
new_chandev_force
->
write_hi_devno
=
write_hi_devno
;
new_chandev_force
->
data_devno
=
data_devno
;
new_chandev_force
->
memory_usage_in_k
=
memory_usage_in_k
;
new_chandev_force
->
port_protocol_no
=
port_protocol_no
;
new_chandev_force
->
checksum_received_ip_pkts
=
checksum_received_ip_pkts
;
new_chandev_force
->
use_hw_stats
=
use_hw_stats
;
if
(
chan_type
==
chandev_type_claw
)
{
strcpy
(
new_chandev_force
->
claw
.
host_name
,
host_name
);
strcpy
(
new_chandev_force
->
claw
.
adapter_name
,
adapter_name
);
strcpy
(
new_chandev_force
->
claw
.
api_type
,
api_type
);
}
chandev_add_to_list
((
list
**
)
&
chandev_force_head
,
new_chandev_force
);
}
return
(
0
);
}
static
void
chandev_del_force
(
int
read_lo_devno
)
{
chandev_force
*
curr_force
,
*
next_force
;
chandev_lock
();
for_each_allow_delete
(
curr_force
,
next_force
,
chandev_force_head
)
{
if
(
curr_force
->
read_lo_devno
==
read_lo_devno
||
read_lo_devno
==-
1
)
chandev_free_listmember
((
list
**
)
&
chandev_force_head
,
(
list
*
)
curr_force
);
}
chandev_unlock
();
}
static
void
chandev_shutdown
(
chandev_activelist
*
curr_device
)
{
int
err
=
0
;
chandev_lock
();
/* unregister_netdev calls the dev->close so we shouldn't do this */
/* this otherwise we crash */
if
(
curr_device
->
unreg_dev
)
{
curr_device
->
unreg_dev
(
curr_device
->
dev_ptr
);
curr_device
->
unreg_dev
=
NULL
;
}
if
(
curr_device
->
shutdownfunc
)
{
err
=
curr_device
->
shutdownfunc
(
curr_device
->
dev_ptr
);
}
if
(
err
)
printk
(
"chandev_shutdown unable to fully shutdown & unload %s err=%d
\n
"
"probably some upper layer still requires the device to exist
\n
"
,
curr_device
->
devname
,
err
);
else
{
chandev_free_irq_by_irqinfo
(
curr_device
->
read_irqinfo
);
chandev_free_irq_by_irqinfo
(
curr_device
->
write_irqinfo
);
if
(
curr_device
->
data_irqinfo
)
chandev_free_irq_by_irqinfo
(
curr_device
->
data_irqinfo
);
chandev_free_listmember
((
list
**
)
&
chandev_activelist_head
,
(
list
*
)
curr_device
);
}
chandev_unlock
();
}
static
void
chandev_shutdown_all
(
void
)
{
while
(
chandev_activelist_head
)
chandev_shutdown
(
chandev_activelist_head
);
}
static
void
chandev_shutdown_by_name
(
char
*
devname
)
{
chandev_activelist
*
curr_device
;
chandev_lock
();
for_each
(
curr_device
,
chandev_activelist_head
)
if
(
strcmp
(
devname
,
curr_device
->
devname
)
==
0
)
{
chandev_shutdown
(
curr_device
);
break
;
}
chandev_unlock
();
}
static
chandev_activelist
*
chandev_active
(
u16
devno
)
{
chandev_activelist
*
curr_device
;
for_each
(
curr_device
,
chandev_activelist_head
)
if
(
curr_device
->
read_irqinfo
->
sch
.
devno
==
devno
||
curr_device
->
write_irqinfo
->
sch
.
devno
==
devno
||
(
curr_device
->
data_irqinfo
&&
curr_device
->
data_irqinfo
->
sch
.
devno
==
devno
))
{
return
(
curr_device
);
}
return
(
NULL
);
}
static
void
chandev_shutdown_by_devno
(
u16
devno
)
{
chandev_activelist
*
curr_device
;
chandev_lock
();
curr_device
=
chandev_active
(
devno
);
if
(
curr_device
)
chandev_shutdown
(
curr_device
);
chandev_unlock
();
}
static
int
chandev_pack_args
(
char
*
str
)
{
char
*
newstr
=
str
,
*
next
;
int
strcnt
=
1
;
while
(
*
str
)
{
next
=
str
+
1
;
/*remove dead spaces */
if
(
isspace
(
*
str
)
&&
isspace
(
*
next
))
{
str
++
;
continue
;
}
if
(
isspace
(
*
str
))
{
*
str
=
','
;
goto
pack_dn
;
}
if
(((
*
str
)
==
';'
)
&&
(
*
next
))
{
strcnt
++
;
*
str
=
0
;
}
pack_dn:
*
newstr
++=*
str
++
;
}
*
newstr
=
0
;
return
(
strcnt
);
}
typedef
enum
{
isnull
=
0
,
isstr
=
1
,
isnum
=
2
,
iscomma
=
4
,
}
chandev_strval
;
static
chandev_strval
chandev_strcmp
(
char
*
teststr
,
char
**
str
,
long
*
endlong
)
{
char
*
cur
;
chandev_strval
retval
=
isnull
;
int
len
=
strlen
(
teststr
);
if
(
strncmp
(
teststr
,
*
str
,
len
)
==
0
)
{
*
str
+=
len
;
retval
=
isstr
;
cur
=*
str
;
*
endlong
=
simple_strtol
(
cur
,
str
,
0
);
if
(
cur
!=*
str
)
retval
|=
isnum
;
if
(
**
str
==
','
)
{
retval
|=
iscomma
;
*
str
+=
1
;
}
else
if
(
**
str
!=
0
)
retval
=
isnull
;
}
return
(
retval
);
}
int
chandev_initdevice
(
chandev_probeinfo
*
probeinfo
,
void
*
dev_ptr
,
u8
port_no
,
char
*
devname
,
chandev_category
category
,
chandev_unregfunc
unreg_dev
)
{
chandev_activelist
*
newdevice
,
*
curr_device
;
chandev_interrupt_check
(
"chandev_initdevice"
);
if
(
probeinfo
->
newdevice
!=
NULL
)
{
printk
(
"probeinfo->newdevice!=NULL in chandev_initdevice for %s"
,
devname
);
return
(
-
EPERM
);
}
chandev_lock
();
for_each
(
curr_device
,
chandev_activelist_head
)
{
if
(
strcmp
(
curr_device
->
devname
,
devname
)
==
0
)
{
printk
(
"chandev_initdevice detected duplicate devicename %s
\n
"
,
devname
);
chandev_unlock
();
return
(
-
EPERM
);
}
}
if
((
newdevice
=
chandev_allocstr
(
devname
,
offsetof
(
chandev_activelist
,
devname
))))
{
newdevice
->
read_irqinfo
=
chandev_get_irqinfo_by_irq
(
probeinfo
->
read
.
irq
);
newdevice
->
write_irqinfo
=
chandev_get_irqinfo_by_irq
(
probeinfo
->
write
.
irq
);
if
(
probeinfo
->
data_exists
)
newdevice
->
data_irqinfo
=
chandev_get_irqinfo_by_irq
(
probeinfo
->
data
.
irq
);
chandev_unlock
();
if
(
newdevice
->
read_irqinfo
==
NULL
||
newdevice
->
write_irqinfo
==
NULL
||
(
probeinfo
->
data_exists
&&
newdevice
->
data_irqinfo
==
NULL
))
{
printk
(
"chandev_initdevice, it appears that chandev_request_irq was not "
"called for devname=%s read_irq=%d write_irq=%d data_irq=%d
\n
"
,
devname
,
probeinfo
->
read
.
irq
,
probeinfo
->
write
.
irq
,
probeinfo
->
data
.
irq
);
kfree
(
newdevice
);
return
(
-
EPERM
);
}
newdevice
->
chan_type
=
probeinfo
->
chan_type
;
newdevice
->
dev_ptr
=
dev_ptr
;
newdevice
->
port_no
=
port_no
;
newdevice
->
memory_usage_in_k
=
probeinfo
->
memory_usage_in_k
;
newdevice
->
category
=
category
;
newdevice
->
unreg_dev
=
unreg_dev
;
probeinfo
->
newdevice
=
newdevice
;
return
(
0
);
}
chandev_unlock
();
return
(
-
ENOMEM
);
}
char
*
chandev_build_device_name
(
chandev_probeinfo
*
probeinfo
,
char
*
destnamebuff
,
char
*
basename
,
int
buildfullname
)
{
if
(
chandev_use_devno_names
&&
(
!
probeinfo
->
device_forced
||
probeinfo
->
devif_num
==-
1
))
sprintf
(
destnamebuff
,
"%s%04x"
,
basename
,(
int
)
probeinfo
->
read
.
devno
);
else
{
if
(
probeinfo
->
devif_num
==-
1
)
{
if
(
buildfullname
)
{
int
idx
,
len
=
strlen
(
basename
);
chandev_activelist
*
curr_device
;
for
(
idx
=
0
;
idx
<
0xffff
;
idx
++
)
{
for_each
(
curr_device
,
chandev_activelist_head
)
{
if
(
strncmp
(
curr_device
->
devname
,
basename
,
len
)
==
0
)
{
char
numbuff
[
10
];
sprintf
(
numbuff
,
"%d"
,
idx
);
if
(
strcmp
(
&
curr_device
->
devname
[
len
],
numbuff
)
==
0
)
continue
;
}
}
sprintf
(
destnamebuff
,
"%s%d"
,
basename
,
idx
);
return
(
destnamebuff
);
}
printk
(
"chandev_build_device_name was usable to build a unique name for %s
\n
"
,
basename
);
return
(
NULL
);
}
else
sprintf
(
destnamebuff
,
"%s%%d"
,
basename
);
}
else
{
sprintf
(
destnamebuff
,
"%s%d"
,
basename
,(
int
)
probeinfo
->
devif_num
);
}
}
return
(
destnamebuff
);
}
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
struct
net_device
*
chandev_init_netdev
(
chandev_probeinfo
*
probeinfo
,
char
*
basename
,
struct
net_device
*
dev
,
int
sizeof_priv
,
struct
net_device
*
(
*
init_netdevfunc
)
(
struct
net_device
*
dev
,
int
sizeof_priv
))
#else
struct
device
*
chandev_init_netdev
(
chandev_probeinfo
*
probeinfo
,
char
*
basename
,
struct
device
*
dev
,
int
sizeof_priv
,
struct
device
*
(
*
init_netdevfunc
)
(
struct
device
*
dev
,
int
sizeof_priv
))
#endif
{
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
struct
net_device
*
retdevice
=
NULL
;
int
new_device
=
FALSE
;
#else
struct
device
*
retdevice
=
NULL
;
#endif
chandev_interrupt_check
(
"chandev_init_netdev"
);
if
(
!
init_netdevfunc
)
{
printk
(
"init_netdevfunc=NULL in chandev_init_netdev, it should not be valid.
\n
"
);
return
NULL
;
}
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
/* Allocate a device if one is not provided. */
if
(
dev
==
NULL
)
{
/* ensure 32-byte alignment of the private area */
int
alloc_size
=
sizeof
(
*
dev
)
+
sizeof_priv
+
31
;
dev
=
(
struct
net_device
*
)
kmalloc
(
alloc_size
,
GFP_KERNEL
);
if
(
dev
==
NULL
)
{
printk
(
KERN_ERR
"chandev_initnetdevice: Unable to allocate device memory.
\n
"
);
return
NULL
;
}
memset
(
dev
,
0
,
alloc_size
);
if
(
sizeof_priv
)
dev
->
priv
=
(
void
*
)
(((
long
)(
dev
+
1
)
+
31
)
&
~
31
);
new_device
=
TRUE
;
}
chandev_build_device_name
(
probeinfo
,
dev
->
name
,
basename
,
FALSE
);
#endif
retdevice
=
init_netdevfunc
(
dev
,
sizeof_priv
);
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
/* Register device if necessary */
/* we need to do this as init_netdev doesn't call register_netdevice */
/* for already allocated devices */
if
(
retdevice
&&
new_device
)
register_netdev
(
retdevice
);
#endif
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
/* We allocated it, so we should free it on error */
if
(
!
retdevice
&&
new_device
)
kfree
(
dev
);
#endif
return
retdevice
;
}
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
struct
net_device
*
chandev_initnetdevice
(
chandev_probeinfo
*
probeinfo
,
u8
port_no
,
struct
net_device
*
dev
,
int
sizeof_priv
,
char
*
basename
,
struct
net_device
*
(
*
init_netdevfunc
)(
struct
net_device
*
dev
,
int
sizeof_priv
),
void
(
*
unreg_netdevfunc
)(
struct
net_device
*
dev
))
#else
struct
device
*
chandev_initnetdevice
(
chandev_probeinfo
*
probeinfo
,
u8
port_no
,
struct
device
*
dev
,
int
sizeof_priv
,
char
*
basename
,
struct
device
*
(
*
init_netdevfunc
)(
struct
device
*
dev
,
int
sizeof_priv
),
void
(
*
unreg_netdevfunc
)(
struct
device
*
dev
))
#endif
{
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
struct
net_device
*
retdevice
=
NULL
;
int
new_device
=
(
dev
==
NULL
);
#else
struct
device
*
retdevice
=
NULL
;
#endif
if
(
!
unreg_netdevfunc
)
{
printk
(
"unreg_netdevfunc=NULL in chandev_initnetdevice, it should not be valid.
\n
"
);
return
NULL
;
}
chandev_interrupt_check
(
"chandev_initnetdevice"
);
retdevice
=
chandev_init_netdev
(
probeinfo
,
basename
,
dev
,
sizeof_priv
,
init_netdevfunc
);
if
(
retdevice
)
{
if
(
chandev_initdevice
(
probeinfo
,
retdevice
,
port_no
,
retdevice
->
name
,
chandev_category_network_device
,(
chandev_unregfunc
)
unreg_netdevfunc
))
{
unreg_netdevfunc
(
retdevice
);
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
/* We allocated it, so we should free it on error */
if
(
new_device
)
kfree
(
dev
);
#endif
retdevice
=
NULL
;
}
}
return
retdevice
;
}
static
int
chandev_compare_chpid_info
(
chandev_subchannel_info
*
chan1
,
chandev_subchannel_info
*
chan2
)
{
return
(
chan1
->
pim
!=
chan2
->
pim
||
*
chan1
->
chpid
!=*
chan2
->
chpid
);
}
static
int
chandev_compare_cu_dev_info
(
chandev_subchannel_info
*
chan1
,
chandev_subchannel_info
*
chan2
)
{
return
((
chan1
->
cu_type
!=
chan2
->
cu_type
)
||
(
chan1
->
cu_model
!=
chan2
->
cu_model
)
||
(
chan1
->
dev_type
!=
chan2
->
dev_type
)
||
(
chan1
->
dev_model
!=
chan2
->
dev_model
));
}
static
int
chandev_compare_subchannel_info
(
chandev_subchannel_info
*
chan1
,
chandev_subchannel_info
*
chan2
)
{
return
((
chan1
->
devno
==
chan2
->
devno
)
&&
(
chan1
->
cu_type
==
chan2
->
cu_type
)
&&
(
chan1
->
cu_model
==
chan2
->
cu_model
)
&&
(
chan1
->
dev_type
==
chan2
->
dev_type
)
&&
(
chan1
->
dev_model
==
chan2
->
dev_model
)
&&
(
chan1
->
pim
==
chan2
->
pim
)
&&
(
*
chan1
->
chpid
==
*
chan2
->
chpid
));
}
static
int
chandev_doprobe
(
chandev_force
*
force
,
chandev
*
read
,
chandev
*
write
,
chandev
*
data
)
{
chandev_probelist
*
probe
;
chandev_model_info
*
model_info
;
chandev_probeinfo
probeinfo
;
int
rc
=-
1
,
hint
=-
1
;
chandev_activelist
*
newdevice
;
chandev_probefunc
probefunc
;
chandev_parms
*
curr_parms
;
chandev_model_info
dummy_model_info
;
memset
(
&
probeinfo
,
0
,
sizeof
(
probeinfo
));
memset
(
&
dummy_model_info
,
0
,
sizeof
(
dummy_model_info
));
probeinfo
.
device_forced
=
(
force
!=
NULL
);
probeinfo
.
chpid_info_inconsistent
=
chandev_compare_chpid_info
(
&
read
->
sch
,
&
write
->
sch
)
||
(
data
&&
chandev_compare_chpid_info
(
&
read
->
sch
,
&
data
->
sch
));
probeinfo
.
cu_dev_info_inconsistent
=
chandev_compare_cu_dev_info
(
&
read
->
sch
,
&
write
->
sch
)
||
(
data
&&
chandev_compare_cu_dev_info
(
&
read
->
sch
,
&
data
->
sch
));
if
(
read
->
model_info
)
model_info
=
read
->
model_info
;
else
{
dummy_model_info
.
chan_type
=
chandev_type_none
;
dummy_model_info
.
max_port_no
=
16
;
model_info
=&
dummy_model_info
;
}
for_each
(
probe
,
chandev_probelist_head
)
{
if
(
force
)
probeinfo
.
chan_type
=
(
probe
->
chan_type
&
force
->
chan_type
);
else
{
if
(
chandev_cautious_auto_detect
)
probeinfo
.
chan_type
=
(
probe
->
chan_type
==
model_info
->
chan_type
?
probe
->
chan_type
:
chandev_type_none
);
else
probeinfo
.
chan_type
=
(
probe
->
chan_type
&
model_info
->
chan_type
);
}
if
(
probeinfo
.
chan_type
&&
(
force
||
(
!
probeinfo
.
cu_dev_info_inconsistent
&&
((
probe
->
chan_type
&
(
chandev_type_ctc
|
chandev_type_escon
))
||
!
probeinfo
.
chpid_info_inconsistent
))))
{
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
if
(
chandev_use_devno_names
)
probeinfo
.
devif_num
=
read
->
sch
.
devno
;
else
#endif
probeinfo
.
devif_num
=-
1
;
probeinfo
.
read
=
read
->
sch
;
probeinfo
.
write
=
write
->
sch
;
if
(
data
)
{
probeinfo
.
data
=
data
->
sch
;
probeinfo
.
data_exists
=
TRUE
;
}
probeinfo
.
max_port_no
=
(
force
&&
(
force
->
port_protocol_no
!=-
1
)
?
force
->
port_protocol_no
:
model_info
->
max_port_no
);
for_each
(
curr_parms
,
chandev_parms_head
)
{
if
(
probe
->
chan_type
==
curr_parms
->
chan_type
&&
read
->
sch
.
devno
>=
curr_parms
->
lo_devno
&&
read
->
sch
.
devno
<=
curr_parms
->
hi_devno
)
{
probeinfo
.
parmstr
=
curr_parms
->
parmstr
;
break
;
}
}
if
(
force
)
{
if
(
force
->
chan_type
==
chandev_type_claw
)
memcpy
(
&
probeinfo
.
claw
,
&
force
->
claw
,
sizeof
(
chandev_claw_info
));
probeinfo
.
port_protocol_no
=
force
->
port_protocol_no
;
if
(
force
->
devif_num
==-
1
&&
force
->
devif_num
==-
2
)
probeinfo
.
devif_num
=-
1
;
else
probeinfo
.
devif_num
=
force
->
devif_num
;
probeinfo
.
memory_usage_in_k
=
force
->
memory_usage_in_k
;
probeinfo
.
checksum_received_ip_pkts
=
force
->
checksum_received_ip_pkts
;
probeinfo
.
use_hw_stats
=
force
->
use_hw_stats
;
}
else
{
probeinfo
.
port_protocol_no
=
0
;
probeinfo
.
checksum_received_ip_pkts
=
model_info
->
default_checksum_received_ip_pkts
;
probeinfo
.
use_hw_stats
=
model_info
->
default_use_hw_stats
;
probeinfo
.
memory_usage_in_k
=
0
;
if
(
probe
->
chan_type
&
chandev_type_lcs
)
{
hint
=
(
read
->
sch
.
devno
&
0xFF
)
>>
1
;
if
(
hint
>
model_info
->
max_port_no
)
{
/* The card is possibly emulated e.g P/390 */
/* or possibly configured to use a shared */
/* port configured by osa-sf. */
hint
=
0
;
}
}
}
probeinfo
.
hint_port_no
=
hint
;
probefunc
=
probe
->
probefunc
;
rc
=
probefunc
(
&
probeinfo
);
if
(
rc
==
0
)
{
newdevice
=
probeinfo
.
newdevice
;
if
(
newdevice
)
{
newdevice
->
probefunc
=
probe
->
probefunc
;
newdevice
->
shutdownfunc
=
probe
->
shutdownfunc
;
newdevice
->
msck_notfunc
=
probe
->
msck_notfunc
;
probe
->
devices_found
++
;
chandev_add_to_list
((
list
**
)
&
chandev_activelist_head
,
newdevice
);
chandev_add_to_userland_notify_list
(
chandev_start
,
newdevice
->
devname
,
chandev_status_good
,
chandev_status_good
);
}
else
{
printk
(
"chandev_initdevice either failed or wasn't called for device read_irq=0x%04x
\n
"
,
probeinfo
.
read
.
irq
);
}
break
;
}
}
}
chandev_remove
(
read
);
chandev_remove
(
write
);
if
(
data
)
chandev_remove
(
data
);
return
(
rc
);
}
static
int
chandev_request_irq_from_irqinfo
(
chandev_irqinfo
*
irqinfo
,
chandev
*
this_chandev
)
{
int
retval
=
s390_request_irq_special
(
irqinfo
->
sch
.
irq
,
irqinfo
->
handler
,
chandev_not_oper_handler
,
irqinfo
->
irqflags
,
irqinfo
->
devname
,
irqinfo
->
dev_id
);
if
(
retval
==
0
)
{
irqinfo
->
msck_status
=
chandev_status_good
;
this_chandev
->
owned
=
TRUE
;
}
return
(
retval
);
}
static
void
chandev_irqallocerr
(
chandev_irqinfo
*
irqinfo
,
int
err
)
{
printk
(
"chandev_probe failed to realloc irq=%d for %s err=%d
\n
"
,
irqinfo
->
sch
.
irq
,
irqinfo
->
devname
,
err
);
}
static
void
chandev_call_notification_func
(
chandev_activelist
*
curr_device
,
chandev_irqinfo
*
curr_irqinfo
,
chandev_msck_status
prevstatus
)
{
if
(
curr_irqinfo
->
msck_status
!=
prevstatus
)
{
chandev_msck_status
new_msck_status
=
curr_irqinfo
->
msck_status
;
if
(
curr_irqinfo
->
msck_status
==
chandev_status_good
)
{
if
(
curr_device
->
read_irqinfo
->
msck_status
==
chandev_status_good
&&
curr_device
->
write_irqinfo
->
msck_status
==
chandev_status_good
)
{
if
(
curr_device
->
data_irqinfo
)
{
if
(
curr_device
->
data_irqinfo
->
msck_status
==
chandev_status_good
)
new_msck_status
=
chandev_status_all_chans_good
;
}
else
new_msck_status
=
chandev_status_all_chans_good
;
}
}
if
(
curr_device
->
msck_notfunc
)
{
curr_device
->
msck_notfunc
(
curr_device
->
dev_ptr
,
curr_irqinfo
->
sch
.
irq
,
prevstatus
,
new_msck_status
);
}
if
(
new_msck_status
!=
chandev_status_good
)
{
/* No point in sending a machine check if only one channel is good */
chandev_add_to_userland_notify_list
(
chandev_msck
,
curr_device
->
devname
,
prevstatus
,
curr_irqinfo
->
msck_status
);
}
}
}
static
int
chandev_find_eligible_channels
(
chandev
*
first_chandev_to_check
,
chandev
**
read
,
chandev
**
write
,
chandev
**
data
,
chandev
**
next
,
chandev_type
chan_type
)
{
chandev
*
curr_chandev
;
int
eligible_found
=
FALSE
,
changed
;
*
next
=
first_chandev_to_check
->
next
;
*
read
=*
write
=*
data
=
NULL
;
for_each
(
curr_chandev
,
first_chandev_to_check
)
if
((
curr_chandev
->
sch
.
devno
&
1
)
==
0
&&
curr_chandev
->
model_info
->
chan_type
!=
chandev_type_claw
)
{
*
read
=
curr_chandev
;
if
(
chan_type
==
chandev_type_none
)
chan_type
=
(
*
read
)
->
model_info
->
chan_type
;
break
;
}
if
(
*
read
)
{
for_each
(
curr_chandev
,(
chandev
*
)
chandev_head
.
head
)
if
((((
*
read
)
->
sch
.
devno
|
1
)
==
curr_chandev
->
sch
.
devno
)
&&
(
chandev_compare_cu_dev_info
(
&
(
*
read
)
->
sch
,
&
curr_chandev
->
sch
)
==
0
)
&&
((
chan_type
&
(
chandev_type_ctc
|
chandev_type_escon
))
||
chandev_compare_chpid_info
(
&
(
*
read
)
->
sch
,
&
curr_chandev
->
sch
)
==
0
))
{
*
write
=
curr_chandev
;
break
;
}
}
if
((
chan_type
&
chandev_type_qeth
))
{
if
(
*
write
)
{
for_each
(
curr_chandev
,(
chandev
*
)
chandev_head
.
head
)
if
((
curr_chandev
!=*
read
&&
curr_chandev
!=*
write
)
&&
(
chandev_compare_cu_dev_info
(
&
(
*
read
)
->
sch
,
&
curr_chandev
->
sch
)
==
0
)
&&
(
chandev_compare_chpid_info
(
&
(
*
read
)
->
sch
,
&
curr_chandev
->
sch
)
==
0
))
{
*
data
=
curr_chandev
;
break
;
}
if
(
*
data
)
eligible_found
=
TRUE
;
}
}
else
if
(
*
write
)
eligible_found
=
TRUE
;
if
(
eligible_found
)
{
do
{
changed
=
FALSE
;
if
(
*
next
&&
((
*
read
&&
(
*
read
==*
next
))
||
(
*
write
&&
(
*
write
==*
next
))
||
(
*
data
&&
(
*
data
==*
next
))))
{
*
next
=
(
*
next
)
->
next
;
changed
=
TRUE
;
}
}
while
(
changed
==
TRUE
);
}
return
(
eligible_found
);
}
static
chandev
*
chandev_get_free_chandev_by_devno
(
int
devno
)
{
chandev
*
curr_chandev
;
if
(
devno
==-
1
)
return
(
NULL
);
for_each
(
curr_chandev
,(
chandev
*
)
chandev_head
.
head
)
if
(
curr_chandev
->
sch
.
devno
==
devno
)
{
if
(
chandev_active
(
devno
))
return
(
NULL
);
else
return
(
curr_chandev
);
}
return
(
NULL
);
}
static
void
chandev_probe
(
void
)
{
chandev
*
read_chandev
,
*
write_chandev
,
*
data_chandev
,
*
curr_chandev
,
*
next_chandev
;
chandev_force
*
curr_force
;
chandev_noauto_range
*
curr_noauto
;
chandev_activelist
*
curr_device
;
chandev_irqinfo
*
curr_irqinfo
;
s390_dev_info_t
curr_devinfo
;
int
err
;
int
auto_msck_recovery
;
chandev_msck_status
prevstatus
;
chandev_msck_range
*
curr_msck_range
;
chandev_interrupt_check
(
"chandev_probe"
);
chandev_read_conf_if_necessary
();
chandev_collect_devices
();
chandev_lock
();
for_each
(
curr_irqinfo
,
chandev_irqinfo_head
)
{
if
((
curr_device
=
chandev_get_activelist_by_irq
(
curr_irqinfo
->
sch
.
irq
)))
{
prevstatus
=
curr_irqinfo
->
msck_status
;
if
(
curr_irqinfo
->
msck_status
!=
chandev_status_good
)
{
curr_chandev
=
chandev_get_by_irq
(
curr_irqinfo
->
sch
.
irq
);
if
(
curr_chandev
)
{
auto_msck_recovery
=
curr_chandev
->
model_info
->
auto_msck_recovery
;
}
else
goto
remove
;
for_each
(
curr_msck_range
,
chandev_msck_range_head
)
{
if
(
curr_msck_range
->
lo_devno
<=
curr_irqinfo
->
sch
.
devno
&&
curr_msck_range
->
hi_devno
>=
curr_irqinfo
->
sch
.
devno
)
{
auto_msck_recovery
=
curr_msck_range
->
auto_msck_recovery
;
break
;
}
}
if
((
1
<<
(
curr_irqinfo
->
msck_status
-
1
))
&
auto_msck_recovery
)
{
if
(
curr_irqinfo
->
msck_status
==
chandev_status_revalidate
)
{
if
((
get_dev_info_by_irq
(
curr_irqinfo
->
sch
.
irq
,
&
curr_devinfo
)
==
0
))
{
curr_irqinfo
->
sch
.
devno
=
curr_devinfo
.
devno
;
curr_irqinfo
->
msck_status
=
chandev_status_good
;
}
}
else
{
if
(
curr_chandev
)
{
/* Has the device reappeared */
if
(
chandev_compare_subchannel_info
(
&
curr_chandev
->
sch
,
&
curr_device
->
read_irqinfo
->
sch
)
||
chandev_compare_subchannel_info
(
&
curr_chandev
->
sch
,
&
curr_device
->
write_irqinfo
->
sch
)
||
(
curr_device
->
data_irqinfo
&&
chandev_compare_subchannel_info
(
&
curr_chandev
->
sch
,
&
curr_device
->
data_irqinfo
->
sch
)))
{
if
((
err
=
chandev_request_irq_from_irqinfo
(
curr_irqinfo
,
curr_chandev
))
==
0
)
curr_irqinfo
->
msck_status
=
chandev_status_good
;
else
chandev_irqallocerr
(
curr_irqinfo
,
err
);
}
}
}
}
}
chandev_call_notification_func
(
curr_device
,
curr_irqinfo
,
prevstatus
);
}
/* This is required because the device can go & come back */
/* even before we realize it is gone owing to the waits in our kernel threads */
/* & the device will be marked as not owned but its status will be good */
/* & an attempt to accidentally reprobe it may be done. */
remove:
chandev_remove
(
chandev_get_by_irq
(
curr_irqinfo
->
sch
.
irq
));
}
/* extra sanity */
for_each_allow_delete
(
curr_chandev
,
next_chandev
,(
chandev
*
)
chandev_head
.
head
)
if
(
curr_chandev
->
owned
)
chandev_remove
(
curr_chandev
);
for_each
(
curr_force
,
chandev_force_head
)
{
if
(
curr_force
->
devif_num
==-
2
)
{
for_each_allow_delete2
(
curr_chandev
,
next_chandev
,(
chandev
*
)
chandev_head
.
head
)
{
if
(
chandev_find_eligible_channels
(
curr_chandev
,
&
read_chandev
,
&
write_chandev
,
&
data_chandev
,
&
next_chandev
,
curr_force
->
chan_type
));
{
if
((
curr_force
->
read_lo_devno
>=
read_chandev
->
sch
.
devno
)
&&
(
curr_force
->
write_hi_devno
<=
read_chandev
->
sch
.
devno
)
&&
(
curr_force
->
read_lo_devno
>=
write_chandev
->
sch
.
devno
)
&&
(
curr_force
->
write_hi_devno
<=
write_chandev
->
sch
.
devno
)
&&
(
!
data_chandev
||
(
data_chandev
&&
(
curr_force
->
read_lo_devno
>=
data_chandev
->
sch
.
devno
)
&&
(
curr_force
->
write_hi_devno
<=
data_chandev
->
sch
.
devno
))))
chandev_doprobe
(
curr_force
,
read_chandev
,
write_chandev
,
data_chandev
);
}
}
}
else
{
read_chandev
=
chandev_get_free_chandev_by_devno
(
curr_force
->
read_lo_devno
);
if
(
read_chandev
)
{
write_chandev
=
chandev_get_free_chandev_by_devno
(
curr_force
->
write_hi_devno
);
if
(
write_chandev
)
{
if
(
curr_force
->
chan_type
==
chandev_type_qeth
)
{
data_chandev
=
chandev_get_free_chandev_by_devno
(
curr_force
->
data_devno
);
if
(
data_chandev
==
NULL
)
printk
(
"chandev_probe unable to force gigabit_ethernet driver invalid device no 0x%04x given
\n
"
,
curr_force
->
data_devno
);
}
else
data_chandev
=
NULL
;
chandev_doprobe
(
curr_force
,
read_chandev
,
write_chandev
,
data_chandev
);
}
}
}
}
for_each_allow_delete
(
curr_chandev
,
next_chandev
,(
chandev
*
)
chandev_head
.
head
)
{
for_each
(
curr_noauto
,
chandev_noauto_head
)
{
if
(
curr_chandev
->
sch
.
devno
>=
curr_noauto
->
lo_devno
&&
curr_chandev
->
sch
.
devno
<=
curr_noauto
->
hi_devno
)
{
chandev_remove
(
curr_chandev
);
break
;
}
}
}
for_each_allow_delete2
(
curr_chandev
,
next_chandev
,(
chandev
*
)
chandev_head
.
head
)
{
if
(
chandev_find_eligible_channels
(
curr_chandev
,
&
read_chandev
,
&
write_chandev
,
&
data_chandev
,
&
next_chandev
,
chandev_type_none
))
chandev_doprobe
(
NULL
,
read_chandev
,
write_chandev
,
data_chandev
);
}
chandev_remove_all
();
chandev_unlock
();
}
static
void
chandev_not_oper_func
(
int
irq
,
int
status
)
{
chandev_irqinfo
*
curr_irqinfo
;
chandev_activelist
*
curr_device
;
chandev_lock
();
for_each
(
curr_irqinfo
,
chandev_irqinfo_head
)
if
(
curr_irqinfo
->
sch
.
irq
==
irq
)
{
chandev_msck_status
prevstatus
=
curr_irqinfo
->
msck_status
;
switch
(
status
)
{
/* Currently defined but not used in kernel */
/* Despite being in specs */
case
DEVSTAT_NOT_OPER
:
curr_irqinfo
->
msck_status
=
chandev_status_not_oper
;
break
;
#ifdef DEVSTAT_NO_PATH
/* Kernel hasn't this defined currently. */
/* Despite being in specs */
case
DEVSTAT_NO_PATH
:
curr_irqinfo
->
msck_status
=
chandev_status_no_path
;
break
;
#endif
case
DEVSTAT_REVALIDATE
:
curr_irqinfo
->
msck_status
=
chandev_status_revalidate
;
break
;
case
DEVSTAT_DEVICE_GONE
:
curr_irqinfo
->
msck_status
=
chandev_status_gone
;
break
;
}
if
((
curr_device
=
chandev_get_activelist_by_irq
(
irq
)))
chandev_call_notification_func
(
curr_device
,
curr_irqinfo
,
prevstatus
);
else
printk
(
"chandev_not_oper_func received channel check for unowned irq %d"
,
irq
);
}
chandev_unlock
();
}
static
int
chandev_msck_thread
(
void
*
unused
)
{
int
loopcnt
,
not_oper_probe_required
=
FALSE
;
wait_queue_head_t
wait
;
chandev_not_oper_struct
*
new_not_oper
;
/* This loop exists because machine checks tend to come in groups & we have
to wait for the other devnos to appear also */
init_waitqueue_head
(
&
wait
);
for
(
loopcnt
=
0
;
loopcnt
<
10
||
(
jiffies
-
chandev_last_machine_check
)
<
HZ
;
loopcnt
++
)
{
sleep_on_timeout
(
&
wait
,
HZ
);
}
atomic_set
(
&
chandev_msck_thread_lock
,
1
);
while
(
!
atomic_compare_and_swap
(
TRUE
,
FALSE
,
&
chandev_new_msck
));
{
chandev_probe
();
}
while
(
TRUE
)
{
unsigned
long
flags
;
spin_lock_irqsave
(
&
chandev_not_oper_spinlock
,
flags
);
new_not_oper
=
(
chandev_not_oper_struct
*
)
dequeue_head
(
&
chandev_not_oper_head
);
spin_unlock_irqrestore
(
&
chandev_not_oper_spinlock
,
flags
);
if
(
new_not_oper
)
{
chandev_not_oper_func
(
new_not_oper
->
irq
,
new_not_oper
->
status
);
not_oper_probe_required
=
TRUE
;
kfree
(
new_not_oper
);
}
else
break
;
}
if
(
not_oper_probe_required
)
chandev_probe
();
return
(
0
);
}
static
void
chandev_msck_task
(
void
*
unused
)
{
if
(
kernel_thread
(
chandev_msck_thread
,
NULL
,
SIGCHLD
)
<
0
)
{
atomic_set
(
&
chandev_msck_thread_lock
,
1
);
printk
(
"error making chandev_msck_thread kernel thread
\n
"
);
}
}
static
char
*
argstrs
[]
=
{
"noauto"
,
"del_noauto"
,
"ctc"
,
"escon"
,
"lcs"
,
"osad"
,
"qeth"
,
"claw"
,
"add_parms"
,
"del_parms"
,
"del_force"
,
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
"use_devno_names"
,
"dont_use_devno_names"
,
#endif
"cautious_auto_detect"
,
"non_cautious_auto_detect"
,
"add_model"
,
"del_model"
,
"auto_msck"
,
"del_auto_msck"
,
"del_all_models"
,
"reset_conf_clean"
,
"reset_conf"
,
"shutdown"
,
"reprobe"
,
"unregister_probe"
,
"unregister_probe_by_chan_type"
,
"read_conf"
,
"dont_read_conf"
,
"persist"
};
typedef
enum
{
stridx_mult
=
256
,
first_stridx
=
0
,
noauto_stridx
=
first_stridx
,
del_noauto_stridx
,
ctc_stridx
,
escon_stridx
,
lcs_stridx
,
osad_stridx
,
qeth_stridx
,
claw_stridx
,
add_parms_stridx
,
del_parms_stridx
,
del_force_stridx
,
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
use_devno_names_stridx
,
dont_use_devno_names_stridx
,
#endif
cautious_auto_detect_stridx
,
non_cautious_auto_detect_stridx
,
add_model_stridx
,
del_model_stridx
,
auto_msck_stridx
,
del_auto_msck_stridx
,
del_all_models_stridx
,
reset_conf_clean_stridx
,
reset_conf_stridx
,
shutdown_stridx
,
reprobe_stridx
,
unregister_probe_stridx
,
unregister_probe_by_chan_type_stridx
,
read_conf_stridx
,
dont_read_conf_stridx
,
persist_stridx
,
last_stridx
,
}
chandev_str_enum
;
static
void
chandev_add_noauto
(
u16
lo_devno
,
u16
hi_devno
)
{
chandev_noauto_range
*
new_range
;
if
((
new_range
=
chandev_alloc
(
sizeof
(
chandev_noauto_range
))))
{
new_range
->
lo_devno
=
lo_devno
;
new_range
->
hi_devno
=
hi_devno
;
chandev_add_to_list
((
list
**
)
&
chandev_noauto_head
,
new_range
);
}
}
static
void
chandev_add_msck_range
(
u16
lo_devno
,
u16
hi_devno
,
int
auto_msck_recovery
)
{
chandev_msck_range
*
new_range
;
if
((
new_range
=
chandev_alloc
(
sizeof
(
chandev_msck_range
))))
{
new_range
->
lo_devno
=
lo_devno
;
new_range
->
hi_devno
=
hi_devno
;
new_range
->
auto_msck_recovery
=
auto_msck_recovery
;
chandev_add_to_list
((
list
**
)
&
chandev_msck_range_head
,
new_range
);
}
}
static
char
chandev_keydescript
[]
=
"
\n
chan_type key bitfield ctc=0x1,escon=0x2,lcs=0x4,osad=0x8,qeth=0x10,claw=0x20
\n
"
;
#if CONFIG_ARCH_S390X
/* We need this as we sometimes use this to evaluate pointers */
typedef
long
chandev_int
;
#else
typedef
int
chandev_int
;
#endif
#if (LINUX_VERSION_CODE<KERNEL_VERSION(2,3,0)) || (CONFIG_ARCH_S390X)
/*
* Read an int from an option string; if available accept a subsequent
* comma as well.
*
* Return values:
* 0 : no int in string
* 1 : int found, no subsequent comma
* 2 : int found including a subsequent comma
*/
static
chandev_int
chandev_get_option
(
char
**
str
,
chandev_int
*
pint
)
{
char
*
cur
=
*
str
;
if
(
!
cur
||
!
(
*
cur
))
return
0
;
*
pint
=
simple_strtol
(
cur
,
str
,
0
);
if
(
cur
==*
str
)
return
0
;
if
(
**
str
==
','
)
{
(
*
str
)
++
;
return
2
;
}
return
1
;
}
static
char
*
chandev_get_options
(
char
*
str
,
int
nints
,
chandev_int
*
ints
)
{
int
res
,
i
=
1
;
while
(
i
<
nints
)
{
res
=
chandev_get_option
(
&
str
,
ints
+
i
);
if
(
res
==
0
)
break
;
i
++
;
if
(
res
==
1
)
break
;
}
ints
[
0
]
=
i
-
1
;
return
(
str
);
}
#else
#define chandev_get_option get_option
#define chandev_get_options get_options
#endif
/*
* Read an string from an option string; if available accept a subsequent
* comma as well & set this comma to a null character when returning the string.
*
* Return values:
* 0 : no string found
* 1 : string found, no subsequent comma
* 2 : string found including a subsequent comma
*/
static
int
chandev_get_string
(
char
**
instr
,
char
**
outstr
)
{
char
*
cur
=
*
instr
;
if
(
!
cur
||*
cur
==
0
)
{
*
outstr
=
NULL
;
return
0
;
}
*
outstr
=*
instr
;
for
(;;)
{
if
(
*
(
++
cur
)
==
','
)
{
*
cur
=
0
;
*
instr
=
cur
+
1
;
return
2
;
}
else
if
(
*
cur
==
0
)
{
*
instr
=
cur
+
1
;
return
1
;
}
}
}
static
int
chandev_setup
(
int
in_read_conf
,
char
*
instr
,
char
*
errstr
,
int
lineno
)
{
chandev_strval
val
=
isnull
;
chandev_str_enum
stridx
;
long
endlong
;
chandev_type
chan_type
;
char
*
str
,
*
currstr
,
*
interpretstr
=
NULL
;
int
cnt
,
strcnt
;
int
retval
=
0
;
#define CHANDEV_MAX_EXTRA_INTS 12
chandev_int
ints
[
CHANDEV_MAX_EXTRA_INTS
+
1
];
currstr
=
alloca
(
strlen
(
instr
)
+
1
);
strcpy
(
currstr
,
instr
);
strcnt
=
chandev_pack_args
(
currstr
);
for
(
cnt
=
1
;
cnt
<=
strcnt
;
cnt
++
)
{
interpretstr
=
currstr
;
memset
(
ints
,
0
,
sizeof
(
ints
));
for
(
stridx
=
first_stridx
;
stridx
<
last_stridx
;
stridx
++
)
{
str
=
currstr
;
if
((
val
=
chandev_strcmp
(
argstrs
[
stridx
],
&
str
,
&
endlong
)))
break
;
}
currstr
=
str
;
if
(
val
)
{
val
=
(((
chandev_strval
)
stridx
)
*
stridx_mult
)
+
(
val
&~
isstr
);
switch
(
val
)
{
case
(
add_parms_stridx
*
stridx_mult
)
|
iscomma
:
currstr
=
chandev_get_options
(
currstr
,
4
,
ints
);
if
(
*
currstr
&&
ints
[
0
]
>=
1
)
{
if
(
ints
[
0
]
==
1
)
{
ints
[
2
]
=
0
;
ints
[
3
]
=
0xffff
;
}
else
if
(
ints
[
0
]
==
2
)
ints
[
3
]
=
ints
[
2
];
chandev_add_parms
(
ints
[
1
],
ints
[
2
],
ints
[
3
],
currstr
);
goto
NextOption
;
}
else
goto
BadArgs
;
break
;
case
(
claw_stridx
*
stridx_mult
)
|
isnum
|
iscomma
:
case
(
claw_stridx
*
stridx_mult
)
|
iscomma
:
currstr
=
chandev_get_options
(
str
,
6
,
ints
);
break
;
default:
if
(
val
&
iscomma
)
currstr
=
chandev_get_options
(
str
,
CHANDEV_MAX_EXTRA_INTS
,
ints
);
break
;
}
switch
(
val
)
{
case
noauto_stridx
*
stridx_mult
:
case
(
noauto_stridx
*
stridx_mult
)
|
iscomma
:
switch
(
ints
[
0
])
{
case
0
:
chandev_free_all_list
((
list
**
)
&
chandev_noauto_head
);
chandev_add_noauto
(
0
,
0xffff
);
break
;
case
1
:
ints
[
2
]
=
ints
[
1
];
case
2
:
chandev_add_noauto
(
ints
[
1
],
ints
[
2
]);
break
;
default:
goto
BadArgs
;
}
break
;
case
(
auto_msck_stridx
*
stridx_mult
)
|
iscomma
:
switch
(
ints
[
0
])
{
case
1
:
chandev_free_all_list
((
list
**
)
&
chandev_msck_range_head
);
chandev_add_msck_range
(
0
,
0xffff
,
ints
[
1
]);
break
;
case
2
:
chandev_add_msck_range
(
ints
[
1
],
ints
[
1
],
ints
[
2
]);
break
;
case
3
:
chandev_add_msck_range
(
ints
[
1
],
ints
[
2
],
ints
[
3
]);
break
;
default:
goto
BadArgs
;
}
case
del_auto_msck_stridx
*
stridx_mult
:
case
(
del_auto_msck_stridx
*
stridx_mult
)
|
iscomma
:
switch
(
ints
[
0
])
{
case
0
:
chandev_free_all_list
((
list
**
)
&
chandev_msck_range_head
);
break
;
case
1
:
chandev_del_msck
(
ints
[
1
]);
default:
goto
BadArgs
;
}
case
del_noauto_stridx
*
stridx_mult
:
chandev_free_all_list
((
list
**
)
&
chandev_noauto_head
);
break
;
case
(
del_noauto_stridx
*
stridx_mult
)
|
iscomma
:
if
(
ints
[
0
]
==
1
)
chandev_del_noauto
(
ints
[
1
]);
else
goto
BadArgs
;
break
;
case
(
qeth_stridx
*
stridx_mult
)
|
isnum
|
iscomma
:
if
(
ints
[
0
]
<
3
||
ints
[
0
]
>
7
)
goto
BadArgs
;
chandev_add_force
(
chandev_type_qeth
,
endlong
,
ints
[
1
],
ints
[
2
],
ints
[
3
],
ints
[
4
],
ints
[
5
],
ints
[
6
],
ints
[
7
],
NULL
,
NULL
,
NULL
);
break
;
case
(
ctc_stridx
*
stridx_mult
)
|
isnum
|
iscomma
:
case
(
escon_stridx
*
stridx_mult
)
|
isnum
|
iscomma
:
case
(
lcs_stridx
*
stridx_mult
)
|
isnum
|
iscomma
:
case
(
osad_stridx
*
stridx_mult
)
|
isnum
|
iscomma
:
case
(
ctc_stridx
*
stridx_mult
)
|
iscomma
:
case
(
escon_stridx
*
stridx_mult
)
|
iscomma
:
case
(
lcs_stridx
*
stridx_mult
)
|
iscomma
:
case
(
osad_stridx
*
stridx_mult
)
|
iscomma
:
switch
(
val
&~
(
isnum
|
iscomma
))
{
case
(
ctc_stridx
*
stridx_mult
):
chan_type
=
chandev_type_ctc
;
break
;
case
(
escon_stridx
*
stridx_mult
):
chan_type
=
chandev_type_escon
;
break
;
case
(
lcs_stridx
*
stridx_mult
):
chan_type
=
chandev_type_lcs
;
break
;
case
(
osad_stridx
*
stridx_mult
):
chan_type
=
chandev_type_osad
;
break
;
case
(
qeth_stridx
*
stridx_mult
):
chan_type
=
chandev_type_qeth
;
break
;
default:
goto
BadArgs
;
}
if
((
val
&
isnum
)
==
0
)
endlong
=-
2
;
if
(
ints
[
0
]
<
2
||
ints
[
0
]
>
6
)
goto
BadArgs
;
chandev_add_force
(
chan_type
,
endlong
,
ints
[
1
],
ints
[
2
],
0
,
ints
[
3
],
ints
[
4
],
ints
[
5
],
ints
[
6
],
NULL
,
NULL
,
NULL
);
break
;
case
(
claw_stridx
*
stridx_mult
)
|
isnum
|
iscomma
:
case
(
claw_stridx
*
stridx_mult
)
|
iscomma
:
if
(
ints
[
0
]
>=
2
&&
ints
[
0
]
<=
5
)
{
char
*
host_name
,
*
adapter_name
,
*
api_type
;
char
*
clawstr
=
alloca
(
strlen
(
currstr
)
+
1
);
strcpy
(
clawstr
,
currstr
);
if
(
!
(
chandev_get_string
(
&
clawstr
,
&
host_name
)
==
2
&&
chandev_get_string
(
&
clawstr
,
&
adapter_name
)
==
2
&&
chandev_get_string
(
&
clawstr
,
&
api_type
)
==
1
&&
chandev_add_force
(
chandev_type_claw
,
endlong
,
ints
[
1
],
ints
[
2
],
0
,
ints
[
3
],
0
,
ints
[
4
],
ints
[
5
],
host_name
,
adapter_name
,
api_type
)
==
0
))
goto
BadArgs
;
}
else
goto
BadArgs
;
break
;
case
(
del_parms_stridx
*
stridx_mult
):
ints
[
1
]
=-
1
;
case
(
del_parms_stridx
*
stridx_mult
)
|
iscomma
:
if
(
ints
[
0
]
==
0
)
ints
[
1
]
=-
1
;
if
(
ints
[
0
]
<=
1
)
ints
[
2
]
=
FALSE
;
if
(
ints
[
0
]
<=
2
)
ints
[
3
]
=-
1
;
if
(
ints
[
0
]
>
3
)
goto
BadArgs
;
chandev_remove_parms
(
ints
[
1
],
ints
[
2
],
ints
[
3
]);
break
;
case
(
del_force_stridx
*
stridx_mult
)
|
iscomma
:
if
(
ints
[
0
]
!=
1
)
goto
BadArgs
;
chandev_del_force
(
ints
[
1
]);
break
;
case
(
del_force_stridx
*
stridx_mult
):
chandev_del_force
(
-
1
);
break
;
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
case
(
use_devno_names_stridx
*
stridx_mult
):
chandev_use_devno_names
=
TRUE
;
break
;
case
(
dont_use_devno_names_stridx
*
stridx_mult
):
chandev_use_devno_names
=
FALSE
;
break
;
#endif
case
(
cautious_auto_detect_stridx
*
stridx_mult
):
chandev_cautious_auto_detect
=
TRUE
;
break
;
case
(
non_cautious_auto_detect_stridx
*
stridx_mult
):
chandev_cautious_auto_detect
=
FALSE
;
break
;
case
(
add_model_stridx
*
stridx_mult
)
|
iscomma
:
if
(
ints
[
0
]
<
3
)
goto
BadArgs
;
if
(
ints
[
0
]
==
3
)
ints
[
4
]
=-
1
;
if
(
ints
[
0
]
<=
4
)
ints
[
5
]
=-
1
;
if
(
ints
[
0
]
<=
5
)
ints
[
6
]
=-
1
;
if
(
ints
[
0
]
<=
6
)
ints
[
7
]
=
default_msck_bits
;
if
(
ints
[
0
]
<=
7
)
ints
[
8
]
=
FALSE
;
if
(
ints
[
0
]
<=
8
)
ints
[
9
]
=
FALSE
;
ints
[
0
]
=
7
;
chandev_add_model
(
ints
[
1
],
ints
[
2
],
ints
[
3
],
ints
[
4
],
ints
[
5
],
ints
[
6
],
ints
[
7
],
ints
[
8
],
ints
[
9
]);
break
;
case
(
del_model_stridx
*
stridx_mult
)
|
iscomma
:
if
(
ints
[
0
]
<
2
||
ints
[
0
]
>
4
)
goto
BadArgs
;
if
(
ints
[
0
]
<
3
)
ints
[
3
]
=-
2
;
if
(
ints
[
0
]
<
4
)
ints
[
4
]
=-
2
;
ints
[
0
]
=
4
;
chandev_del_model
(
ints
[
1
],
ints
[
2
],
ints
[
3
],
ints
[
4
]);
break
;
case
del_all_models_stridx
*
stridx_mult
:
chandev_remove_all_models
();
break
;
case
reset_conf_stridx
*
stridx_mult
:
chandev_reset
();
chandev_init_default_models
();
break
;
case
reset_conf_clean_stridx
*
stridx_mult
:
chandev_reset
();
break
;
case
shutdown_stridx
*
stridx_mult
:
chandev_shutdown_all
();
break
;
case
(
shutdown_stridx
*
stridx_mult
)
|
iscomma
:
switch
(
ints
[
0
])
{
case
0
:
if
(
strlen
(
str
))
chandev_shutdown_by_name
(
str
);
else
goto
BadArgs
;
break
;
case
1
:
chandev_shutdown_by_devno
(
ints
[
1
]);
break
;
default:
goto
BadArgs
;
}
break
;
case
reprobe_stridx
*
stridx_mult
:
chandev_probe
();
break
;
case
unregister_probe_stridx
*
stridx_mult
:
chandev_free_all_list
((
list
**
)
&
chandev_probelist_head
);
break
;
case
(
unregister_probe_stridx
*
stridx_mult
)
|
iscomma
:
if
(
ints
[
0
]
!=
1
)
goto
BadArgs
;
chandev_unregister_probe
((
chandev_probefunc
)
ints
[
1
]);
break
;
case
(
unregister_probe_by_chan_type_stridx
*
stridx_mult
)
|
iscomma
:
if
(
ints
[
0
]
!=
1
)
goto
BadArgs
;
chandev_unregister_probe_by_chan_type
((
chandev_type
)
ints
[
1
]);
break
;
case
read_conf_stridx
*
stridx_mult
:
if
(
in_read_conf
)
{
printk
(
"attempt to recursively call read_conf
\n
"
);
goto
BadArgs
;
}
chandev_read_conf
();
break
;
case
dont_read_conf_stridx
*
stridx_mult
:
atomic_set
(
&
chandev_conf_read
,
TRUE
);
break
;
case
(
persist_stridx
*
stridx_mult
)
|
iscomma
:
if
(
ints
[
0
]
==
1
)
chandev_persistent
=
ints
[
1
];
else
goto
BadArgs
;
break
;
default:
goto
BadArgs
;
}
}
else
goto
BadArgs
;
NextOption:
if
(
cnt
<
strcnt
)
{
/* eat up stuff till next string */
while
(
*
(
currstr
++
));
}
}
retval
=
1
;
BadArgs:
if
(
!
retval
)
{
printk
(
"chandev_setup %s %s"
,(
val
==
0
?
"unknown verb"
:
"bad argument"
),
instr
);
if
(
errstr
)
{
printk
(
"%s %d interpreted as %s"
,
errstr
,
lineno
,
interpretstr
);
if
(
strcnt
>
1
)
{
if
(
cnt
==
strcnt
)
printk
(
" after the last semicolon
\n
"
);
else
printk
(
" before semicolon no %d"
,
cnt
);
}
}
printk
(
".
\n
Type man chandev for more info.
\n\n
"
);
}
return
(
retval
);
}
#define CHANDEV_KEYWORD "chandev="
static
int
chandev_setup_bootargs
(
char
*
str
,
int
paramno
)
{
int
len
;
char
*
copystr
;
for
(
len
=
0
;
str
[
len
]
!=
0
&&!
isspace
(
str
[
len
]);
len
++
);
copystr
=
alloca
(
len
+
1
);
strncpy
(
copystr
,
str
,
len
);
copystr
[
len
]
=
0
;
if
(
chandev_setup
(
FALSE
,
copystr
,
"at "
CHANDEV_KEYWORD
" bootparam no"
,
paramno
)
==
0
)
return
(
0
);
return
(
len
);
}
/*
We can't parse using a __setup function as kmalloc isn't available
at this time.
*/
static
void
__init
chandev_parse_args
(
void
)
{
#define CHANDEV_KEYWORD "chandev="
extern
char
saved_command_line
[];
int
cnt
,
len
,
paramno
=
1
;
len
=
strlen
(
saved_command_line
)
-
sizeof
(
CHANDEV_KEYWORD
);
for
(
cnt
=
0
;
cnt
<
len
;
cnt
++
)
{
if
(
strncmp
(
&
saved_command_line
[
cnt
],
CHANDEV_KEYWORD
,
sizeof
(
CHANDEV_KEYWORD
)
-
1
)
==
0
)
{
cnt
+=
(
sizeof
(
CHANDEV_KEYWORD
)
-
1
);
cnt
+=
chandev_setup_bootargs
(
&
saved_command_line
[
cnt
],
paramno
);
paramno
++
;
}
}
}
static
int
chandev_do_setup
(
int
in_read_conf
,
char
*
buff
,
int
size
)
{
int
curr
,
comment
=
FALSE
,
newline
=
FALSE
,
oldnewline
=
TRUE
;
char
*
startline
=
NULL
,
*
endbuff
=&
buff
[
size
];
int
lineno
=
0
;
*
endbuff
=
0
;
for
(;
buff
<=
endbuff
;
curr
++
,
buff
++
)
{
if
(
*
buff
==
0xa
||*
buff
==
0xc
||*
buff
==
0
)
{
if
(
*
buff
==
0xa
||*
buff
==
0
)
lineno
++
;
*
buff
=
0
;
newline
=
TRUE
;
}
else
{
newline
=
FALSE
;
if
(
*
buff
==
'#'
)
comment
=
TRUE
;
}
if
(
comment
==
TRUE
)
*
buff
=
0
;
if
(
startline
==
NULL
&&
isalpha
(
*
buff
))
startline
=
buff
;
if
(
startline
&&
(
buff
>
startline
)
&&
(
oldnewline
==
FALSE
)
&&
(
newline
==
TRUE
))
{
if
((
chandev_setup
(
in_read_conf
,
startline
,
" on line no"
,
lineno
))
==
0
)
return
(
-
EINVAL
);
startline
=
NULL
;
}
if
(
newline
)
comment
=
FALSE
;
oldnewline
=
newline
;
}
return
(
0
);
}
static
void
chandev_read_conf
(
void
)
{
#define CHANDEV_FILE "/etc/chandev.conf"
struct
stat
statbuf
;
char
*
buff
;
int
curr
,
left
,
len
,
fd
;
mm_segment_t
oldfs
;
/* if called from chandev_register_and_probe &
the driver is compiled into the kernel the
parameters will need to be passed in from
the kernel boot parameter line as the root
fs is not mounted yet, we can't wait here.
*/
if
(
in_interrupt
()
||
current
->
fs
->
root
==
NULL
)
return
;
atomic_set
(
&
chandev_conf_read
,
TRUE
);
oldfs
=
get_fs
();
set_fs
(
KERNEL_DS
);
if
(
stat
(
CHANDEV_FILE
,
&
statbuf
)
==
0
)
{
set_fs
(
USER_DS
);
buff
=
vmalloc
(
statbuf
.
st_size
+
1
);
if
(
buff
)
{
set_fs
(
KERNEL_DS
);
if
((
fd
=
open
(
CHANDEV_FILE
,
O_RDONLY
,
0
))
!=-
1
)
{
curr
=
0
;
left
=
statbuf
.
st_size
;
while
((
len
=
read
(
fd
,
&
buff
[
curr
],
left
))
>
0
)
{
curr
+=
len
;
left
-=
len
;
}
close
(
fd
);
}
set_fs
(
USER_DS
);
chandev_do_setup
(
TRUE
,
buff
,
statbuf
.
st_size
);
vfree
(
buff
);
}
}
set_fs
(
oldfs
);
}
static
void
chandev_read_conf_if_necessary
(
void
)
{
if
(
in_interrupt
()
||
current
->
fs
->
root
==
NULL
)
return
;
if
(
!
atomic_compare_and_swap
(
FALSE
,
TRUE
,
&
chandev_conf_read
))
chandev_read_conf
();
}
#ifdef CONFIG_PROC_FS
#define chandev_printf(exitchan,args...) \
splen=sprintf(spbuff,##args); \
spoffset+=splen; \
if(spoffset>offset) { \
spbuff+=splen; \
currlen+=splen; \
} \
if(currlen>=length) \
goto exitchan;
static
void
sprintf_msck
(
char
*
buff
,
int
auto_msck_recovery
)
{
chandev_msck_status
idx
;
int
first_time
=
TRUE
;
buff
[
0
]
=
0
;
for
(
idx
=
chandev_status_first_msck
;
idx
<
chandev_status_last_msck
;
idx
++
)
{
if
((
1
<<
(
idx
-
1
))
&
auto_msck_recovery
)
{
buff
+=
sprintf
(
buff
,
"%s%s"
,(
first_time
?
""
:
","
),
msck_status_strs
[
idx
]);
first_time
=
FALSE
;
}
}
}
static
int
chandev_read_proc
(
char
*
page
,
char
**
start
,
off_t
offset
,
int
length
,
int
*
eof
,
void
*
data
)
{
char
*
spbuff
=*
start
=
page
;
int
currlen
=
0
,
splen
=
0
;
off_t
spoffset
=
0
;
chandev_model_info
*
curr_model
;
chandev_noauto_range
*
curr_noauto
;
chandev_force
*
curr_force
;
chandev_activelist
*
curr_device
;
chandev_probelist
*
curr_probe
;
chandev_msck_range
*
curr_msck_range
;
s390_dev_info_t
curr_devinfo
;
int
pass
,
chandevs_detected
,
curr_irq
,
loopcnt
;
chandev_irqinfo
*
read_irqinfo
,
*
write_irqinfo
,
*
data_irqinfo
;
char
buff
[
3
][
80
];
chandev_lock
();
chandev_printf
(
chan_exit
,
"
\n
%s
\n
"
"*'s for cu/dev type/models indicate don't cares
\n
"
,
chandev_keydescript
);
chandev_printf
(
chan_exit
,
"
\n
cautious_auto_detect: %s
\n
"
,
chandev_cautious_auto_detect
?
"on"
:
"off"
);
chandev_printf
(
chan_exit
,
"
\n
persist = 0x%02x
\n
"
,
chandev_persistent
);
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
chandev_printf
(
chan_exit
,
"
\n
use_devno_names: %s
\n\n
"
,
chandev_use_devno_names
?
"on"
:
"off"
);
#endif
if
(
chandev_models_head
)
{
chandev_printf
(
chan_exit
,
"Channels enabled for detection
\n
"
);
chandev_printf
(
chan_exit
,
" chan cu cu dev dev max checksum use hw auto recovery
\n
"
);
chandev_printf
(
chan_exit
,
" type type model type model port_no. received stats type
\n
"
);
chandev_printf
(
chan_exit
,
"==============================================================================
\n
"
);
for_each
(
curr_model
,
chandev_models_head
)
{
chandev_sprint_devinfo
(
buff
[
0
],
curr_model
->
cu_type
,
curr_model
->
cu_model
,
curr_model
->
dev_type
,
curr_model
->
dev_model
);
sprintf_msck
(
buff
[
1
],
curr_model
->
auto_msck_recovery
);
chandev_printf
(
chan_exit
,
" 0x%02x %s%3d %s %s %s
\n
"
,
curr_model
->
chan_type
,
buff
[
0
],
(
int
)
curr_model
->
max_port_no
,
curr_model
->
default_checksum_received_ip_pkts
?
"yes"
:
"no "
,
curr_model
->
default_use_hw_stats
?
"yes"
:
"no "
,
buff
[
1
]);
}
}
if
(
chandev_noauto_head
)
{
chandev_printf
(
chan_exit
,
"
\n
No auto devno ranges
\n
"
);
chandev_printf
(
chan_exit
,
" From To
\n
"
);
chandev_printf
(
chan_exit
,
"====================
\n
"
);
for_each
(
curr_noauto
,
chandev_noauto_head
)
{
chandev_printf
(
chan_exit
,
" 0x%04x 0x%04x
\n
"
,
curr_noauto
->
lo_devno
,
curr_noauto
->
hi_devno
);
}
}
if
(
chandev_msck_range_head
)
{
chandev_printf
(
chan_exit
,
"
\n
Automatic machine check recovery devno ranges
\n
"
);
chandev_printf
(
chan_exit
,
" From To automatic recovery type
\n
"
);
chandev_printf
(
chan_exit
,
"===========================================
\n
"
);
for_each
(
curr_msck_range
,
chandev_msck_range_head
)
{
sprintf_msck
(
buff
[
0
],
curr_msck_range
->
auto_msck_recovery
);
chandev_printf
(
chan_exit
,
" 0x%04x 0x%04x %s
\n
"
,
curr_msck_range
->
lo_devno
,
curr_msck_range
->
hi_devno
,
buff
[
0
])
}
}
if
(
chandev_force_head
)
{
chandev_printf
(
chan_exit
,
"
\n
Forced devices
\n
"
);
chandev_printf
(
chan_exit
,
" chan defif read write data memory port ip hw host adapter api
\n
"
);
chandev_printf
(
chan_exit
,
" type num devno devno devno usage(k) protocol no. chksum stats name name name
\n
"
);
chandev_printf
(
chan_exit
,
"===============================================================================================
\n
"
);
for_each
(
curr_force
,
chandev_force_head
)
{
if
(
curr_force
->
memory_usage_in_k
==
0
)
strcpy
(
buff
[
0
],
"default"
);
else
sprintf
(
buff
[
0
],
"%6d"
,
curr_force
->
memory_usage_in_k
);
chandev_printf
(
chan_exit
,
" 0x%02x %3d 0x%04x 0x%04x 0x%04x %7s %3d %1d %1d%s"
,
(
int
)
curr_force
->
chan_type
,(
int
)
curr_force
->
devif_num
,
(
int
)
curr_force
->
read_lo_devno
,(
int
)
curr_force
->
write_hi_devno
,
(
int
)
curr_force
->
data_devno
,
buff
[
0
],
(
int
)
curr_force
->
port_protocol_no
,(
int
)
curr_force
->
checksum_received_ip_pkts
,
(
int
)
curr_force
->
use_hw_stats
,
curr_force
->
chan_type
==
chandev_type_claw
?
""
:
"
\n
"
);
if
(
curr_force
->
chan_type
==
chandev_type_claw
)
{
chandev_printf
(
chan_exit
,
" %9s %9s %9s
\n
"
,
curr_force
->
claw
.
host_name
,
curr_force
->
claw
.
adapter_name
,
curr_force
->
claw
.
api_type
);
}
}
}
if
(
chandev_probelist_head
)
{
#if CONFIG_ARCH_S390X
chandev_printf
(
chan_exit
,
"
\n
Registered probe functions
\n
"
"probefunc shutdownfunc msck_notfunc chan devices devices
\n
"
" type found active
\n
"
"==================================================================================
\n
"
);
#else
chandev_printf
(
chan_exit
,
"
\n
Registered probe functions
\n
"
"probefunc shutdownfunc msck_notfunc chan devices devices
\n
"
" type found active
\n
"
"===============================================================
\n
"
);
#endif
for_each
(
curr_probe
,
chandev_probelist_head
)
{
int
devices_active
=
0
;
for_each
(
curr_device
,
chandev_activelist_head
)
{
if
(
curr_device
->
probefunc
==
curr_probe
->
probefunc
)
devices_active
++
;
}
chandev_printf
(
chan_exit
,
"0x%p 0x%p 0x%p 0x%02x %d %d
\n
"
,
curr_probe
->
probefunc
,
curr_probe
->
shutdownfunc
,
curr_probe
->
msck_notfunc
,
curr_probe
->
chan_type
,
curr_probe
->
devices_found
,
devices_active
);
}
}
if
(
chandev_activelist_head
)
{
unsigned
long
long
total_memory_usage_in_k
=
0
;
chandev_printf
(
chan_exit
,
"
\n
Initialised Devices
\n
"
" read write data read write data chan port dev dev memory read msck write msck data msck
\n
"
" irq irq irq devno devno devno type no. ptr name usage(k) status status status
\n
"
"=====================================================================================================================
\n
"
);
/* We print this list backwards for cosmetic reasons */
for
(
curr_device
=
chandev_activelist_head
;
curr_device
->
next
!=
NULL
;
curr_device
=
curr_device
->
next
);
while
(
curr_device
)
{
read_irqinfo
=
curr_device
->
read_irqinfo
;
write_irqinfo
=
curr_device
->
write_irqinfo
;
data_irqinfo
=
curr_device
->
data_irqinfo
;
if
(
data_irqinfo
)
{
sprintf
(
buff
[
0
],
"0x%04x"
,
data_irqinfo
->
sch
.
irq
);
sprintf
(
buff
[
1
],
"0x%04x"
,(
int
)
data_irqinfo
->
sch
.
devno
);
}
else
{
strcpy
(
buff
[
0
],
" n/a "
);
strcpy
(
buff
[
1
],
" n/a "
);
}
if
(
curr_device
->
memory_usage_in_k
<
0
)
{
sprintf
(
buff
[
2
],
"%d"
,(
int
)
-
curr_device
->
memory_usage_in_k
);
total_memory_usage_in_k
-=
curr_device
->
memory_usage_in_k
;
}
else
strcpy
(
buff
[
2
],
" n/a "
);
chandev_printf
(
chan_exit
,
"0x%04x 0x%04x %s 0x%04x 0x%04x %s 0x%02x %2d 0x%p %-10s %6s %-12s %-12s %-12s
\n
"
,
read_irqinfo
->
sch
.
irq
,
write_irqinfo
->
sch
.
irq
,
buff
[
0
],
(
int
)
read_irqinfo
->
sch
.
devno
,
(
int
)
write_irqinfo
->
sch
.
devno
,
buff
[
1
],
curr_device
->
chan_type
,(
int
)
curr_device
->
port_no
,
curr_device
->
dev_ptr
,
curr_device
->
devname
,
buff
[
2
],
msck_status_strs
[
read_irqinfo
->
msck_status
],
msck_status_strs
[
write_irqinfo
->
msck_status
],
data_irqinfo
?
msck_status_strs
[
data_irqinfo
->
msck_status
]
:
"not applicable"
);
get_prev
((
list
*
)
chandev_activelist_head
,
(
list
*
)
curr_device
,
(
list
**
)
&
curr_device
);
}
chandev_printf
(
chan_exit
,
"
\n
Total device memory usage %Luk.
\n
"
,
total_memory_usage_in_k
);
}
chandevs_detected
=
FALSE
;
for
(
pass
=
FALSE
;
pass
<=
TRUE
;
pass
++
)
{
if
(
pass
&&
chandevs_detected
)
{
chandev_printf
(
chan_exit
,
"
\n
channels detected
\n
"
);
chandev_printf
(
chan_exit
,
" chan cu cu dev dev in chandev
\n
"
);
chandev_printf
(
chan_exit
,
" irq devno type type model type model pim chpids use reg.
\n
"
);
chandev_printf
(
chan_exit
,
"===============================================================================
\n
"
);
}
for
(
curr_irq
=
get_irq_first
(),
loopcnt
=
0
;
curr_irq
>=
0
;
curr_irq
=
get_irq_next
(
curr_irq
),
loopcnt
++
)
{
if
(
loopcnt
>
0x10000
)
{
printk
(
KERN_ERR
"chandev_read_proc detected infinite loop bug in get_irq_next
\n
"
);
goto
chan_error
;
}
if
(
chandev_is_chandev
(
curr_irq
,
&
curr_devinfo
,
&
curr_force
,
&
curr_model
))
{
schib_t
*
curr_schib
;
curr_schib
=
s390_get_schib
(
curr_irq
);
chandevs_detected
=
TRUE
;
if
(
pass
)
{
chandev_printf
(
chan_exit
,
"0x%04x 0x%04x 0x%02x 0x%04x 0x%02x 0x%04x 0x%02x 0x%02x 0x%016Lx %-5s%-5s
\n
"
,
curr_irq
,
curr_devinfo
.
devno
,
(
curr_force
?
curr_force
->
chan_type
:
(
curr_model
?
curr_model
->
chan_type
:
chandev_type_none
)),
(
int
)
curr_devinfo
.
sid_data
.
cu_type
,
(
int
)
curr_devinfo
.
sid_data
.
cu_model
,
(
int
)
curr_devinfo
.
sid_data
.
dev_type
,
(
int
)
curr_devinfo
.
sid_data
.
dev_model
,
(
int
)(
curr_schib
?
curr_schib
->
pmcw
.
pim
:
0
),
*
(
long
long
*
)(
curr_schib
?
&
curr_schib
->
pmcw
.
chpid
[
0
]
:
0
),
(
curr_devinfo
.
status
&
DEVSTAT_DEVICE_OWNED
)
?
"yes"
:
"no "
,
(
chandev_get_irqinfo_by_irq
(
curr_irq
)
?
"yes"
:
"no "
));
}
}
}
}
if
(
chandev_parms_head
)
{
chandev_parms
*
curr_parms
;
chandev_printf
(
chan_exit
,
"
\n
driver specific parameters
\n
"
);
chandev_printf
(
chan_exit
,
"chan lo hi driver
\n
"
);
chandev_printf
(
chan_exit
,
"type devno devno parameters
\n
"
);
chandev_printf
(
chan_exit
,
"=============================================================================
\n
"
);
for_each
(
curr_parms
,
chandev_parms_head
)
{
chandev_printf
(
chan_exit
,
"0x%02x 0x%04x 0x%04x %s
\n
"
,
curr_parms
->
chan_type
,(
int
)
curr_parms
->
lo_devno
,
(
int
)
curr_parms
->
hi_devno
,
curr_parms
->
parmstr
);
}
}
chan_error:
*
eof
=
TRUE
;
chan_exit:
if
(
currlen
>
length
)
{
/* rewind to previous printf so that we are correctly
* aligned if we get called to print another page.
*/
currlen
-=
splen
;
}
chandev_unlock
();
return
(
currlen
);
}
static
int
chandev_write_proc
(
struct
file
*
file
,
const
char
*
buffer
,
unsigned
long
count
,
void
*
data
)
{
int
rc
;
char
*
buff
;
if
((
buff
=
vmalloc
(
count
+
1
))
==
0
)
return
-
ENOMEM
;
if
(
copy_from_user
(
buff
,
buffer
,
count
)
==
0
)
{
chandev_do_setup
(
FALSE
,
buff
,
count
);
rc
=
count
;
}
else
{
rc
=-
EFAULT
;
}
vfree
(
buff
);
return
rc
;
}
static
void
__init
chandev_create_proc
(
void
)
{
struct
proc_dir_entry
*
dir_entry
=
create_proc_entry
(
"chandev"
,
0644
,
&
proc_root
);
if
(
dir_entry
)
{
dir_entry
->
read_proc
=&
chandev_read_proc
;
dir_entry
->
write_proc
=&
chandev_write_proc
;
}
}
#endif
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
static
#endif
int
__init
chandev_init
(
void
)
{
atomic_set
(
&
chandev_initialised
,
TRUE
);
chandev_parse_args
();
chandev_init_default_models
();
#if CONFIG_PROC_FS
chandev_create_proc
();
#endif
chandev_last_startmsck_list_update
=
chandev_last_machine_check
=
jiffies
-
HZ
;
atomic_set
(
&
chandev_msck_thread_lock
,
1
);
chandev_lock_owner
=
CHANDEV_INVALID_LOCK_OWNER
;
chandev_lock_cnt
=
0
;
spin_lock_init
(
&
chandev_spinlock
);
spin_lock_init
(
&
chandev_not_oper_spinlock
);
atomic_set
(
&
chandev_new_msck
,
FALSE
);
return
(
0
);
}
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
__initcall
(
chandev_init
);
#endif
int
chandev_register_and_probe
(
chandev_probefunc
probefunc
,
chandev_shutdownfunc
shutdownfunc
,
chandev_msck_notification_func
msck_notfunc
,
chandev_type
chan_type
)
{
chandev_probelist
*
new_probe
,
*
curr_probe
;
/* Avoid chicked & egg situations where we may be called before we */
/* are initialised. */
chandev_interrupt_check
(
"chandev_register_and_probe"
);
if
(
!
atomic_compare_and_swap
(
FALSE
,
TRUE
,
&
chandev_initialised
))
chandev_init
();
chandev_lock
();
for_each
(
curr_probe
,
chandev_probelist_head
)
{
if
(
curr_probe
->
probefunc
==
probefunc
)
{
chandev_unlock
();
printk
(
"chandev_register_and_probe detected duplicate probefunc %p"
" for chan_type 0x%02x
\n
"
,
probefunc
,
chan_type
);
return
(
-
EPERM
);
}
}
chandev_unlock
();
if
((
new_probe
=
chandev_alloc
(
sizeof
(
chandev_probelist
))))
{
new_probe
->
probefunc
=
probefunc
;
new_probe
->
shutdownfunc
=
shutdownfunc
;
new_probe
->
msck_notfunc
=
msck_notfunc
;
new_probe
->
chan_type
=
chan_type
;
new_probe
->
devices_found
=
0
;
chandev_add_to_list
((
list
**
)
&
chandev_probelist_head
,
new_probe
);
chandev_probe
();
}
return
(
new_probe
?
new_probe
->
devices_found
:-
ENOMEM
);
}
void
chandev_unregister
(
chandev_probefunc
probefunc
,
int
call_shutdown
)
{
chandev_probelist
*
curr_probe
;
chandev_activelist
*
curr_device
,
*
next_device
;
chandev_interrupt_check
(
"chandev_unregister"
);
chandev_lock
();
for_each
(
curr_probe
,
chandev_probelist_head
)
{
if
(
curr_probe
->
probefunc
==
probefunc
)
{
for_each_allow_delete
(
curr_device
,
next_device
,
chandev_activelist_head
)
if
(
curr_device
->
probefunc
==
probefunc
&&
call_shutdown
)
chandev_shutdown
(
curr_device
);
chandev_free_listmember
((
list
**
)
&
chandev_probelist_head
,
(
list
*
)
curr_probe
);
break
;
}
}
chandev_unlock
();
}
int
chandev_persist
(
chandev_type
chan_type
)
{
return
((
chandev_persistent
&
chan_type
)
?
TRUE
:
FALSE
);
}
EXPORT_SYMBOL
(
chandev_register_and_probe
);
EXPORT_SYMBOL
(
chandev_request_irq
);
EXPORT_SYMBOL
(
chandev_unregister
);
EXPORT_SYMBOL
(
chandev_initdevice
);
EXPORT_SYMBOL
(
chandev_build_device_name
);
EXPORT_SYMBOL
(
chandev_initnetdevice
);
/* see chandev.h as to why this is exported */
EXPORT_SYMBOL
(
chandev_init_netdev
);
EXPORT_SYMBOL
(
chandev_use_devno_names
);
EXPORT_SYMBOL
(
chandev_free_irq
);
/* see chandev.h as to why chandev_add_model and chandev_del_model
* are exported */
EXPORT_SYMBOL
(
chandev_add_model
);
EXPORT_SYMBOL
(
chandev_del_model
);
EXPORT_SYMBOL
(
chandev_persist
);
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment