/* * The root device for ctcm group devices
*/ staticstruct device *ctcm_root_dev;
/* * Linked list of all detected channels.
*/ struct channel *channels;
/* * Unpack a just received skb and hand it over to * upper layers. * * ch The channel where this skb has been received. * pskb The received skb.
*/ void ctcm_unpack_skb(struct channel *ch, struct sk_buff *pskb)
{ struct net_device *dev = ch->netdev; struct ctcm_priv *priv = dev->ml_priv;
__u16 len = *((__u16 *) pskb->data);
/* * Release a specific channel in the channel list. * * ch Pointer to channel struct to be released.
*/ staticvoid channel_free(struct channel *ch)
{
CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, "%s(%s)", CTCM_FUNTAIL, ch->id);
ch->flags &= ~CHANNEL_FLAGS_INUSE;
fsm_newstate(ch->fsm, CTC_STATE_IDLE);
}
/* * Remove a specific channel in the channel list. * * ch Pointer to channel struct to be released.
*/ staticvoid channel_remove(struct channel *ch)
{ struct channel **c = &channels; char chid[CTCM_ID_SIZE]; int ok = 0;
if (ch == NULL) return; else
strscpy(chid, ch->id, sizeof(chid));
channel_free(ch); while (*c) { if (*c == ch) {
*c = ch->next;
fsm_deltimer(&ch->timer); if (IS_MPC(ch))
fsm_deltimer(&ch->sweep_timer);
kfree_fsm(ch->fsm);
clear_normalized_cda(&ch->ccw[4]); if (ch->trans_skb != NULL) {
clear_normalized_cda(&ch->ccw[1]);
dev_kfree_skb_any(ch->trans_skb);
} if (IS_MPC(ch)) {
tasklet_kill(&ch->ch_tasklet);
tasklet_kill(&ch->ch_disc_tasklet);
kfree(ch->discontact_th);
}
kfree(ch->ccw);
kfree(ch->irb);
kfree(ch);
ok = 1; break;
}
c = &((*c)->next);
}
/* * Get a specific channel from the channel list. * * type Type of channel we are interested in. * id Id of channel we are interested in. * direction Direction we want to use this channel for. * * returns Pointer to a channel or NULL if no matching channel available.
*/ staticstruct channel *channel_get(enum ctcm_channel_types type, char *id, int direction)
{ struct channel *ch = channels;
CTCM_DBF_TEXT_(ERROR, CTC_DBF_WARN, "irb error %ld on device %s\n",
PTR_ERR(irb), dev_name(&cdev->dev));
switch (PTR_ERR(irb)) { case -EIO:
dev_err(&cdev->dev, "An I/O-error occurred on the CTCM device\n"); break; case -ETIMEDOUT:
dev_err(&cdev->dev, "An adapter hardware operation timed out\n"); break; default:
dev_err(&cdev->dev, "An error occurred on the adapter hardware\n");
} return PTR_ERR(irb);
}
/* * Check sense of a unit check. * * ch The channel, the sense code belongs to. * sense The sense code to inspect.
*/ staticvoid ccw_unit_check(struct channel *ch, __u8 sense)
{
CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG, "%s(%s): %02x",
CTCM_FUNTAIL, ch->id, sense);
/* * Open an interface. * Called from generic network layer when ifconfig up is run. * * dev Pointer to interface struct. * * returns 0 on success, -ERRNO on failure. (Never fails.)
*/ int ctcm_open(struct net_device *dev)
{ struct ctcm_priv *priv = dev->ml_priv;
/* * Close an interface. * Called from generic network layer when ifconfig down is run. * * dev Pointer to interface struct. * * returns 0 on success, -ERRNO on failure. (Never fails.)
*/ int ctcm_close(struct net_device *dev)
{ struct ctcm_priv *priv = dev->ml_priv;
/* * Transmit a packet. * This is a helper function for ctcm_tx(). * * ch Channel to be used for sending. * skb Pointer to struct sk_buff of packet to send. * The linklevel header has already been set up * by ctcm_tx(). * * returns 0 on success, -ERRNO on failure. (Never fails.)
*/ staticint ctcm_transmit_skb(struct channel *ch, struct sk_buff *skb)
{ unsignedlong saveflags; struct ll_header header; int rc = 0;
__u16 block_len; int ccw_idx; struct sk_buff *nskb; unsignedlong hi;
/* we need to acquire the lock for testing the state * otherwise we can have an IRQ changing the state to * TXIDLE after the test but before acquiring the lock.
*/
spin_lock_irqsave(&ch->collect_lock, saveflags); if (fsm_getstate(ch->fsm) != CTC_STATE_TXIDLE) { int l = skb->len + LL_HEADER_LENGTH;
/* sweep processing is not complete until response and request */ /* has completed for all read channels in group */ if (grp->in_sweep == 0) {
grp->in_sweep = 1;
grp->sweep_rsp_pend_num = grp->active_channels[CTCM_READ];
grp->sweep_req_pend_num = grp->active_channels[CTCM_READ];
}
CTCM_PR_DEBUG("%s(%s): Put on collect_q - skb len: %04x \n" "pdu header and data for up to 32 bytes:\n",
__func__, dev->name, skb->len);
CTCM_D3_DUMP((char *)skb->data, min_t(int, 32, skb->len));
/* * Protect skb against beeing free'd by upper * layers.
*/
refcount_inc(&skb->users);
/* * IDAL support in CTCM is broken, so we have to * care about skb's above 2G ourselves.
*/
hi = ((unsignedlong)skb->tail + TH_HEADER_LENGTH) >> 31; if (hi) {
nskb = __dev_alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA); if (!nskb) { goto nomem_exit;
} else {
skb_put_data(nskb, skb->data, skb->len);
refcount_inc(&nskb->users);
refcount_dec(&skb->users);
dev_kfree_skb_irq(skb);
skb = nskb;
}
}
CTCM_PR_DBGDATA("%s(%s): skb len: %04x\n - pdu header and data for " "up to 32 bytes sent to vtam:\n",
__func__, dev->name, skb->len);
CTCM_D3_DUMP((char *)skb->data, min_t(int, 32, skb->len));
ch->ccw[4].count = skb->len; if (set_normalized_cda(&ch->ccw[4], skb->data)) { /* * idal allocation failed, try via copying to trans_skb. * trans_skb usually has a pre-allocated idal.
*/ if (ctcm_checkalloc_buffer(ch)) { /* * Remove our header. * It gets added again on retransmit.
*/ goto nomem_exit;
}
/* * If channels are not running, try to restart them * and throw away packet.
*/ if (fsm_getstate(priv->fsm) != DEV_STATE_RUNNING) {
fsm_event(priv->fsm, DEV_EVENT_START, dev);
dev_kfree_skb(skb);
priv->stats.tx_dropped++;
priv->stats.tx_errors++;
priv->stats.tx_carrier_errors++; return NETDEV_TX_OK;
}
if (ctcm_test_and_set_busy(dev)) return NETDEV_TX_BUSY;
return NETDEV_TX_OK; /* handle freeing of skb here */
}
/* * Sets MTU of an interface. * * dev Pointer to interface struct. * new_mtu The new MTU to use for this interface. * * returns 0 on success, -EINVAL if MTU is out of valid range. * (valid range is 576 .. 65527). If VM is on the * remote side, maximum MTU is 32760, however this is * not checked here.
*/ staticint ctcm_change_mtu(struct net_device *dev, int new_mtu)
{ struct ctcm_priv *priv; int max_bufsize;
/* * Initialize everything of the net device except the name and the * channel structs.
*/ staticstruct net_device *ctcm_init_netdevice(struct ctcm_priv *priv)
{ struct net_device *dev; struct mpc_group *grp; if (!priv) return NULL;
if (IS_MPC(priv))
dev = alloc_netdev(0, MPC_DEVICE_GENE, NET_NAME_UNKNOWN,
ctcm_dev_setup); else
dev = alloc_netdev(0, CTC_DEVICE_GENE, NET_NAME_UNKNOWN,
ctcm_dev_setup);
/* * Add a new channel to the list of channels. * Keeps the channel list sorted. * * cdev The ccw_device to be added. * type The type class of the new channel. * priv Points to the private data of the ccwgroup_device. * * returns 0 on success, !0 on error.
*/ staticint 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);
/* * "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;
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);
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;
}
/* * Return type of a detected device.
*/ staticenum ctcm_channel_types get_channel_type(struct ccw_device_id *id)
{ enum ctcm_channel_types type;
type = (enum ctcm_channel_types)id->driver_info;
if (type == ctcm_channel_type_ficon)
type = ctcm_channel_type_escon;
return type;
}
/* * * Setup an interface. * * cgdev Device to be setup. * * returns 0 on success, !0 on failure.
*/ staticint ctcm_new_device(struct ccwgroup_device *cgdev)
{ char read_id[CTCM_ID_SIZE]; char write_id[CTCM_ID_SIZE]; int direction; enum ctcm_channel_types type; struct ctcm_priv *priv; struct net_device *dev; struct ccw_device *cdev0; struct ccw_device *cdev1; struct channel *readc; struct channel *writec; int ret; int result;
priv = dev_get_drvdata(&cgdev->dev); if (!priv) {
result = -ENODEV; goto out_err_result;
}
ret = add_channel(cdev0, type, priv); if (ret) {
result = ret; goto out_err_result;
}
ret = add_channel(cdev1, type, priv); if (ret) {
result = ret; goto out_remove_channel1;
}
ret = ccw_device_set_online(cdev0); if (ret != 0) {
CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE, "%s(%s) set_online rc=%d",
CTCM_FUNTAIL, read_id, ret);
result = -EIO; goto out_remove_channel2;
}
ret = ccw_device_set_online(cdev1); if (ret != 0) {
CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE, "%s(%s) set_online rc=%d",
CTCM_FUNTAIL, write_id, ret);
result = -EIO; goto out_ccw1;
}
dev = ctcm_init_netdevice(priv); if (dev == NULL) {
result = -ENODEV; goto out_ccw2;
}
for (direction = CTCM_READ; direction <= CTCM_WRITE; direction++) {
priv->channel[direction] =
channel_get(type, direction == CTCM_READ ?
read_id : write_id, direction); if (priv->channel[direction] == NULL) { if (direction == CTCM_WRITE)
channel_free(priv->channel[CTCM_READ]);
result = -ENODEV; goto out_dev;
}
priv->channel[direction]->netdev = dev;
priv->channel[direction]->protocol = priv->protocol;
priv->channel[direction]->max_bufsize = priv->buffer_size;
} /* sysfs magic */
SET_NETDEV_DEV(dev, &cgdev->dev);
if (register_netdev(dev)) {
result = -ENODEV; goto out_dev;
}
/* * Shutdown an interface. * * cgdev Device to be shut down. * * returns 0 on success, !0 on failure.
*/ staticint ctcm_shutdown_device(struct ccwgroup_device *cgdev)
{ struct ctcm_priv *priv; struct net_device *dev;
priv = dev_get_drvdata(&cgdev->dev); if (!priv) return -ENODEV;
if (priv->channel[CTCM_READ]) {
dev = priv->channel[CTCM_READ]->netdev;
CTCM_DBF_DEV(SETUP, dev, ""); /* Close the device */
ctcm_close(dev);
dev->flags &= ~IFF_RUNNING;
channel_free(priv->channel[CTCM_READ]);
} else
dev = NULL;
if (priv->channel[CTCM_WRITE])
channel_free(priv->channel[CTCM_WRITE]);
if (dev) {
unregister_netdev(dev);
ctcm_free_netdevice(dev);
}
/* * Prepare to be unloaded. Free IRQ's and release all resources. * This is called just before this module is unloaded. It is * not called, if the usage count is !0, so we don't need to check * for that.
*/ staticvoid __exit ctcm_exit(void)
{
ccwgroup_driver_unregister(&ctcm_group_driver);
ccw_driver_unregister(&ctcm_ccw_driver);
root_device_unregister(ctcm_root_dev);
ctcm_unregister_dbf_views();
pr_info("CTCM driver unloaded\n");
}
/* * Initialize module. * This is called just after the module is loaded. * * returns 0 on success, !0 on error.
*/ staticint __init ctcm_init(void)
{ int ret;
channels = NULL;
ret = ctcm_register_dbf_views(); if (ret) goto out_err;
ctcm_root_dev = root_device_register("ctcm");
ret = PTR_ERR_OR_ZERO(ctcm_root_dev); if (ret) goto register_err;
ret = ccw_driver_register(&ctcm_ccw_driver); if (ret) goto ccw_err;
ctcm_group_driver.driver.groups = ctcm_drv_attr_groups;
ret = ccwgroup_driver_register(&ctcm_group_driver); if (ret) goto ccwgroup_err;
print_banner(); return 0;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.