⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 device.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
		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 + -