📄 device.c
字号:
PREPARE_WORK(&cdev->private->kick_work, ccw_device_call_sch_unregister); queue_work(slow_path_wq, &cdev->private->kick_work); if (atomic_dec_and_test(&ccw_device_init_count)) wake_up(&ccw_device_init_wq); break; case DEV_STATE_BOXED: /* Device did not respond in time. */ case DEV_STATE_OFFLINE: /* * We can't register the device in interrupt context so * we schedule a work item. */ if (!get_device(&cdev->dev)) break; PREPARE_WORK(&cdev->private->kick_work, io_subchannel_register); queue_work(slow_path_wq, &cdev->private->kick_work); break; }}static intio_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch){ int rc; struct ccw_device_private *priv; sch->dev.driver_data = cdev; sch->driver = &io_subchannel_driver; cdev->ccwlock = sch->lock; /* Init private data. */ priv = cdev->private; priv->dev_id.devno = sch->schib.pmcw.dev; priv->dev_id.ssid = sch->schid.ssid; priv->schid = sch->schid; priv->state = DEV_STATE_NOT_OPER; INIT_LIST_HEAD(&priv->cmb_list); init_waitqueue_head(&priv->wait_q); init_timer(&priv->timer); /* Set an initial name for the device. */ snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x", sch->schid.ssid, sch->schib.pmcw.dev); /* Increase counter of devices currently in recognition. */ atomic_inc(&ccw_device_init_count); /* Start async. device sensing. */ spin_lock_irq(sch->lock); rc = ccw_device_recognition(cdev); spin_unlock_irq(sch->lock); if (rc) { if (atomic_dec_and_test(&ccw_device_init_count)) wake_up(&ccw_device_init_wq); } return rc;}static void ccw_device_move_to_sch(struct work_struct *work){ struct ccw_device_private *priv; int rc; struct subchannel *sch; struct ccw_device *cdev; struct subchannel *former_parent; priv = container_of(work, struct ccw_device_private, kick_work); sch = priv->sch; cdev = priv->cdev; former_parent = ccw_device_is_orphan(cdev) ? NULL : to_subchannel(get_device(cdev->dev.parent)); mutex_lock(&sch->reg_mutex); /* Try to move the ccw device to its new subchannel. */ rc = device_move(&cdev->dev, &sch->dev); mutex_unlock(&sch->reg_mutex); if (rc) { CIO_MSG_EVENT(2, "Moving device 0.%x.%04x to subchannel " "0.%x.%04x failed (ret=%d)!\n", cdev->private->dev_id.ssid, cdev->private->dev_id.devno, sch->schid.ssid, sch->schid.sch_no, rc); css_sch_device_unregister(sch); goto out; } if (former_parent) { spin_lock_irq(former_parent->lock); former_parent->dev.driver_data = NULL; spin_unlock_irq(former_parent->lock); css_sch_device_unregister(former_parent); /* Reset intparm to zeroes. */ former_parent->schib.pmcw.intparm = 0; cio_modify(former_parent); } sch_attach_device(sch, cdev);out: if (former_parent) put_device(&former_parent->dev); put_device(&cdev->dev);}static intio_subchannel_probe (struct subchannel *sch){ struct ccw_device *cdev; int rc; unsigned long flags; struct ccw_dev_id dev_id; if (sch->dev.driver_data) { /* * This subchannel already has an associated ccw_device. * Register it and exit. This happens for all early * device, e.g. the console. */ cdev = sch->dev.driver_data; cdev->dev.groups = ccwdev_attr_groups; device_initialize(&cdev->dev); ccw_device_register(cdev); /* * Check if the device is already online. If it is * the reference count needs to be corrected * (see ccw_device_online and css_init_done for the * ugly details). */ if (cdev->private->state != DEV_STATE_NOT_OPER && cdev->private->state != DEV_STATE_OFFLINE && cdev->private->state != DEV_STATE_BOXED) get_device(&cdev->dev); return 0; } /* * First check if a fitting device may be found amongst the * disconnected devices or in the orphanage. */ dev_id.devno = sch->schib.pmcw.dev; dev_id.ssid = sch->schid.ssid; cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL); if (!cdev) cdev = get_orphaned_ccwdev_by_dev_id(to_css(sch->dev.parent), &dev_id); if (cdev) { /* * Schedule moving the device until when we have a registered * subchannel to move to and succeed the probe. We can * unregister later again, when the probe is through. */ cdev->private->sch = sch; PREPARE_WORK(&cdev->private->kick_work, ccw_device_move_to_sch); queue_work(slow_path_wq, &cdev->private->kick_work); return 0; } cdev = io_subchannel_create_ccwdev(sch); if (IS_ERR(cdev)) return PTR_ERR(cdev); rc = io_subchannel_recog(cdev, sch); if (rc) { spin_lock_irqsave(sch->lock, flags); sch->dev.driver_data = NULL; spin_unlock_irqrestore(sch->lock, flags); if (cdev->dev.release) cdev->dev.release(&cdev->dev); } return rc;}static intio_subchannel_remove (struct subchannel *sch){ struct ccw_device *cdev; unsigned long flags; if (!sch->dev.driver_data) return 0; cdev = sch->dev.driver_data; /* Set ccw device to not operational and drop reference. */ spin_lock_irqsave(cdev->ccwlock, flags); sch->dev.driver_data = NULL; cdev->private->state = DEV_STATE_NOT_OPER; spin_unlock_irqrestore(cdev->ccwlock, flags); ccw_device_unregister(cdev); put_device(&cdev->dev); return 0;}static intio_subchannel_notify(struct device *dev, int event){ struct ccw_device *cdev; cdev = dev->driver_data; if (!cdev) return 0; if (!cdev->drv) return 0; if (!cdev->online) return 0; return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0;}static voidio_subchannel_verify(struct device *dev){ struct ccw_device *cdev; cdev = dev->driver_data; if (cdev) dev_fsm_event(cdev, DEV_EVENT_VERIFY);}static voidio_subchannel_ioterm(struct device *dev){ struct ccw_device *cdev; cdev = dev->driver_data; if (!cdev) return; /* Internal I/O will be retried by the interrupt handler. */ if (cdev->private->flags.intretry) return; cdev->private->state = DEV_STATE_CLEAR_VERIFY; if (cdev->handler) cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO));}static voidio_subchannel_shutdown(struct subchannel *sch){ struct ccw_device *cdev; int ret; cdev = sch->dev.driver_data; if (cio_is_console(sch->schid)) return; if (!sch->schib.pmcw.ena) /* Nothing to do. */ return; ret = cio_disable_subchannel(sch); if (ret != -EBUSY) /* Subchannel is disabled, we're done. */ return; cdev->private->state = DEV_STATE_QUIESCE; if (cdev->handler) cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO)); ret = ccw_device_cancel_halt_clear(cdev); if (ret == -EBUSY) { ccw_device_set_timeout(cdev, HZ/10); wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); } cio_disable_subchannel(sch);}#ifdef CONFIG_CCW_CONSOLEstatic struct ccw_device console_cdev;static struct ccw_device_private console_private;static int console_cdev_in_use;static DEFINE_SPINLOCK(ccw_console_lock);spinlock_t * cio_get_console_lock(void){ return &ccw_console_lock;}static intccw_device_console_enable (struct ccw_device *cdev, struct subchannel *sch){ int rc; /* Initialize the ccw_device structure. */ cdev->dev.parent= &sch->dev; rc = io_subchannel_recog(cdev, sch); if (rc) return rc; /* Now wait for the async. recognition to come to an end. */ spin_lock_irq(cdev->ccwlock); while (!dev_fsm_final_state(cdev)) wait_cons_dev(); rc = -EIO; if (cdev->private->state != DEV_STATE_OFFLINE) goto out_unlock; ccw_device_online(cdev); while (!dev_fsm_final_state(cdev)) wait_cons_dev(); if (cdev->private->state != DEV_STATE_ONLINE) goto out_unlock; rc = 0;out_unlock: spin_unlock_irq(cdev->ccwlock); return 0;}struct ccw_device *ccw_device_probe_console(void){ struct subchannel *sch; int ret; if (xchg(&console_cdev_in_use, 1) != 0) return ERR_PTR(-EBUSY); sch = cio_probe_console(); if (IS_ERR(sch)) { console_cdev_in_use = 0; return (void *) sch; } memset(&console_cdev, 0, sizeof(struct ccw_device)); memset(&console_private, 0, sizeof(struct ccw_device_private)); console_cdev.private = &console_private; console_private.cdev = &console_cdev; ret = ccw_device_console_enable(&console_cdev, sch); if (ret) { cio_release_console(); console_cdev_in_use = 0; return ERR_PTR(ret); } console_cdev.online = 1; return &console_cdev;}#endif/* * get ccw_device matching the busid, but only if owned by cdrv */static int__ccwdev_check_busid(struct device *dev, void *id){ char *bus_id; bus_id = id; return (strncmp(bus_id, dev->bus_id, BUS_ID_SIZE) == 0);}/** * get_ccwdev_by_busid() - obtain device from a bus id * @cdrv: driver the device is owned by * @bus_id: bus id of the device to be searched * * This function searches all devices owned by @cdrv for a device with a bus * id matching @bus_id. * Returns: * If a match is found, its reference count of the found device is increased * and it is returned; else %NULL is returned. */struct ccw_device *get_ccwdev_by_busid(struct ccw_driver *cdrv, const char *bus_id){ struct device *dev; struct device_driver *drv; drv = get_driver(&cdrv->driver); if (!drv) return NULL; dev = driver_find_device(drv, NULL, (void *)bus_id, __ccwdev_check_busid); put_driver(drv); return dev ? to_ccwdev(dev) : NULL;}/************************** device driver handling ************************//* This is the implementation of the ccw_driver class. The probe, remove * and release methods are initially very similar to the device_driver * implementations, with the difference that they have ccw_device * arguments. * * A ccw driver also contains the information that is needed for * device matching. */static intccw_device_probe (struct device *dev){ struct ccw_device *cdev = to_ccwdev(dev); struct ccw_driver *cdrv = to_ccwdrv(dev->driver); int ret; cdev->drv = cdrv; /* to let the driver call _set_online */ ret = cdrv->probe ? cdrv->probe(cdev) : -ENODEV; if (ret) { cdev->drv = NULL; return ret; } return 0;}static intccw_device_remove (struct device *dev){ struct ccw_device *cdev = to_ccwdev(dev); struct ccw_driver *cdrv = cdev->drv; int ret; if (cdrv->remove) cdrv->remove(cdev); if (cdev->online) { cdev->online = 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 //FIXME: we can't fail! 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); } ccw_device_set_timeout(cdev, 0); cdev->drv = NULL; return 0;}static void ccw_device_shutdown(struct device *dev){ struct ccw_device *cdev; cdev = to_ccwdev(dev); if (cdev->drv && cdev->drv->shutdown) cdev->drv->shutdown(cdev); disable_cmf(cdev);}struct bus_type ccw_bus_type = { .name = "ccw", .match = ccw_bus_match, .uevent = ccw_uevent, .probe = ccw_device_probe, .remove = ccw_device_remove, .shutdown = ccw_device_shutdown,};/** * ccw_driver_register() - register a ccw driver * @cdriver: driver to be registered * * This function is mainly a wrapper around driver_register(). * Returns: * %0 on success and a negative error value on failure. */int ccw_driver_register(struct ccw_driver *cdriver){ struct device_driver *drv = &cdriver->driver; drv->bus = &ccw_bus_type; drv->name = cdriver->name; return driver_register(drv);}/** * ccw_driver_unregister() - deregister a ccw driver * @cdriver: driver to be deregistered * * This function is mainly a wrapper around driver_unregister(). */void ccw_driver_unregister(struct ccw_driver *cdriver){ driver_unregister(&cdriver->driver);}/* Helper func for qdio. */struct subchannel_idccw_device_get_subchannel_id(struct ccw_device *cdev){ struct subchannel *sch; sch = to_subchannel(cdev->dev.parent); return sch->schid;}MODULE_LICENSE("GPL");EXPORT_SYMBOL(ccw_device_set_online);EXPORT_SYMBOL(ccw_device_set_offline);EXPORT_SYMBOL(ccw_driver_register);EXPORT_SYMBOL(ccw_driver_unregister);EXPORT_SYMBOL(get_ccwdev_by_busid);EXPORT_SYMBOL(ccw_bus_type);EXPORT_SYMBOL(ccw_device_work);EXPORT_SYMBOL(ccw_device_notify_work);EXPORT_SYMBOL_GPL(ccw_device_get_subchannel_id);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -