diff --git a/drivers/s390/net/qeth.h b/drivers/s390/net/qeth.h index 84b108d7c7fda8077fad5c31ec7de28a43a63579..b34eb82edd983190e0f727b2f4cc04dee6065b16 100644 --- a/drivers/s390/net/qeth.h +++ b/drivers/s390/net/qeth.h @@ -288,6 +288,7 @@ qeth_is_ipa_enabled(struct qeth_ipa_info *ipa, enum qeth_ipa_funcs func) */ #define IF_NAME_LEN 16 #define QETH_TX_TIMEOUT 100 * HZ +#define QETH_RCD_TIMEOUT 60 * HZ #define QETH_HEADER_SIZE 32 #define MAX_PORTNO 15 #define QETH_FAKE_LL_LEN_ETH ETH_HLEN @@ -582,6 +583,8 @@ enum qeth_channel_states { CH_STATE_ACTIVATING, CH_STATE_HALTED, CH_STATE_STOPPED, + CH_STATE_RCD, + CH_STATE_RCD_DONE, }; /** * card state machine diff --git a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c index ad7792dc1a0461b9f7fb1a8e2758e56c5e18fec6..6fd8870551d36ff9245975c994f1d904d983dd8e 100644 --- a/drivers/s390/net/qeth_main.c +++ b/drivers/s390/net/qeth_main.c @@ -315,7 +315,8 @@ qeth_alloc_card(void) } static long -__qeth_check_irb_error(struct ccw_device *cdev, struct irb *irb) +__qeth_check_irb_error(struct ccw_device *cdev, unsigned long intparm, + struct irb *irb) { if (!IS_ERR(irb)) return 0; @@ -330,6 +331,14 @@ __qeth_check_irb_error(struct ccw_device *cdev, struct irb *irb) PRINT_WARN("timeout on device %s\n", cdev->dev.bus_id); QETH_DBF_TEXT(trace, 2, "ckirberr"); QETH_DBF_TEXT_(trace, 2, " rc%d", -ETIMEDOUT); + if (intparm == QETH_RCD_PARM) { + struct qeth_card *card = CARD_FROM_CDEV(cdev); + + if (card && (card->data.ccwdev == cdev)) { + card->data.state = CH_STATE_DOWN; + wake_up(&card->wait_q); + } + } break; default: PRINT_WARN("unknown error %ld on device %s\n", PTR_ERR(irb), @@ -401,7 +410,7 @@ qeth_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) QETH_DBF_TEXT(trace,5,"irq"); - if (__qeth_check_irb_error(cdev, irb)) + if (__qeth_check_irb_error(cdev, intparm, irb)) return; cstat = irb->scsw.cstat; dstat = irb->scsw.dstat; @@ -429,7 +438,8 @@ qeth_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) channel->state = CH_STATE_HALTED; /*let's wake up immediately on data channel*/ - if ((channel == &card->data) && (intparm != 0)) + if ((channel == &card->data) && (intparm != 0) && + (intparm != QETH_RCD_PARM)) goto out; if (intparm == QETH_CLEAR_CHANNEL_PARM) { @@ -453,6 +463,10 @@ qeth_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) HEXDUMP16(WARN,"irb: ",irb); HEXDUMP16(WARN,"sense data: ",irb->ecw); } + if (intparm == QETH_RCD_PARM) { + channel->state = CH_STATE_DOWN; + goto out; + } rc = qeth_get_problem(cdev,irb); if (rc) { qeth_schedule_recovery(card); @@ -460,6 +474,10 @@ qeth_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) } } + if (intparm == QETH_RCD_PARM) { + channel->state = CH_STATE_RCD_DONE; + goto out; + } if (intparm) { buffer = (struct qeth_cmd_buffer *) __va((addr_t)intparm); buffer->state = BUF_STATE_PROCESSED; @@ -1204,6 +1222,54 @@ qeth_probe_device(struct ccwgroup_device *gdev) } +static int qeth_read_conf_data(struct qeth_card *card, void **buffer, + int *length) +{ + struct ciw *ciw; + char *rcd_buf; + int ret; + struct qeth_channel *channel = &card->data; + unsigned long flags; + + /* + * scan for RCD command in extended SenseID data + */ + ciw = ccw_device_get_ciw(channel->ccwdev, CIW_TYPE_RCD); + if (!ciw || ciw->cmd == 0) + return -EOPNOTSUPP; + rcd_buf = kzalloc(ciw->count, GFP_KERNEL | GFP_DMA); + if (!rcd_buf) + return -ENOMEM; + + channel->ccw.cmd_code = ciw->cmd; + channel->ccw.cda = (__u32) __pa (rcd_buf); + channel->ccw.count = ciw->count; + channel->ccw.flags = CCW_FLAG_SLI; + channel->state = CH_STATE_RCD; + spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); + ret = ccw_device_start_timeout(channel->ccwdev, &channel->ccw, + QETH_RCD_PARM, LPM_ANYPATH, 0, + QETH_RCD_TIMEOUT); + spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); + if (!ret) + wait_event(card->wait_q, + (channel->state == CH_STATE_RCD_DONE || + channel->state == CH_STATE_DOWN)); + if (channel->state == CH_STATE_DOWN) + ret = -EIO; + else + channel->state = CH_STATE_DOWN; + if (ret) { + kfree(rcd_buf); + *buffer = NULL; + *length = 0; + } else { + *length = ciw->count; + *buffer = rcd_buf; + } + return ret; +} + static int qeth_get_unitaddr(struct qeth_card *card) { @@ -1212,9 +1278,9 @@ qeth_get_unitaddr(struct qeth_card *card) int rc; QETH_DBF_TEXT(setup, 2, "getunit"); - rc = read_conf_data(CARD_DDEV(card), (void **) &prcd, &length); + rc = qeth_read_conf_data(card, (void **) &prcd, &length); if (rc) { - PRINT_ERR("read_conf_data for device %s returned %i\n", + PRINT_ERR("qeth_read_conf_data for device %s returned %i\n", CARD_DDEV_ID(card), rc); return rc; } @@ -1223,6 +1289,7 @@ qeth_get_unitaddr(struct qeth_card *card) card->info.cula = prcd[63]; card->info.guestlan = ((prcd[0x10] == _ascebc['V']) && (prcd[0x11] == _ascebc['M'])); + kfree(prcd); return 0; } diff --git a/drivers/s390/net/qeth_mpc.h b/drivers/s390/net/qeth_mpc.h index 0477c47471c5a7ccb60c00bbd4179ee73a5f47c6..d74bc43da72ae7c72cfb4a27c734e6db4adebccb 100644 --- a/drivers/s390/net/qeth_mpc.h +++ b/drivers/s390/net/qeth_mpc.h @@ -37,6 +37,7 @@ extern unsigned char IPA_PDU_HEADER[]; #define QETH_CLEAR_CHANNEL_PARM -10 #define QETH_HALT_CHANNEL_PARM -11 +#define QETH_RCD_PARM -12 /*****************************************************************************/ /* IP Assist related definitions */