📄 device.c
字号:
}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 + -