in net/ctcm_main.c [1306:1458]
static int add_channel(struct ccw_device *cdev, enum ctcm_channel_types type,
struct ctcm_priv *priv)
{
struct channel **c = &channels;
struct channel *ch;
int ccw_num;
int rc = 0;
CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,
"%s(%s), type %d, proto %d",
__func__, dev_name(&cdev->dev), type, priv->protocol);
ch = kzalloc(sizeof(struct channel), GFP_KERNEL);
if (ch == NULL)
return -ENOMEM;
ch->protocol = priv->protocol;
if (IS_MPC(priv)) {
ch->discontact_th = kzalloc(TH_HEADER_LENGTH, GFP_KERNEL);
if (ch->discontact_th == NULL)
goto nomem_return;
ch->discontact_th->th_blk_flag = TH_DISCONTACT;
tasklet_init(&ch->ch_disc_tasklet,
mpc_action_send_discontact, (unsigned long)ch);
tasklet_init(&ch->ch_tasklet, ctcmpc_bh, (unsigned long)ch);
ch->max_bufsize = (MPC_BUFSIZE_DEFAULT - 35);
ccw_num = 17;
} else
ccw_num = 8;
ch->ccw = kcalloc(ccw_num, sizeof(struct ccw1), GFP_KERNEL | GFP_DMA);
if (ch->ccw == NULL)
goto nomem_return;
ch->cdev = cdev;
snprintf(ch->id, CTCM_ID_SIZE, "ch-%s", dev_name(&cdev->dev));
ch->type = type;
/*
* "static" ccws are used in the following way:
*
* ccw[0..2] (Channel program for generic I/O):
* 0: prepare
* 1: read or write (depending on direction) with fixed
* buffer (idal allocated once when buffer is allocated)
* 2: nop
* ccw[3..5] (Channel program for direct write of packets)
* 3: prepare
* 4: write (idal allocated on every write).
* 5: nop
* ccw[6..7] (Channel program for initial channel setup):
* 6: set extended mode
* 7: nop
*
* ch->ccw[0..5] are initialized in ch_action_start because
* the channel's direction is yet unknown here.
*
* ccws used for xid2 negotiations
* ch-ccw[8-14] need to be used for the XID exchange either
* X side XID2 Processing
* 8: write control
* 9: write th
* 10: write XID
* 11: read th from secondary
* 12: read XID from secondary
* 13: read 4 byte ID
* 14: nop
* Y side XID Processing
* 8: sense
* 9: read th
* 10: read XID
* 11: write th
* 12: write XID
* 13: write 4 byte ID
* 14: nop
*
* ccws used for double noop due to VM timing issues
* which result in unrecoverable Busy on channel
* 15: nop
* 16: nop
*/
ch->ccw[6].cmd_code = CCW_CMD_SET_EXTENDED;
ch->ccw[6].flags = CCW_FLAG_SLI;
ch->ccw[7].cmd_code = CCW_CMD_NOOP;
ch->ccw[7].flags = CCW_FLAG_SLI;
if (IS_MPC(priv)) {
ch->ccw[15].cmd_code = CCW_CMD_WRITE;
ch->ccw[15].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
ch->ccw[15].count = TH_HEADER_LENGTH;
ch->ccw[15].cda = virt_to_phys(ch->discontact_th);
ch->ccw[16].cmd_code = CCW_CMD_NOOP;
ch->ccw[16].flags = CCW_FLAG_SLI;
ch->fsm = init_fsm(ch->id, ctc_ch_state_names,
ctc_ch_event_names, CTC_MPC_NR_STATES,
CTC_MPC_NR_EVENTS, ctcmpc_ch_fsm,
mpc_ch_fsm_len, GFP_KERNEL);
} else {
ch->fsm = init_fsm(ch->id, ctc_ch_state_names,
ctc_ch_event_names, CTC_NR_STATES,
CTC_NR_EVENTS, ch_fsm,
ch_fsm_len, GFP_KERNEL);
}
if (ch->fsm == NULL)
goto nomem_return;
fsm_newstate(ch->fsm, CTC_STATE_IDLE);
ch->irb = kzalloc(sizeof(struct irb), GFP_KERNEL);
if (ch->irb == NULL)
goto nomem_return;
while (*c && ctcm_less_than((*c)->id, ch->id))
c = &(*c)->next;
if (*c && (!strncmp((*c)->id, ch->id, CTCM_ID_SIZE))) {
CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,
"%s (%s) already in list, using old entry",
__func__, (*c)->id);
goto free_return;
}
spin_lock_init(&ch->collect_lock);
fsm_settimer(ch->fsm, &ch->timer);
skb_queue_head_init(&ch->io_queue);
skb_queue_head_init(&ch->collect_queue);
if (IS_MPC(priv)) {
fsm_settimer(ch->fsm, &ch->sweep_timer);
skb_queue_head_init(&ch->sweep_queue);
}
ch->next = *c;
*c = ch;
return 0;
nomem_return:
rc = -ENOMEM;
free_return: /* note that all channel pointers are 0 or valid */
kfree(ch->ccw);
kfree(ch->discontact_th);
kfree_fsm(ch->fsm);
kfree(ch->irb);
kfree(ch);
return rc;
}