📄 lcs.c
字号:
/** * Get empty buffer. */static struct lcs_buffer *__lcs_get_buffer(struct lcs_channel *channel){ int index; LCS_DBF_TEXT(5, trace, "_getbuff"); index = channel->io_idx; do { if (channel->iob[index].state == BUF_STATE_EMPTY) { channel->iob[index].state = BUF_STATE_LOCKED; return channel->iob + index; } index = (index + 1) & (LCS_NUM_BUFFS - 1); } while (index != channel->io_idx); return NULL;}static struct lcs_buffer *lcs_get_buffer(struct lcs_channel *channel){ struct lcs_buffer *buffer; unsigned long flags; LCS_DBF_TEXT(5, trace, "getbuff"); spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); buffer = __lcs_get_buffer(channel); spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); return buffer;}/** * Resume channel program if the channel is suspended. */static int__lcs_resume_channel(struct lcs_channel *channel){ int rc; if (channel->state != CH_STATE_SUSPENDED) return 0; if (channel->ccws[channel->io_idx].flags & CCW_FLAG_SUSPEND) return 0; LCS_DBF_TEXT_(5, trace, "rsch%s", channel->ccwdev->dev.bus_id); rc = ccw_device_resume(channel->ccwdev); if (rc) { LCS_DBF_TEXT_(4, trace, "ersc%s", channel->ccwdev->dev.bus_id); PRINT_ERR("Error in lcs_resume_channel: rc=%d\n",rc); } else channel->state = CH_STATE_RUNNING; return rc;}/** * Make a buffer ready for processing. */static inline void__lcs_ready_buffer_bits(struct lcs_channel *channel, int index){ int prev, next; LCS_DBF_TEXT(5, trace, "rdybits"); prev = (index - 1) & (LCS_NUM_BUFFS - 1); next = (index + 1) & (LCS_NUM_BUFFS - 1); /* Check if we may clear the suspend bit of this buffer. */ if (channel->ccws[next].flags & CCW_FLAG_SUSPEND) { /* Check if we have to set the PCI bit. */ if (!(channel->ccws[prev].flags & CCW_FLAG_SUSPEND)) /* Suspend bit of the previous buffer is not set. */ channel->ccws[index].flags |= CCW_FLAG_PCI; /* Suspend bit of the next buffer is set. */ channel->ccws[index].flags &= ~CCW_FLAG_SUSPEND; }}static intlcs_ready_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer){ unsigned long flags; int index, rc; LCS_DBF_TEXT(5, trace, "rdybuff"); if (buffer->state != BUF_STATE_LOCKED && buffer->state != BUF_STATE_PROCESSED) BUG(); spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); buffer->state = BUF_STATE_READY; index = buffer - channel->iob; /* Set length. */ channel->ccws[index].count = buffer->count; /* Check relevant PCI/suspend bits. */ __lcs_ready_buffer_bits(channel, index); rc = __lcs_resume_channel(channel); spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); return rc;}/** * Mark the buffer as processed. Take care of the suspend bit * of the previous buffer. This function is called from * interrupt context, so the lock must not be taken. */static int__lcs_processed_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer){ int index, prev, next; LCS_DBF_TEXT(5, trace, "prcsbuff"); if (buffer->state != BUF_STATE_READY) BUG(); buffer->state = BUF_STATE_PROCESSED; index = buffer - channel->iob; prev = (index - 1) & (LCS_NUM_BUFFS - 1); next = (index + 1) & (LCS_NUM_BUFFS - 1); /* Set the suspend bit and clear the PCI bit of this buffer. */ channel->ccws[index].flags |= CCW_FLAG_SUSPEND; channel->ccws[index].flags &= ~CCW_FLAG_PCI; /* Check the suspend bit of the previous buffer. */ if (channel->iob[prev].state == BUF_STATE_READY) { /* * Previous buffer is in state ready. It might have * happened in lcs_ready_buffer that the suspend bit * has not been cleared to avoid an endless loop. * Do it now. */ __lcs_ready_buffer_bits(channel, prev); } /* Clear PCI bit of next buffer. */ channel->ccws[next].flags &= ~CCW_FLAG_PCI; return __lcs_resume_channel(channel);}/** * Put a processed buffer back to state empty. */static voidlcs_release_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer){ unsigned long flags; LCS_DBF_TEXT(5, trace, "relbuff"); if (buffer->state != BUF_STATE_LOCKED && buffer->state != BUF_STATE_PROCESSED) BUG(); spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); buffer->state = BUF_STATE_EMPTY; spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);}/** * Get buffer for a lan command. */static struct lcs_buffer *lcs_get_lancmd(struct lcs_card *card, int count){ struct lcs_buffer *buffer; struct lcs_cmd *cmd; LCS_DBF_TEXT(4, trace, "getlncmd"); /* Get buffer and wait if none is available. */ wait_event(card->write.wait_q, ((buffer = lcs_get_buffer(&card->write)) != NULL)); count += sizeof(struct lcs_header); *(__u16 *)(buffer->data + count) = 0; buffer->count = count + sizeof(__u16); buffer->callback = lcs_release_buffer; cmd = (struct lcs_cmd *) buffer->data; cmd->offset = count; cmd->type = LCS_FRAME_TYPE_CONTROL; cmd->slot = 0; return buffer;}static voidlcs_get_reply(struct lcs_reply *reply){ WARN_ON(atomic_read(&reply->refcnt) <= 0); atomic_inc(&reply->refcnt);}static voidlcs_put_reply(struct lcs_reply *reply){ WARN_ON(atomic_read(&reply->refcnt) <= 0); if (atomic_dec_and_test(&reply->refcnt)) { kfree(reply); }}static struct lcs_reply *lcs_alloc_reply(struct lcs_cmd *cmd){ struct lcs_reply *reply; LCS_DBF_TEXT(4, trace, "getreply"); reply = kzalloc(sizeof(struct lcs_reply), GFP_ATOMIC); if (!reply) return NULL; atomic_set(&reply->refcnt,1); reply->sequence_no = cmd->sequence_no; reply->received = 0; reply->rc = 0; init_waitqueue_head(&reply->wait_q); return reply;}/** * Notifier function for lancmd replies. Called from read irq. */static voidlcs_notify_lancmd_waiters(struct lcs_card *card, struct lcs_cmd *cmd){ struct list_head *l, *n; struct lcs_reply *reply; LCS_DBF_TEXT(4, trace, "notiwait"); spin_lock(&card->lock); list_for_each_safe(l, n, &card->lancmd_waiters) { reply = list_entry(l, struct lcs_reply, list); if (reply->sequence_no == cmd->sequence_no) { lcs_get_reply(reply); list_del_init(&reply->list); if (reply->callback != NULL) reply->callback(card, cmd); reply->received = 1; reply->rc = cmd->return_code; wake_up(&reply->wait_q); lcs_put_reply(reply); break; } } spin_unlock(&card->lock);}/** * Emit buffer of a lan comand. */voidlcs_lancmd_timeout(unsigned long data){ struct lcs_reply *reply, *list_reply, *r; unsigned long flags; LCS_DBF_TEXT(4, trace, "timeout"); reply = (struct lcs_reply *) data; spin_lock_irqsave(&reply->card->lock, flags); list_for_each_entry_safe(list_reply, r, &reply->card->lancmd_waiters,list) { if (reply == list_reply) { lcs_get_reply(reply); list_del_init(&reply->list); spin_unlock_irqrestore(&reply->card->lock, flags); reply->received = 1; reply->rc = -ETIME; wake_up(&reply->wait_q); lcs_put_reply(reply); return; } } spin_unlock_irqrestore(&reply->card->lock, flags);}static intlcs_send_lancmd(struct lcs_card *card, struct lcs_buffer *buffer, void (*reply_callback)(struct lcs_card *, struct lcs_cmd *)){ struct lcs_reply *reply; struct lcs_cmd *cmd; struct timer_list timer; unsigned long flags; int rc; LCS_DBF_TEXT(4, trace, "sendcmd"); cmd = (struct lcs_cmd *) buffer->data; cmd->return_code = 0; cmd->sequence_no = card->sequence_no++; reply = lcs_alloc_reply(cmd); if (!reply) return -ENOMEM; reply->callback = reply_callback; reply->card = card; spin_lock_irqsave(&card->lock, flags); list_add_tail(&reply->list, &card->lancmd_waiters); spin_unlock_irqrestore(&card->lock, flags); buffer->callback = lcs_release_buffer; rc = lcs_ready_buffer(&card->write, buffer); if (rc) return rc; init_timer(&timer); timer.function = lcs_lancmd_timeout; timer.data = (unsigned long) reply; timer.expires = jiffies + HZ*card->lancmd_timeout; add_timer(&timer); wait_event(reply->wait_q, reply->received); del_timer_sync(&timer); LCS_DBF_TEXT_(4, trace, "rc:%d",reply->rc); rc = reply->rc; lcs_put_reply(reply); return rc ? -EIO : 0;}/** * LCS startup command */static intlcs_send_startup(struct lcs_card *card, __u8 initiator){ struct lcs_buffer *buffer; struct lcs_cmd *cmd; LCS_DBF_TEXT(2, trace, "startup"); buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE); cmd = (struct lcs_cmd *) buffer->data; cmd->cmd_code = LCS_CMD_STARTUP; cmd->initiator = initiator; cmd->cmd.lcs_startup.buff_size = LCS_IOBUFFERSIZE; return lcs_send_lancmd(card, buffer, NULL);}/** * LCS shutdown command */static intlcs_send_shutdown(struct lcs_card *card){ struct lcs_buffer *buffer; struct lcs_cmd *cmd; LCS_DBF_TEXT(2, trace, "shutdown"); buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE); cmd = (struct lcs_cmd *) buffer->data; cmd->cmd_code = LCS_CMD_SHUTDOWN; cmd->initiator = LCS_INITIATOR_TCPIP; return lcs_send_lancmd(card, buffer, NULL);}/** * LCS lanstat command */static void__lcs_lanstat_cb(struct lcs_card *card, struct lcs_cmd *cmd){ LCS_DBF_TEXT(2, trace, "statcb"); memcpy(card->mac, cmd->cmd.lcs_lanstat_cmd.mac_addr, LCS_MAC_LENGTH);}static intlcs_send_lanstat(struct lcs_card *card){ struct lcs_buffer *buffer; struct lcs_cmd *cmd; LCS_DBF_TEXT(2,trace, "cmdstat"); buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE); cmd = (struct lcs_cmd *) buffer->data; /* Setup lanstat command. */ cmd->cmd_code = LCS_CMD_LANSTAT; cmd->initiator = LCS_INITIATOR_TCPIP; cmd->cmd.lcs_std_cmd.lan_type = card->lan_type; cmd->cmd.lcs_std_cmd.portno = card->portno; return lcs_send_lancmd(card, buffer, __lcs_lanstat_cb);}/** * send stoplan command */static intlcs_send_stoplan(struct lcs_card *card, __u8 initiator){ struct lcs_buffer *buffer; struct lcs_cmd *cmd; LCS_DBF_TEXT(2, trace, "cmdstpln"); buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE); cmd = (struct lcs_cmd *) buffer->data; cmd->cmd_code = LCS_CMD_STOPLAN; cmd->initiator = initiator; cmd->cmd.lcs_std_cmd.lan_type = card->lan_type; cmd->cmd.lcs_std_cmd.portno = card->portno; return lcs_send_lancmd(card, buffer, NULL);}/** * send startlan command */static void__lcs_send_startlan_cb(struct lcs_card *card, struct lcs_cmd *cmd){ LCS_DBF_TEXT(2, trace, "srtlancb"); card->lan_type = cmd->cmd.lcs_std_cmd.lan_type; card->portno = cmd->cmd.lcs_std_cmd.portno;}static intlcs_send_startlan(struct lcs_card *card, __u8 initiator){ struct lcs_buffer *buffer; struct lcs_cmd *cmd; LCS_DBF_TEXT(2, trace, "cmdstaln"); buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE); cmd = (struct lcs_cmd *) buffer->data; cmd->cmd_code = LCS_CMD_STARTLAN; cmd->initiator = initiator; cmd->cmd.lcs_std_cmd.lan_type = card->lan_type; cmd->cmd.lcs_std_cmd.portno = card->portno; return lcs_send_lancmd(card, buffer, __lcs_send_startlan_cb);}#ifdef CONFIG_IP_MULTICAST/** * send setipm command (Multicast) */static intlcs_send_setipm(struct lcs_card *card,struct lcs_ipm_list *ipm_list){ struct lcs_buffer *buffer; struct lcs_cmd *cmd; LCS_DBF_TEXT(2, trace, "cmdsetim"); buffer = lcs_get_lancmd(card, LCS_MULTICAST_CMD_SIZE); cmd = (struct lcs_cmd *) buffer->data; cmd->cmd_code = LCS_CMD_SETIPM; cmd->initiator = LCS_INITIATOR_TCPIP; cmd->cmd.lcs_qipassist.lan_type = card->lan_type; cmd->cmd.lcs_qipassist.portno = card->portno; cmd->cmd.lcs_qipassist.version = 4; cmd->cmd.lcs_qipassist.num_ip_pairs = 1; memcpy(cmd->cmd.lcs_qipassist.lcs_ipass_ctlmsg.ip_mac_pair, &ipm_list->ipm, sizeof (struct lcs_ip_mac_pair)); LCS_DBF_TEXT_(2, trace, "%x",ipm_list->ipm.ip_addr); return lcs_send_lancmd(card, buffer, NULL);}/** * send delipm command (Multicast) */static intlcs_send_delipm(struct lcs_card *card,struct lcs_ipm_list *ipm_list){ struct lcs_buffer *buffer; struct lcs_cmd *cmd; LCS_DBF_TEXT(2, trace, "cmddelim"); buffer = lcs_get_lancmd(card, LCS_MULTICAST_CMD_SIZE); cmd = (struct lcs_cmd *) buffer->data; cmd->cmd_code = LCS_CMD_DELIPM; cmd->initiator = LCS_INITIATOR_TCPIP; cmd->cmd.lcs_qipassist.lan_type = card->lan_type; cmd->cmd.lcs_qipassist.portno = card->portno; cmd->cmd.lcs_qipassist.version = 4; cmd->cmd.lcs_qipassist.num_ip_pairs = 1; memcpy(cmd->cmd.lcs_qipassist.lcs_ipass_ctlmsg.ip_mac_pair, &ipm_list->ipm, sizeof (struct lcs_ip_mac_pair)); LCS_DBF_TEXT_(2, trace, "%x",ipm_list->ipm.ip_addr); return lcs_send_lancmd(card, buffer, NULL);}/** * check if multicast is supported by LCS */static void__lcs_check_multicast_cb(struct lcs_card *card, struct lcs_cmd *cmd){ LCS_DBF_TEXT(2, trace, "chkmccb"); card->ip_assists_supported = cmd->cmd.lcs_qipassist.ip_assists_supported; card->ip_assists_enabled = cmd->cmd.lcs_qipassist.ip_assists_enabled;}static intlcs_check_multicast_support(struct lcs_card *card){ struct lcs_buffer *buffer; struct lcs_cmd *cmd; int rc; LCS_DBF_TEXT(2, trace, "cmdqipa"); /* Send query ipassist. */ buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE); cmd = (struct lcs_cmd *) buffer->data; cmd->cmd_code = LCS_CMD_QIPASSIST; cmd->initiator = LCS_INITIATOR_TCPIP; cmd->cmd.lcs_qipassist.lan_type = card->lan_type; cmd->cmd.lcs_qipassist.portno = card->portno; cmd->cmd.lcs_qipassist.version = 4; cmd->cmd.lcs_qipassist.num_ip_pairs = 1; rc = lcs_send_lancmd(card, buffer, __lcs_check_multicast_cb); if (rc != 0) { PRINT_ERR("Query IPAssist failed. Assuming unsupported!\n"); return -EOPNOTSUPP; } if (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT) return 0; return -EOPNOTSUPP;}/** * set or del multicast address on LCS card */static voidlcs_fix_multicast_list(struct lcs_card *card){ struct list_head failed_list; struct lcs_ipm_list *ipm, *tmp; unsigned long flags; int rc; LCS_DBF_TEXT(4,trace, "fixipm"); INIT_LIST_HEAD(&failed_list); spin_lock_irqsave(&card->ipm_lock, flags);list_modified: list_for_each_entry_safe(ipm, tmp, &card->ipm_list, list){ switch (ipm->ipm_state) { case LCS_IPM_STATE_SET_REQUIRED: /* del from ipm_list so noone else can tamper with * this entry */ list_del_init(&ipm->list); spin_unlock_irqrestore(&card->ipm_lock, flags); rc = lcs_send_setipm(card, ipm); spin_lock_irqsave(&card->ipm_lock, flags); if (rc) { PRINT_INFO("Adding multicast address failed." "Table possibly full!\n"); /* store ipm in failed list -> will be added * to ipm_list again, so a retry will be done * during the next call of this function */ list_add_tail(&ipm->list, &failed_list); } else { ipm->ipm_state = LCS_IPM_STATE_ON_CARD; /* re-insert into ipm_list */ list_add_tail(&ipm->list, &card->ipm_list); } goto list_modified; case LCS_IPM_STATE_DEL_REQUIRED: list_del(&ipm->list); spin_unlock_irqrestore(&card->ipm_lock, flags); lcs_send_delipm(card, ipm); spin_lock_irqsave(&card->ipm_lock, flags); kfree(ipm); goto list_modified; case LCS_IPM_STATE_ON_CARD: break; } } /* re-insert all entries from the failed_list into ipm_list */ list_for_each_entry_safe(ipm, tmp, &failed_list, list) { list_del_init(&ipm->list); list_add_tail(&ipm->list, &card->ipm_list); } spin_unlock_irqrestore(&card->ipm_lock, flags);}/** * get mac address for the relevant Multicast address */static voidlcs_get_mac_for_ipm(__u32 ipm, char *mac, struct net_device *dev){ LCS_DBF_TEXT(4,trace, "getmac"); if (dev->type == ARPHRD_IEEE802_TR) ip_tr_mc_map(ipm, mac); else ip_eth_mc_map(ipm, mac);}/** * function called by net device to handle multicast address relevant things */static inline voidlcs_remove_mc_addresses(struct lcs_card *card, struct in_device *in4_dev){ struct ip_mc_list *im4; struct list_head *l; struct lcs_ipm_list *ipm; unsigned long flags;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -