/* * Function: cio_cancel * Issues a "Cancel Subchannel" on the specified subchannel * Note: We don't need any fancy intparms and flags here * since xsch is executed synchronously. * Only for common I/O internal use as for now. */ int cio_cancel (struct subchannel *sch) { int ccode; if (!sch) return -ENODEV; CIO_TRACE_EVENT(2, "cancelIO"); CIO_TRACE_EVENT(2, dev_name(&sch->dev)); ccode = xsch (sch->schid); CIO_HEX_EVENT(2, &ccode, sizeof(ccode)); switch (ccode) { case 0: /* success */ /* Update information in scsw. */ if (cio_update_schib(sch)) return -ENODEV; return 0; case 1: /* status pending */ return -EBUSY; case 2: /* not applicable */ return -EINVAL; default: /* not oper */ return -ENODEV; } }
/* * resume suspended I/O operation */ int cio_resume (struct subchannel *sch) { int ccode; CIO_TRACE_EVENT(4, "resIO"); CIO_TRACE_EVENT(4, dev_name(&sch->dev)); ccode = rsch (sch->schid); CIO_HEX_EVENT(4, &ccode, sizeof(ccode)); switch (ccode) { case 0: sch->schib.scsw.cmd.actl |= SCSW_ACTL_RESUME_PEND; return 0; case 1: return -EBUSY; case 2: return -EINVAL; default: /* * useless to wait for request completion * as device is no longer operational ! */ return -ENODEV; } }
/** * cio_disable_subchannel - disable a subchannel. * @sch: subchannel to disable */ int cio_disable_subchannel(struct subchannel *sch) { int retry; int ret; CIO_TRACE_EVENT(2, "dissch"); CIO_TRACE_EVENT(2, dev_name(&sch->dev)); if (sch_is_pseudo_sch(sch)) return 0; if (cio_update_schib(sch)) return -ENODEV; sch->config.ena = 0; for (retry = 0; retry < 3; retry++) { ret = cio_commit_config(sch); if (ret == -EBUSY) { struct irb irb; if (tsch(sch->schid, &irb) != 0) break; } else break; } CIO_HEX_EVENT(2, &ret, sizeof(ret)); return ret; }
/* * Clear I/O operation */ int cio_clear(struct subchannel *sch) { int ccode; if (!sch) return -ENODEV; CIO_TRACE_EVENT(2, "clearIO"); CIO_TRACE_EVENT(2, dev_name(&sch->dev)); /* * Issue "Clear subchannel" and process condition code */ ccode = csch (sch->schid); CIO_HEX_EVENT(2, &ccode, sizeof(ccode)); switch (ccode) { case 0: sch->schib.scsw.cmd.actl |= SCSW_ACTL_CLEAR_PEND; return 0; default: /* device not operational */ return -ENODEV; } }
int cio_cancel (struct subchannel *sch) { int ccode; if (!sch) return -ENODEV; CIO_TRACE_EVENT(2, "cancelIO"); CIO_TRACE_EVENT(2, dev_name(&sch->dev)); ccode = xsch (sch->schid); CIO_HEX_EVENT(2, &ccode, sizeof(ccode)); switch (ccode) { case 0: if (cio_update_schib(sch)) return -ENODEV; return 0; case 1: return -EBUSY; case 2: return -EINVAL; default: return -ENODEV; } }
int cio_enable_subchannel(struct subchannel *sch, u32 intparm) { int retry; int ret; CIO_TRACE_EVENT(2, "ensch"); CIO_TRACE_EVENT(2, dev_name(&sch->dev)); if (sch_is_pseudo_sch(sch)) return -EINVAL; if (cio_update_schib(sch)) return -ENODEV; sch->config.ena = 1; sch->config.isc = sch->isc; sch->config.intparm = intparm; for (retry = 0; retry < 3; retry++) { ret = cio_commit_config(sch); if (ret == -EIO) { sch->config.csense = 0; } else if (ret == -EBUSY) { struct irb irb; if (tsch(sch->schid, &irb) != 0) break; } else break; } CIO_HEX_EVENT(2, &ret, sizeof(ret)); return ret; }
/** * cio_enable_subchannel - enable a subchannel. * @sch: subchannel to be enabled * @intparm: interruption parameter to set */ int cio_enable_subchannel(struct subchannel *sch, u32 intparm) { int ret; CIO_TRACE_EVENT(2, "ensch"); CIO_TRACE_EVENT(2, dev_name(&sch->dev)); if (sch_is_pseudo_sch(sch)) return -EINVAL; if (cio_update_schib(sch)) return -ENODEV; sch->config.ena = 1; sch->config.isc = sch->isc; sch->config.intparm = intparm; ret = cio_commit_config(sch); if (ret == -EIO) { /* * Got a program check in msch. Try without * the concurrent sense bit the next time. */ sch->config.csense = 0; ret = cio_commit_config(sch); } CIO_HEX_EVENT(2, &ret, sizeof(ret)); return ret; }
int cio_halt(struct subchannel *sch) { int ccode; if (!sch) return -ENODEV; CIO_TRACE_EVENT(2, "haltIO"); CIO_TRACE_EVENT(2, dev_name(&sch->dev)); ccode = hsch (sch->schid); CIO_HEX_EVENT(2, &ccode, sizeof(ccode)); switch (ccode) { case 0: sch->schib.scsw.cmd.actl |= SCSW_ACTL_HALT_PEND; return 0; case 1: case 2: return -EBUSY; default: return -ENODEV; } }
/* * halt I/O operation */ int cio_halt(struct subchannel *sch) { int ccode; if (!sch) return -ENODEV; CIO_TRACE_EVENT(2, "haltIO"); CIO_TRACE_EVENT(2, dev_name(&sch->dev)); /* * Issue "Halt subchannel" and process condition code */ ccode = hsch (sch->schid); CIO_HEX_EVENT(2, &ccode, sizeof(ccode)); switch (ccode) { case 0: sch->schib.scsw.cmd.actl |= SCSW_ACTL_HALT_PEND; return 0; case 1: /* status pending */ case 2: /* busy */ return -EBUSY; default: /* device not operational */ return -ENODEV; } }
int cio_start_key (struct subchannel *sch, /* subchannel structure */ struct ccw1 * cpa, /* logical channel prog addr */ __u8 lpm, /* logical path mask */ __u8 key) /* storage key */ { struct io_subchannel_private *priv = to_io_private(sch); union orb *orb = &priv->orb; int ccode; CIO_TRACE_EVENT(5, "stIO"); CIO_TRACE_EVENT(5, dev_name(&sch->dev)); memset(orb, 0, sizeof(union orb)); /* sch is always under 2G. */ orb->cmd.intparm = (u32)(addr_t)sch; orb->cmd.fmt = 1; orb->cmd.pfch = priv->options.prefetch == 0; orb->cmd.spnd = priv->options.suspend; orb->cmd.ssic = priv->options.suspend && priv->options.inter; orb->cmd.lpm = (lpm != 0) ? lpm : sch->lpm; #ifdef CONFIG_64BIT /* * for 64 bit we always support 64 bit IDAWs with 4k page size only */ orb->cmd.c64 = 1; orb->cmd.i2k = 0; #endif orb->cmd.key = key >> 4; /* issue "Start Subchannel" */ orb->cmd.cpa = (__u32) __pa(cpa); ccode = ssch(sch->schid, orb); /* process condition code */ CIO_HEX_EVENT(5, &ccode, sizeof(ccode)); switch (ccode) { case 0: /* * initialize device status information */ sch->schib.scsw.cmd.actl |= SCSW_ACTL_START_PEND; return 0; case 1: /* status pending */ case 2: /* busy */ return -EBUSY; case 3: /* device/path not operational */ return cio_start_handle_notoper(sch, lpm); default: return ccode; } }
int cio_start_key (struct subchannel *sch, struct ccw1 * cpa, __u8 lpm, __u8 key) { struct io_subchannel_private *priv = to_io_private(sch); union orb *orb = &priv->orb; int ccode; CIO_TRACE_EVENT(5, "stIO"); CIO_TRACE_EVENT(5, dev_name(&sch->dev)); memset(orb, 0, sizeof(union orb)); orb->cmd.intparm = (u32)(addr_t)sch; orb->cmd.fmt = 1; orb->cmd.pfch = priv->options.prefetch == 0; orb->cmd.spnd = priv->options.suspend; orb->cmd.ssic = priv->options.suspend && priv->options.inter; orb->cmd.lpm = (lpm != 0) ? lpm : sch->lpm; #ifdef CONFIG_64BIT orb->cmd.c64 = 1; orb->cmd.i2k = 0; #endif orb->cmd.key = key >> 4; orb->cmd.cpa = (__u32) __pa(cpa); ccode = ssch(sch->schid, orb); CIO_HEX_EVENT(5, &ccode, sizeof(ccode)); switch (ccode) { case 0: sch->schib.scsw.cmd.actl |= SCSW_ACTL_START_PEND; return 0; case 1: case 2: return -EBUSY; case 3: return cio_start_handle_notoper(sch, lpm); default: return ccode; } }
/** * cio_disable_subchannel - disable a subchannel. * @sch: subchannel to disable */ int cio_disable_subchannel(struct subchannel *sch) { int ret; CIO_TRACE_EVENT(2, "dissch"); CIO_TRACE_EVENT(2, dev_name(&sch->dev)); if (sch_is_pseudo_sch(sch)) return 0; if (cio_update_schib(sch)) return -ENODEV; sch->config.ena = 0; ret = cio_commit_config(sch); CIO_HEX_EVENT(2, &ret, sizeof(ret)); return ret; }
static enum io_status ccwreq_status(struct ccw_device *cdev, struct irb *lcirb) { struct irb *irb = &cdev->private->irb; struct cmd_scsw *scsw = &irb->scsw.cmd; enum uc_todo todo; if (ccw_device_accumulate_and_sense(cdev, lcirb)) return IO_RUNNING; if (scsw->fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) return IO_KILLED; if (scsw->cc == 3 || scsw->pno) return IO_PATH_ERROR; if (irb->esw.esw0.erw.cons) { CIO_TRACE_EVENT(2, "sensedata"); CIO_HEX_EVENT(2, &cdev->private->dev_id, sizeof(struct ccw_dev_id)); CIO_HEX_EVENT(2, &cdev->private->irb.ecw, SENSE_MAX_COUNT); if (irb->ecw[0] & SNS0_CMD_REJECT) return IO_REJECTED; if (cdev->drv && cdev->drv->uc_handler) { todo = cdev->drv->uc_handler(cdev, lcirb); CIO_TRACE_EVENT(2, "uc_response"); CIO_HEX_EVENT(2, &todo, sizeof(todo)); switch (todo) { case UC_TODO_RETRY: return IO_STATUS_ERROR; case UC_TODO_RETRY_ON_NEW_PATH: return IO_PATH_ERROR; case UC_TODO_STOP: return IO_REJECTED; default: return IO_STATUS_ERROR; } } return IO_STATUS_ERROR; }
/* * Return the status of the internal I/O started on the specified ccw device. * Perform BASIC SENSE if required. */ static enum io_status ccwreq_status(struct ccw_device *cdev, struct irb *lcirb) { struct irb *irb = &cdev->private->irb; struct cmd_scsw *scsw = &irb->scsw.cmd; enum uc_todo todo; /* Perform BASIC SENSE if needed. */ if (ccw_device_accumulate_and_sense(cdev, lcirb)) return IO_RUNNING; /* Check for halt/clear interrupt. */ if (scsw->fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) return IO_KILLED; /* Check for path error. */ if (scsw->cc == 3 || scsw->pno) return IO_PATH_ERROR; /* Handle BASIC SENSE data. */ if (irb->esw.esw0.erw.cons) { CIO_TRACE_EVENT(2, "sensedata"); CIO_HEX_EVENT(2, &cdev->private->dev_id, sizeof(struct ccw_dev_id)); CIO_HEX_EVENT(2, &cdev->private->irb.ecw, SENSE_MAX_COUNT); /* Check for command reject. */ if (irb->ecw[0] & SNS0_CMD_REJECT) return IO_REJECTED; /* Ask the driver what to do */ if (cdev->drv && cdev->drv->uc_handler) { todo = cdev->drv->uc_handler(cdev, lcirb); CIO_TRACE_EVENT(2, "uc_response"); CIO_HEX_EVENT(2, &todo, sizeof(todo)); switch (todo) { case UC_TODO_RETRY: return IO_STATUS_ERROR; case UC_TODO_RETRY_ON_NEW_PATH: return IO_PATH_ERROR; case UC_TODO_STOP: return IO_REJECTED; default: return IO_STATUS_ERROR; } } /* Assume that unexpected SENSE data implies an error. */ return IO_STATUS_ERROR; }
static inline int cio_start_handle_notoper(struct subchannel *sch, __u8 lpm) { char dbf_text[15]; if (lpm != 0) sch->lpm &= ~lpm; else sch->lpm = 0; stsch (sch->irq, &sch->schib); CIO_MSG_EVENT(0, "cio_start: 'not oper' status for " "subchannel %04x!\n", sch->irq); sprintf(dbf_text, "no%s", sch->dev.bus_id); CIO_TRACE_EVENT(0, dbf_text); CIO_HEX_EVENT(0, &sch->schib, sizeof (struct schib)); return (sch->lpm ? -EACCES : -ENODEV); }
static int cio_start_handle_notoper(struct subchannel *sch, __u8 lpm) { char dbf_text[15]; if (lpm != 0) sch->lpm &= ~lpm; else sch->lpm = 0; stsch (sch->schid, &sch->schib); CIO_MSG_EVENT(2, "cio_start: 'not oper' status for " "subchannel 0.%x.%04x!\n", sch->schid.ssid, sch->schid.sch_no); sprintf(dbf_text, "no%s", dev_name(&sch->dev)); CIO_TRACE_EVENT(0, dbf_text); CIO_HEX_EVENT(0, &sch->schib, sizeof (struct schib)); return (sch->lpm ? -EACCES : -ENODEV); }
/* * Check for any kind of channel or interface control check but don't * issue the message for the console device */ static inline void ccw_device_msg_control_check(struct ccw_device *cdev, struct irb *irb) { if (!(irb->scsw.cstat & (SCHN_STAT_CHN_DATA_CHK | SCHN_STAT_CHN_CTRL_CHK | SCHN_STAT_INTF_CTRL_CHK))) return; CIO_MSG_EVENT(0, "Channel-Check or Interface-Control-Check " "received" " ... device %04x on subchannel 0.%x.%04x, dev_stat " ": %02X sch_stat : %02X\n", cdev->private->dev_id.devno, cdev->private->schid.ssid, cdev->private->schid.sch_no, irb->scsw.dstat, irb->scsw.cstat); if (irb->scsw.cc != 3) { char dbf_text[15]; sprintf(dbf_text, "chk%x", cdev->private->schid.sch_no); CIO_TRACE_EVENT(0, dbf_text); CIO_HEX_EVENT(0, irb, sizeof (struct irb)); }
int cio_clear(struct subchannel *sch) { int ccode; if (!sch) return -ENODEV; CIO_TRACE_EVENT(2, "clearIO"); CIO_TRACE_EVENT(2, dev_name(&sch->dev)); ccode = csch (sch->schid); CIO_HEX_EVENT(2, &ccode, sizeof(ccode)); switch (ccode) { case 0: sch->schib.scsw.cmd.actl |= SCSW_ACTL_CLEAR_PEND; return 0; default: return -ENODEV; } }
int cio_resume (struct subchannel *sch) { int ccode; CIO_TRACE_EVENT(4, "resIO"); CIO_TRACE_EVENT(4, dev_name(&sch->dev)); ccode = rsch (sch->schid); CIO_HEX_EVENT(4, &ccode, sizeof(ccode)); switch (ccode) { case 0: sch->schib.scsw.cmd.actl |= SCSW_ACTL_RESUME_PEND; return 0; case 1: return -EBUSY; case 2: return -EINVAL; default: return -ENODEV; } }