blob: 0fb89baf41824e573454996e9d4747475b91a851 [file] [log] [blame]
/*
* arch/s390/kernel/s390dyn.c
* S/390 dynamic device attachment
*
* S390 version
* Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Ingo Adlung (adlung@de.ibm.com)
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/smp_lock.h>
#include <asm/irq.h>
#include <asm/s390io.h>
#include <asm/s390dyn.h>
static struct list_head devreg_anchor = LIST_HEAD_INIT(devreg_anchor);
static spinlock_t dyn_lock = SPIN_LOCK_UNLOCKED;
static inline int s390_device_register_internal(devreg_t *drinfo)
{
struct list_head *p;
list_for_each(p, &devreg_anchor) {
devreg_t *pdevreg = list_entry(p, devreg_t, list);
if (pdevreg == drinfo)
return -EINVAL;
/*
* We don't allow multiple drivers to register
* for the same device number
*/
if (pdevreg->ci.devno == drinfo->ci.devno &&
(pdevreg->flag & DEVREG_TYPE_DEVNO) &&
(drinfo->flag & DEVREG_TYPE_DEVNO))
return -EBUSY;
if (drinfo->flag == (DEVREG_TYPE_DEVCHARS |
DEVREG_EXACT_MATCH) &&
!memcmp(&drinfo->ci.hc, &pdevreg->ci.hc,
sizeof(devreg_hc_t)))
return -EBUSY;
}
/*
* no collision found, enqueue
*/
list_add (&drinfo->list, &devreg_anchor);
return 0;
}
int s390_device_register( devreg_t *drinfo )
{
unsigned long flags;
int ret;
if (drinfo == NULL ||
!(drinfo->flag & (DEVREG_TYPE_DEVNO | DEVREG_TYPE_DEVCHARS)))
return -EINVAL;
spin_lock_irqsave (&dyn_lock, flags);
ret = s390_device_register_internal(drinfo);
spin_unlock_irqrestore( &dyn_lock, flags );
return ret;
}
static inline int s390_device_unregister_internal(devreg_t *dreg)
{
struct list_head *p;
list_for_each(p, &devreg_anchor) {
devreg_t *pdevreg = list_entry(p, devreg_t, list);
if (pdevreg == dreg) {
list_del (&dreg->list);
return 0;
}
}
return -EINVAL;
}
int s390_device_unregister(devreg_t *dreg)
{
unsigned long flags;
int ret;
if (dreg == NULL)
return -EINVAL;
spin_lock_irqsave(&dyn_lock, flags);
ret = s390_device_unregister_internal(dreg);
spin_unlock_irqrestore(&dyn_lock, flags);
return ret;
}
static inline devreg_t *s390_search_devreg_internal(ioinfo_t *ioinfo)
{
struct list_head *p;
list_for_each(p, &devreg_anchor) {
devreg_t *pdevreg = list_entry(p, devreg_t, list);
senseid_t *sid;
int flag;
flag = pdevreg->flag;
sid = &ioinfo->senseid;
if (flag & DEVREG_TYPE_DEVNO) {
if (ioinfo->ui.flags.dval != 1 ||
ioinfo->devno != pdevreg->ci.devno)
continue;
} else if (flag & DEVREG_TYPE_DEVCHARS) {
if ( (flag & DEVREG_MATCH_CU_TYPE) &&
pdevreg->ci.hc.ctype != sid->cu_type )
continue;
if ( (flag & DEVREG_MATCH_CU_MODEL) &&
pdevreg->ci.hc.cmode != sid->cu_model )
continue;
if ( (flag & DEVREG_MATCH_DEV_TYPE) &&
pdevreg->ci.hc.dtype != sid->dev_type )
continue;
if ( (flag & DEVREG_MATCH_DEV_MODEL) &&
pdevreg->ci.hc.dmode != sid->dev_model )
continue;
} else {
continue;
}
return pdevreg;
}
return NULL;
}
devreg_t * s390_search_devreg( ioinfo_t *ioinfo )
{
unsigned long flags;
devreg_t *pdevreg;
if (ioinfo == NULL)
return NULL;
spin_lock_irqsave(&dyn_lock, flags);
pdevreg = s390_search_devreg_internal(ioinfo);
spin_unlock_irqrestore(&dyn_lock, flags);
return pdevreg;
}
EXPORT_SYMBOL(s390_device_register);
EXPORT_SYMBOL(s390_device_unregister);