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

📄 device.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
}static ssize_t online_store (struct device *dev, struct device_attribute *attr,			     const char *buf, size_t count){	struct ccw_device *cdev = to_ccwdev(dev);	int i, force;	char *tmp;	if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0)		return -EAGAIN;	if (cdev->drv && !try_module_get(cdev->drv->owner)) {		atomic_set(&cdev->private->onoff, 0);		return -EINVAL;	}	if (!strncmp(buf, "force\n", count)) {		force = 1;		i = 1;	} else {		force = 0;		i = simple_strtoul(buf, &tmp, 16);	}	switch (i) {	case 0:		online_store_handle_offline(cdev);		break;	case 1:		online_store_handle_online(cdev, force);		break;	default:		count = -EINVAL;	}	if (cdev->drv)		module_put(cdev->drv->owner);	atomic_set(&cdev->private->onoff, 0);	return count;}static ssize_tavailable_show (struct device *dev, struct device_attribute *attr, char *buf){	struct ccw_device *cdev = to_ccwdev(dev);	struct subchannel *sch;	if (ccw_device_is_orphan(cdev))		return sprintf(buf, "no device\n");	switch (cdev->private->state) {	case DEV_STATE_BOXED:		return sprintf(buf, "boxed\n");	case DEV_STATE_DISCONNECTED:	case DEV_STATE_DISCONNECTED_SENSE_ID:	case DEV_STATE_NOT_OPER:		sch = to_subchannel(dev->parent);		if (!sch->lpm)			return sprintf(buf, "no path\n");		else			return sprintf(buf, "no device\n");	default:		/* All other states considered fine. */		return sprintf(buf, "good\n");	}}static DEVICE_ATTR(chpids, 0444, chpids_show, NULL);static DEVICE_ATTR(pimpampom, 0444, pimpampom_show, NULL);static DEVICE_ATTR(devtype, 0444, devtype_show, NULL);static DEVICE_ATTR(cutype, 0444, cutype_show, NULL);static DEVICE_ATTR(modalias, 0444, modalias_show, NULL);static DEVICE_ATTR(online, 0644, online_show, online_store);extern struct device_attribute dev_attr_cmb_enable;static DEVICE_ATTR(availability, 0444, available_show, NULL);static struct attribute * subch_attrs[] = {	&dev_attr_chpids.attr,	&dev_attr_pimpampom.attr,	NULL,};static struct attribute_group subch_attr_group = {	.attrs = subch_attrs,};struct attribute_group *subch_attr_groups[] = {	&subch_attr_group,	NULL,};static struct attribute * ccwdev_attrs[] = {	&dev_attr_devtype.attr,	&dev_attr_cutype.attr,	&dev_attr_modalias.attr,	&dev_attr_online.attr,	&dev_attr_cmb_enable.attr,	&dev_attr_availability.attr,	NULL,};static struct attribute_group ccwdev_attr_group = {	.attrs = ccwdev_attrs,};static struct attribute_group *ccwdev_attr_groups[] = {	&ccwdev_attr_group,	NULL,};/* this is a simple abstraction for device_register that sets the * correct bus type and adds the bus specific files */static int ccw_device_register(struct ccw_device *cdev){	struct device *dev = &cdev->dev;	int ret;	dev->bus = &ccw_bus_type;	if ((ret = device_add(dev)))		return ret;	set_bit(1, &cdev->private->registered);	return ret;}struct match_data {	struct ccw_dev_id dev_id;	struct ccw_device * sibling;};static intmatch_devno(struct device * dev, void * data){	struct match_data * d = data;	struct ccw_device * cdev;	cdev = to_ccwdev(dev);	if ((cdev->private->state == DEV_STATE_DISCONNECTED) &&	    !ccw_device_is_orphan(cdev) &&	    ccw_dev_id_is_equal(&cdev->private->dev_id, &d->dev_id) &&	    (cdev != d->sibling))		return 1;	return 0;}static struct ccw_device * get_disc_ccwdev_by_dev_id(struct ccw_dev_id *dev_id,						     struct ccw_device *sibling){	struct device *dev;	struct match_data data;	data.dev_id = *dev_id;	data.sibling = sibling;	dev = bus_find_device(&ccw_bus_type, NULL, &data, match_devno);	return dev ? to_ccwdev(dev) : NULL;}static int match_orphan(struct device *dev, void *data){	struct ccw_dev_id *dev_id;	struct ccw_device *cdev;	dev_id = data;	cdev = to_ccwdev(dev);	return ccw_dev_id_is_equal(&cdev->private->dev_id, dev_id);}static struct ccw_device *get_orphaned_ccwdev_by_dev_id(struct channel_subsystem *css,			      struct ccw_dev_id *dev_id){	struct device *dev;	dev = device_find_child(&css->pseudo_subchannel->dev, dev_id,				match_orphan);	return dev ? to_ccwdev(dev) : NULL;}static voidccw_device_add_changed(struct work_struct *work){	struct ccw_device_private *priv;	struct ccw_device *cdev;	priv = container_of(work, struct ccw_device_private, kick_work);	cdev = priv->cdev;	if (device_add(&cdev->dev)) {		put_device(&cdev->dev);		return;	}	set_bit(1, &cdev->private->registered);}void ccw_device_do_unreg_rereg(struct work_struct *work){	struct ccw_device_private *priv;	struct ccw_device *cdev;	struct subchannel *sch;	priv = container_of(work, struct ccw_device_private, kick_work);	cdev = priv->cdev;	sch = to_subchannel(cdev->dev.parent);	ccw_device_unregister(cdev);	PREPARE_WORK(&cdev->private->kick_work,		     ccw_device_add_changed);	queue_work(ccw_device_work, &cdev->private->kick_work);}static voidccw_device_release(struct device *dev){	struct ccw_device *cdev;	cdev = to_ccwdev(dev);	kfree(cdev->private);	kfree(cdev);}static struct ccw_device * io_subchannel_allocate_dev(struct subchannel *sch){	struct ccw_device *cdev;	cdev  = kzalloc(sizeof(*cdev), GFP_KERNEL);	if (cdev) {		cdev->private = kzalloc(sizeof(struct ccw_device_private),					GFP_KERNEL | GFP_DMA);		if (cdev->private)			return cdev;	}	kfree(cdev);	return ERR_PTR(-ENOMEM);}static int io_subchannel_initialize_dev(struct subchannel *sch,					struct ccw_device *cdev){	cdev->private->cdev = cdev;	atomic_set(&cdev->private->onoff, 0);	cdev->dev.parent = &sch->dev;	cdev->dev.release = ccw_device_release;	INIT_WORK(&cdev->private->kick_work, NULL);	cdev->dev.groups = ccwdev_attr_groups;	/* Do first half of device_register. */	device_initialize(&cdev->dev);	if (!get_device(&sch->dev)) {		if (cdev->dev.release)			cdev->dev.release(&cdev->dev);		return -ENODEV;	}	return 0;}static struct ccw_device * io_subchannel_create_ccwdev(struct subchannel *sch){	struct ccw_device *cdev;	int ret;	cdev = io_subchannel_allocate_dev(sch);	if (!IS_ERR(cdev)) {		ret = io_subchannel_initialize_dev(sch, cdev);		if (ret) {			kfree(cdev);			cdev = ERR_PTR(ret);		}	}	return cdev;}static int io_subchannel_recog(struct ccw_device *, struct subchannel *);static void sch_attach_device(struct subchannel *sch,			      struct ccw_device *cdev){	css_update_ssd_info(sch);	spin_lock_irq(sch->lock);	sch->dev.driver_data = cdev;	cdev->private->schid = sch->schid;	cdev->ccwlock = sch->lock;	device_trigger_reprobe(sch);	spin_unlock_irq(sch->lock);}static void sch_attach_disconnected_device(struct subchannel *sch,					   struct ccw_device *cdev){	struct subchannel *other_sch;	int ret;	other_sch = to_subchannel(get_device(cdev->dev.parent));	ret = device_move(&cdev->dev, &sch->dev);	if (ret) {		CIO_MSG_EVENT(2, "Moving disconnected device 0.%x.%04x failed "			      "(ret=%d)!\n", cdev->private->dev_id.ssid,			      cdev->private->dev_id.devno, ret);		put_device(&other_sch->dev);		return;	}	other_sch->dev.driver_data = NULL;	/* No need to keep a subchannel without ccw device around. */	css_sch_device_unregister(other_sch);	put_device(&other_sch->dev);	sch_attach_device(sch, cdev);}static void sch_attach_orphaned_device(struct subchannel *sch,				       struct ccw_device *cdev){	int ret;	/* Try to move the ccw device to its new subchannel. */	ret = device_move(&cdev->dev, &sch->dev);	if (ret) {		CIO_MSG_EVENT(0, "Moving device 0.%x.%04x from orphanage "			      "failed (ret=%d)!\n",			      cdev->private->dev_id.ssid,			      cdev->private->dev_id.devno, ret);		return;	}	sch_attach_device(sch, cdev);}static void sch_create_and_recog_new_device(struct subchannel *sch){	struct ccw_device *cdev;	/* Need to allocate a new ccw device. */	cdev = io_subchannel_create_ccwdev(sch);	if (IS_ERR(cdev)) {		/* OK, we did everything we could... */		css_sch_device_unregister(sch);		return;	}	spin_lock_irq(sch->lock);	sch->dev.driver_data = cdev;	spin_unlock_irq(sch->lock);	/* Start recognition for the new ccw device. */	if (io_subchannel_recog(cdev, sch)) {		spin_lock_irq(sch->lock);		sch->dev.driver_data = NULL;		spin_unlock_irq(sch->lock);		if (cdev->dev.release)			cdev->dev.release(&cdev->dev);		css_sch_device_unregister(sch);	}}void ccw_device_move_to_orphanage(struct work_struct *work){	struct ccw_device_private *priv;	struct ccw_device *cdev;	struct ccw_device *replacing_cdev;	struct subchannel *sch;	int ret;	struct channel_subsystem *css;	struct ccw_dev_id dev_id;	priv = container_of(work, struct ccw_device_private, kick_work);	cdev = priv->cdev;	sch = to_subchannel(cdev->dev.parent);	css = to_css(sch->dev.parent);	dev_id.devno = sch->schib.pmcw.dev;	dev_id.ssid = sch->schid.ssid;	/*	 * Move the orphaned ccw device to the orphanage so the replacing	 * ccw device can take its place on the subchannel.	 */	ret = device_move(&cdev->dev, &css->pseudo_subchannel->dev);	if (ret) {		CIO_MSG_EVENT(0, "Moving device 0.%x.%04x to orphanage failed "			      "(ret=%d)!\n", cdev->private->dev_id.ssid,			      cdev->private->dev_id.devno, ret);		return;	}	cdev->ccwlock = css->pseudo_subchannel->lock;	/*	 * Search for the replacing ccw device	 * - among the disconnected devices	 * - in the orphanage	 */	replacing_cdev = get_disc_ccwdev_by_dev_id(&dev_id, cdev);	if (replacing_cdev) {		sch_attach_disconnected_device(sch, replacing_cdev);		return;	}	replacing_cdev = get_orphaned_ccwdev_by_dev_id(css, &dev_id);	if (replacing_cdev) {		sch_attach_orphaned_device(sch, replacing_cdev);		return;	}	sch_create_and_recog_new_device(sch);}/* * Register recognized device. */static voidio_subchannel_register(struct work_struct *work){	struct ccw_device_private *priv;	struct ccw_device *cdev;	struct subchannel *sch;	int ret;	unsigned long flags;	priv = container_of(work, struct ccw_device_private, kick_work);	cdev = priv->cdev;	sch = to_subchannel(cdev->dev.parent);	css_update_ssd_info(sch);	/*	 * io_subchannel_register() will also be called after device	 * recognition has been done for a boxed device (which will already	 * be registered). We need to reprobe since we may now have sense id	 * information.	 */	if (klist_node_attached(&cdev->dev.knode_parent)) {		if (!cdev->drv) {			ret = device_reprobe(&cdev->dev);			if (ret)				/* We can't do much here. */				CIO_MSG_EVENT(2, "device_reprobe() returned"					      " %d for 0.%x.%04x\n", ret,					      cdev->private->dev_id.ssid,					      cdev->private->dev_id.devno);		}		goto out;	}	/*	 * Now we know this subchannel will stay, we can throw	 * our delayed uevent.	 */	sch->dev.uevent_suppress = 0;	kobject_uevent(&sch->dev.kobj, KOBJ_ADD);	/* make it known to the system */	ret = ccw_device_register(cdev);	if (ret) {		CIO_MSG_EVENT(0, "Could not register ccw dev 0.%x.%04x: %d\n",			      cdev->private->dev_id.ssid,			      cdev->private->dev_id.devno, ret);		put_device(&cdev->dev);		spin_lock_irqsave(sch->lock, flags);		sch->dev.driver_data = NULL;		spin_unlock_irqrestore(sch->lock, flags);		kfree (cdev->private);		kfree (cdev);		put_device(&sch->dev);		if (atomic_dec_and_test(&ccw_device_init_count))			wake_up(&ccw_device_init_wq);		return;	}	put_device(&cdev->dev);out:	cdev->private->flags.recog_done = 1;	put_device(&sch->dev);	wake_up(&cdev->private->wait_q);	if (atomic_dec_and_test(&ccw_device_init_count))		wake_up(&ccw_device_init_wq);}static void ccw_device_call_sch_unregister(struct work_struct *work){	struct ccw_device_private *priv;	struct ccw_device *cdev;	struct subchannel *sch;	priv = container_of(work, struct ccw_device_private, kick_work);	cdev = priv->cdev;	sch = to_subchannel(cdev->dev.parent);	css_sch_device_unregister(sch);	/* Reset intparm to zeroes. */	sch->schib.pmcw.intparm = 0;	cio_modify(sch);	put_device(&cdev->dev);	put_device(&sch->dev);}/* * subchannel recognition done. Called from the state machine. */voidio_subchannel_recog_done(struct ccw_device *cdev){	struct subchannel *sch;	if (css_init_done == 0) {		cdev->private->flags.recog_done = 1;		return;	}	switch (cdev->private->state) {	case DEV_STATE_NOT_OPER:		cdev->private->flags.recog_done = 1;		/* Remove device found not operational. */		if (!get_device(&cdev->dev))			break;		sch = to_subchannel(cdev->dev.parent);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -