?? device.c
?? linux 內(nèi)核源代碼
?? C
?? 第 1 頁(yè) / 共 3 頁(yè)
字號(hào):
??
/* * drivers/s390/cio/device.c * bus driver for ccw devices * * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, * IBM Corporation * Author(s): Arnd Bergmann (arndb@de.ibm.com) * Cornelia Huck (cornelia.huck@de.ibm.com) * Martin Schwidefsky (schwidefsky@de.ibm.com) */#include <linux/module.h>#include <linux/init.h>#include <linux/spinlock.h>#include <linux/errno.h>#include <linux/err.h>#include <linux/slab.h>#include <linux/list.h>#include <linux/device.h>#include <linux/workqueue.h>#include <asm/ccwdev.h>#include <asm/cio.h>#include <asm/param.h> /* HZ */#include <asm/cmb.h>#include "cio.h"#include "cio_debug.h"#include "css.h"#include "device.h"#include "ioasm.h"/******************* bus type handling ***********************//* The Linux driver model distinguishes between a bus type and * the bus itself. Of course we only have one channel * subsystem driver and one channel system per machine, but * we still use the abstraction. T.R. says it's a good idea. */static intccw_bus_match (struct device * dev, struct device_driver * drv){ struct ccw_device *cdev = to_ccwdev(dev); struct ccw_driver *cdrv = to_ccwdrv(drv); const struct ccw_device_id *ids = cdrv->ids, *found; if (!ids) return 0; found = ccw_device_id_match(ids, &cdev->id); if (!found) return 0; cdev->id.driver_info = found->driver_info; return 1;}/* Store modalias string delimited by prefix/suffix string into buffer with * specified size. Return length of resulting string (excluding trailing '\0') * even if string doesn't fit buffer (snprintf semantics). */static int snprint_alias(char *buf, size_t size, struct ccw_device_id *id, const char *suffix){ int len; len = snprintf(buf, size, "ccw:t%04Xm%02X", id->cu_type, id->cu_model); if (len > size) return len; buf += len; size -= len; if (id->dev_type != 0) len += snprintf(buf, size, "dt%04Xdm%02X%s", id->dev_type, id->dev_model, suffix); else len += snprintf(buf, size, "dtdm%s", suffix); return len;}/* Set up environment variables for ccw device uevent. Return 0 on success, * non-zero otherwise. */static int ccw_uevent(struct device *dev, struct kobj_uevent_env *env){ struct ccw_device *cdev = to_ccwdev(dev); struct ccw_device_id *id = &(cdev->id); int ret; char modalias_buf[30]; /* CU_TYPE= */ ret = add_uevent_var(env, "CU_TYPE=%04X", id->cu_type); if (ret) return ret; /* CU_MODEL= */ ret = add_uevent_var(env, "CU_MODEL=%02X", id->cu_model); if (ret) return ret; /* The next two can be zero, that's ok for us */ /* DEV_TYPE= */ ret = add_uevent_var(env, "DEV_TYPE=%04X", id->dev_type); if (ret) return ret; /* DEV_MODEL= */ ret = add_uevent_var(env, "DEV_MODEL=%02X", id->dev_model); if (ret) return ret; /* MODALIAS= */ snprint_alias(modalias_buf, sizeof(modalias_buf), id, ""); ret = add_uevent_var(env, "MODALIAS=%s", modalias_buf); return ret;}struct bus_type ccw_bus_type;static int io_subchannel_probe (struct subchannel *);static int io_subchannel_remove (struct subchannel *);static int io_subchannel_notify(struct device *, int);static void io_subchannel_verify(struct device *);static void io_subchannel_ioterm(struct device *);static void io_subchannel_shutdown(struct subchannel *);static struct css_driver io_subchannel_driver = { .subchannel_type = SUBCHANNEL_TYPE_IO, .drv = { .name = "io_subchannel", .bus = &css_bus_type, }, .irq = io_subchannel_irq, .notify = io_subchannel_notify, .verify = io_subchannel_verify, .termination = io_subchannel_ioterm, .probe = io_subchannel_probe, .remove = io_subchannel_remove, .shutdown = io_subchannel_shutdown,};struct workqueue_struct *ccw_device_work;struct workqueue_struct *ccw_device_notify_work;wait_queue_head_t ccw_device_init_wq;atomic_t ccw_device_init_count;static int __initinit_ccw_bus_type (void){ int ret; init_waitqueue_head(&ccw_device_init_wq); atomic_set(&ccw_device_init_count, 0); ccw_device_work = create_singlethread_workqueue("cio"); if (!ccw_device_work) return -ENOMEM; /* FIXME: better errno ? */ ccw_device_notify_work = create_singlethread_workqueue("cio_notify"); if (!ccw_device_notify_work) { ret = -ENOMEM; /* FIXME: better errno ? */ goto out_err; } slow_path_wq = create_singlethread_workqueue("kslowcrw"); if (!slow_path_wq) { ret = -ENOMEM; /* FIXME: better errno ? */ goto out_err; } if ((ret = bus_register (&ccw_bus_type))) goto out_err; if ((ret = driver_register(&io_subchannel_driver.drv))) goto out_err; wait_event(ccw_device_init_wq, atomic_read(&ccw_device_init_count) == 0); flush_workqueue(ccw_device_work); return 0;out_err: if (ccw_device_work) destroy_workqueue(ccw_device_work); if (ccw_device_notify_work) destroy_workqueue(ccw_device_notify_work); if (slow_path_wq) destroy_workqueue(slow_path_wq); return ret;}static void __exitcleanup_ccw_bus_type (void){ driver_unregister(&io_subchannel_driver.drv); bus_unregister(&ccw_bus_type); destroy_workqueue(ccw_device_notify_work); destroy_workqueue(ccw_device_work);}subsys_initcall(init_ccw_bus_type);module_exit(cleanup_ccw_bus_type);/************************ device handling **************************//* * A ccw_device has some interfaces in sysfs in addition to the * standard ones. * The following entries are designed to export the information which * resided in 2.4 in /proc/subchannels. Subchannel and device number * are obvious, so they don't have an entry :) * TODO: Split chpids and pimpampom up? Where is "in use" in the tree? */static ssize_tchpids_show (struct device * dev, struct device_attribute *attr, char * buf){ struct subchannel *sch = to_subchannel(dev); struct chsc_ssd_info *ssd = &sch->ssd_info; ssize_t ret = 0; int chp; int mask; for (chp = 0; chp < 8; chp++) { mask = 0x80 >> chp; if (ssd->path_mask & mask) ret += sprintf(buf + ret, "%02x ", ssd->chpid[chp].id); else ret += sprintf(buf + ret, "00 "); } ret += sprintf (buf+ret, "\n"); return min((ssize_t)PAGE_SIZE, ret);}static ssize_tpimpampom_show (struct device * dev, struct device_attribute *attr, char * buf){ struct subchannel *sch = to_subchannel(dev); struct pmcw *pmcw = &sch->schib.pmcw; return sprintf (buf, "%02x %02x %02x\n", pmcw->pim, pmcw->pam, pmcw->pom);}static ssize_tdevtype_show (struct device *dev, struct device_attribute *attr, char *buf){ struct ccw_device *cdev = to_ccwdev(dev); struct ccw_device_id *id = &(cdev->id); if (id->dev_type != 0) return sprintf(buf, "%04x/%02x\n", id->dev_type, id->dev_model); else return sprintf(buf, "n/a\n");}static ssize_tcutype_show (struct device *dev, struct device_attribute *attr, char *buf){ struct ccw_device *cdev = to_ccwdev(dev); struct ccw_device_id *id = &(cdev->id); return sprintf(buf, "%04x/%02x\n", id->cu_type, id->cu_model);}static ssize_tmodalias_show (struct device *dev, struct device_attribute *attr, char *buf){ struct ccw_device *cdev = to_ccwdev(dev); struct ccw_device_id *id = &(cdev->id); int len; len = snprint_alias(buf, PAGE_SIZE, id, "\n"); return len > PAGE_SIZE ? PAGE_SIZE : len;}static ssize_tonline_show (struct device *dev, struct device_attribute *attr, char *buf){ struct ccw_device *cdev = to_ccwdev(dev); return sprintf(buf, cdev->online ? "1\n" : "0\n");}int ccw_device_is_orphan(struct ccw_device *cdev){ return sch_is_pseudo_sch(to_subchannel(cdev->dev.parent));}static void ccw_device_unregister(struct ccw_device *cdev){ if (test_and_clear_bit(1, &cdev->private->registered)) device_del(&cdev->dev);}static void ccw_device_remove_orphan_cb(struct device *dev){ struct ccw_device *cdev = to_ccwdev(dev); ccw_device_unregister(cdev); put_device(&cdev->dev);}static void ccw_device_remove_sch_cb(struct device *dev){ struct subchannel *sch; sch = to_subchannel(dev); css_sch_device_unregister(sch); /* Reset intparm to zeroes. */ sch->schib.pmcw.intparm = 0; cio_modify(sch); put_device(&sch->dev);}static voidccw_device_remove_disconnected(struct ccw_device *cdev){ unsigned long flags; int rc; /* * Forced offline in disconnected state means * 'throw away device'. */ if (ccw_device_is_orphan(cdev)) { /* * Deregister ccw device. * Unfortunately, we cannot do this directly from the * attribute method. */ spin_lock_irqsave(cdev->ccwlock, flags); cdev->private->state = DEV_STATE_NOT_OPER; spin_unlock_irqrestore(cdev->ccwlock, flags); rc = device_schedule_callback(&cdev->dev, ccw_device_remove_orphan_cb); if (rc) CIO_MSG_EVENT(2, "Couldn't unregister orphan " "0.%x.%04x\n", cdev->private->dev_id.ssid, cdev->private->dev_id.devno); return; } /* Deregister subchannel, which will kill the ccw device. */ rc = device_schedule_callback(cdev->dev.parent, ccw_device_remove_sch_cb); if (rc) CIO_MSG_EVENT(2, "Couldn't unregister disconnected device " "0.%x.%04x\n", cdev->private->dev_id.ssid, cdev->private->dev_id.devno);}/** * ccw_device_set_offline() - disable a ccw device for I/O * @cdev: target ccw device * * This function calls the driver's set_offline() function for @cdev, if * given, and then disables @cdev. * Returns: * %0 on success and a negative error value on failure. * Context: * enabled, ccw device lock not held */int ccw_device_set_offline(struct ccw_device *cdev){ int ret; if (!cdev) return -ENODEV; if (!cdev->online || !cdev->drv) return -EINVAL; if (cdev->drv->set_offline) { ret = cdev->drv->set_offline(cdev); if (ret != 0) return ret; } cdev->online = 0; spin_lock_irq(cdev->ccwlock); ret = ccw_device_offline(cdev); if (ret == -ENODEV) { if (cdev->private->state != DEV_STATE_NOT_OPER) { cdev->private->state = DEV_STATE_OFFLINE; dev_fsm_event(cdev, DEV_EVENT_NOTOPER); } spin_unlock_irq(cdev->ccwlock); return ret; } spin_unlock_irq(cdev->ccwlock); if (ret == 0) wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); else { CIO_MSG_EVENT(2, "ccw_device_offline returned %d, " "device 0.%x.%04x\n", ret, cdev->private->dev_id.ssid, cdev->private->dev_id.devno); cdev->online = 1; } return ret;}/** * ccw_device_set_online() - enable a ccw device for I/O * @cdev: target ccw device * * This function first enables @cdev and then calls the driver's set_online() * function for @cdev, if given. If set_online() returns an error, @cdev is * disabled again. * Returns: * %0 on success and a negative error value on failure. * Context: * enabled, ccw device lock not held */int ccw_device_set_online(struct ccw_device *cdev){ int ret; if (!cdev) return -ENODEV; if (cdev->online || !cdev->drv) return -EINVAL; spin_lock_irq(cdev->ccwlock); ret = ccw_device_online(cdev); spin_unlock_irq(cdev->ccwlock); if (ret == 0) wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); else { CIO_MSG_EVENT(2, "ccw_device_online returned %d, " "device 0.%x.%04x\n", ret, cdev->private->dev_id.ssid, cdev->private->dev_id.devno); return ret; } if (cdev->private->state != DEV_STATE_ONLINE) return -ENODEV; if (!cdev->drv->set_online || cdev->drv->set_online(cdev) == 0) { cdev->online = 1; return 0; } spin_lock_irq(cdev->ccwlock); ret = ccw_device_offline(cdev); spin_unlock_irq(cdev->ccwlock); if (ret == 0) wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); else CIO_MSG_EVENT(2, "ccw_device_offline returned %d, " "device 0.%x.%04x\n", ret, cdev->private->dev_id.ssid, cdev->private->dev_id.devno); return (ret == 0) ? -ENODEV : ret;}static void online_store_handle_offline(struct ccw_device *cdev){ if (cdev->private->state == DEV_STATE_DISCONNECTED) ccw_device_remove_disconnected(cdev); else if (cdev->drv && cdev->drv->set_offline) ccw_device_set_offline(cdev);}static int online_store_recog_and_online(struct ccw_device *cdev){ int ret; /* Do device recognition, if needed. */ if (cdev->id.cu_type == 0) { ret = ccw_device_recognition(cdev); if (ret) { CIO_MSG_EVENT(0, "Couldn't start recognition " "for device 0.%x.%04x (ret=%d)\n", cdev->private->dev_id.ssid, cdev->private->dev_id.devno, ret); return ret; } wait_event(cdev->private->wait_q, cdev->private->flags.recog_done); } if (cdev->drv && cdev->drv->set_online) ccw_device_set_online(cdev); return 0;}static void online_store_handle_online(struct ccw_device *cdev, int force){ int ret; ret = online_store_recog_and_online(cdev); if (ret) return; if (force && cdev->private->state == DEV_STATE_BOXED) { ret = ccw_device_stlck(cdev); if (ret) { dev_warn(&cdev->dev, "ccw_device_stlck returned %d!\n", ret); return; } if (cdev->id.cu_type == 0) cdev->private->state = DEV_STATE_NOT_OPER; online_store_recog_and_online(cdev); }
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -