449 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			449 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| /* Marvell Octeon EP (EndPoint) Ethernet Driver
 | |
|  *
 | |
|  * Copyright (C) 2020 Marvell.
 | |
|  *
 | |
|  */
 | |
| #include <linux/string.h>
 | |
| #include <linux/types.h>
 | |
| #include <linux/etherdevice.h>
 | |
| #include <linux/pci.h>
 | |
| #include <linux/wait.h>
 | |
| 
 | |
| #include "octep_config.h"
 | |
| #include "octep_main.h"
 | |
| #include "octep_ctrl_net.h"
 | |
| #include "octep_pfvf_mbox.h"
 | |
| 
 | |
| /* Control plane version */
 | |
| #define OCTEP_CP_VERSION_CURRENT	OCTEP_CP_VERSION(1, 0, 0)
 | |
| 
 | |
| static const u32 req_hdr_sz = sizeof(union octep_ctrl_net_req_hdr);
 | |
| static const u32 mtu_sz = sizeof(struct octep_ctrl_net_h2f_req_cmd_mtu);
 | |
| static const u32 mac_sz = sizeof(struct octep_ctrl_net_h2f_req_cmd_mac);
 | |
| static const u32 state_sz = sizeof(struct octep_ctrl_net_h2f_req_cmd_state);
 | |
| static const u32 link_info_sz = sizeof(struct octep_ctrl_net_link_info);
 | |
| static const u32 offloads_sz = sizeof(struct octep_ctrl_net_offloads);
 | |
| static atomic_t ctrl_net_msg_id;
 | |
| 
 | |
| /* Control plane version in which OCTEP_CTRL_NET_H2F_CMD was added */
 | |
| static const u32 octep_ctrl_net_h2f_cmd_versions[OCTEP_CTRL_NET_H2F_CMD_MAX] = {
 | |
| 	[OCTEP_CTRL_NET_H2F_CMD_INVALID ... OCTEP_CTRL_NET_H2F_CMD_DEV_REMOVE] =
 | |
| 	 OCTEP_CP_VERSION(1, 0, 0),
 | |
| 	[OCTEP_CTRL_NET_H2F_CMD_OFFLOADS] = OCTEP_CP_VERSION(1, 0, 1)
 | |
| 
 | |
| };
 | |
| 
 | |
| /* Control plane version in which OCTEP_CTRL_NET_F2H_CMD was added */
 | |
| static const u32 octep_ctrl_net_f2h_cmd_versions[OCTEP_CTRL_NET_F2H_CMD_MAX] = {
 | |
| 	[OCTEP_CTRL_NET_F2H_CMD_INVALID ... OCTEP_CTRL_NET_F2H_CMD_LINK_STATUS] =
 | |
| 	 OCTEP_CP_VERSION(1, 0, 0)
 | |
| };
 | |
| 
 | |
| static void init_send_req(struct octep_ctrl_mbox_msg *msg, void *buf,
 | |
| 			  u16 sz, int vfid)
 | |
| {
 | |
| 	msg->hdr.s.flags = OCTEP_CTRL_MBOX_MSG_HDR_FLAG_REQ;
 | |
| 	msg->hdr.s.msg_id = atomic_inc_return(&ctrl_net_msg_id) &
 | |
| 			    GENMASK(sizeof(msg->hdr.s.msg_id) * BITS_PER_BYTE, 0);
 | |
| 	msg->hdr.s.sz = req_hdr_sz + sz;
 | |
| 	msg->sg_num = 1;
 | |
| 	msg->sg_list[0].msg = buf;
 | |
| 	msg->sg_list[0].sz = msg->hdr.s.sz;
 | |
| 	if (vfid != OCTEP_CTRL_NET_INVALID_VFID) {
 | |
| 		msg->hdr.s.is_vf = 1;
 | |
| 		msg->hdr.s.vf_idx = vfid;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int octep_send_mbox_req(struct octep_device *oct,
 | |
| 			       struct octep_ctrl_net_wait_data *d,
 | |
| 			       bool wait_for_response)
 | |
| {
 | |
| 	int err, ret, cmd;
 | |
| 
 | |
| 	/* check if firmware is compatible for this request */
 | |
| 	cmd = d->data.req.hdr.s.cmd;
 | |
| 	if (octep_ctrl_net_h2f_cmd_versions[cmd] > oct->ctrl_mbox.max_fw_version ||
 | |
| 	    octep_ctrl_net_h2f_cmd_versions[cmd] < oct->ctrl_mbox.min_fw_version)
 | |
| 		return -EOPNOTSUPP;
 | |
| 
 | |
| 	err = octep_ctrl_mbox_send(&oct->ctrl_mbox, &d->msg);
 | |
| 	if (err < 0)
 | |
| 		return err;
 | |
| 
 | |
| 	if (!wait_for_response)
 | |
| 		return 0;
 | |
| 
 | |
| 	d->done = 0;
 | |
| 	INIT_LIST_HEAD(&d->list);
 | |
| 	list_add_tail(&d->list, &oct->ctrl_req_wait_list);
 | |
| 	ret = wait_event_interruptible_timeout(oct->ctrl_req_wait_q,
 | |
| 					       (d->done != 0),
 | |
| 					       msecs_to_jiffies(500));
 | |
| 	list_del(&d->list);
 | |
| 	if (ret == 0 || ret == 1)
 | |
| 		return -EAGAIN;
 | |
| 
 | |
| 	/**
 | |
| 	 * (ret == 0)  cond = false && timeout, return 0
 | |
| 	 * (ret < 0) interrupted by signal, return 0
 | |
| 	 * (ret == 1) cond = true && timeout, return 1
 | |
| 	 * (ret >= 1) cond = true && !timeout, return 1
 | |
| 	 */
 | |
| 
 | |
| 	if (d->data.resp.hdr.s.reply != OCTEP_CTRL_NET_REPLY_OK)
 | |
| 		return -EAGAIN;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int octep_ctrl_net_init(struct octep_device *oct)
 | |
| {
 | |
| 	struct octep_ctrl_mbox *ctrl_mbox;
 | |
| 	struct pci_dev *pdev = oct->pdev;
 | |
| 	int ret;
 | |
| 
 | |
| 	init_waitqueue_head(&oct->ctrl_req_wait_q);
 | |
| 	INIT_LIST_HEAD(&oct->ctrl_req_wait_list);
 | |
| 
 | |
| 	/* Initialize control mbox */
 | |
| 	ctrl_mbox = &oct->ctrl_mbox;
 | |
| 	ctrl_mbox->version = OCTEP_CP_VERSION_CURRENT;
 | |
| 	ctrl_mbox->barmem = CFG_GET_CTRL_MBOX_MEM_ADDR(oct->conf);
 | |
| 	ret = octep_ctrl_mbox_init(ctrl_mbox);
 | |
| 	if (ret) {
 | |
| 		dev_err(&pdev->dev, "Failed to initialize control mbox\n");
 | |
| 		return ret;
 | |
| 	}
 | |
| 	dev_info(&pdev->dev, "Control plane versions host: %llx, firmware: %x:%x\n",
 | |
| 		 ctrl_mbox->version, ctrl_mbox->min_fw_version,
 | |
| 		 ctrl_mbox->max_fw_version);
 | |
| 	oct->ctrl_mbox_ifstats_offset = ctrl_mbox->barmem_sz;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int octep_ctrl_net_get_link_status(struct octep_device *oct, int vfid)
 | |
| {
 | |
| 	struct octep_ctrl_net_wait_data d = {};
 | |
| 	struct octep_ctrl_net_h2f_req *req = &d.data.req;
 | |
| 	int err;
 | |
| 
 | |
| 	init_send_req(&d.msg, (void *)req, state_sz, vfid);
 | |
| 	req->hdr.s.cmd = OCTEP_CTRL_NET_H2F_CMD_LINK_STATUS;
 | |
| 	req->link.cmd = OCTEP_CTRL_NET_CMD_GET;
 | |
| 	err = octep_send_mbox_req(oct, &d, true);
 | |
| 	if (err < 0)
 | |
| 		return err;
 | |
| 
 | |
| 	return d.data.resp.link.state;
 | |
| }
 | |
| 
 | |
| int octep_ctrl_net_set_link_status(struct octep_device *oct, int vfid, bool up,
 | |
| 				   bool wait_for_response)
 | |
| {
 | |
| 	struct octep_ctrl_net_wait_data d = {};
 | |
| 	struct octep_ctrl_net_h2f_req *req = &d.data.req;
 | |
| 
 | |
| 	init_send_req(&d.msg, req, state_sz, vfid);
 | |
| 	req->hdr.s.cmd = OCTEP_CTRL_NET_H2F_CMD_LINK_STATUS;
 | |
| 	req->link.cmd = OCTEP_CTRL_NET_CMD_SET;
 | |
| 	req->link.state = (up) ? OCTEP_CTRL_NET_STATE_UP :
 | |
| 				OCTEP_CTRL_NET_STATE_DOWN;
 | |
| 
 | |
| 	return octep_send_mbox_req(oct, &d, wait_for_response);
 | |
| }
 | |
| 
 | |
| int octep_ctrl_net_set_rx_state(struct octep_device *oct, int vfid, bool up,
 | |
| 				bool wait_for_response)
 | |
| {
 | |
| 	struct octep_ctrl_net_wait_data d = {};
 | |
| 	struct octep_ctrl_net_h2f_req *req = &d.data.req;
 | |
| 
 | |
| 	init_send_req(&d.msg, req, state_sz, vfid);
 | |
| 	req->hdr.s.cmd = OCTEP_CTRL_NET_H2F_CMD_RX_STATE;
 | |
| 	req->link.cmd = OCTEP_CTRL_NET_CMD_SET;
 | |
| 	req->link.state = (up) ? OCTEP_CTRL_NET_STATE_UP :
 | |
| 				OCTEP_CTRL_NET_STATE_DOWN;
 | |
| 
 | |
| 	return octep_send_mbox_req(oct, &d, wait_for_response);
 | |
| }
 | |
| 
 | |
| int octep_ctrl_net_get_mac_addr(struct octep_device *oct, int vfid, u8 *addr)
 | |
| {
 | |
| 	struct octep_ctrl_net_wait_data d = {};
 | |
| 	struct octep_ctrl_net_h2f_req *req = &d.data.req;
 | |
| 	int err;
 | |
| 
 | |
| 	init_send_req(&d.msg, req, mac_sz, vfid);
 | |
| 	req->hdr.s.cmd = OCTEP_CTRL_NET_H2F_CMD_MAC;
 | |
| 	req->link.cmd = OCTEP_CTRL_NET_CMD_GET;
 | |
| 	err = octep_send_mbox_req(oct, &d, true);
 | |
| 	if (err < 0)
 | |
| 		return err;
 | |
| 
 | |
| 	memcpy(addr, d.data.resp.mac.addr, ETH_ALEN);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int octep_ctrl_net_set_mac_addr(struct octep_device *oct, int vfid, u8 *addr,
 | |
| 				bool wait_for_response)
 | |
| {
 | |
| 	struct octep_ctrl_net_wait_data d = {};
 | |
| 	struct octep_ctrl_net_h2f_req *req = &d.data.req;
 | |
| 
 | |
| 	init_send_req(&d.msg, req, mac_sz, vfid);
 | |
| 	req->hdr.s.cmd = OCTEP_CTRL_NET_H2F_CMD_MAC;
 | |
| 	req->mac.cmd = OCTEP_CTRL_NET_CMD_SET;
 | |
| 	memcpy(&req->mac.addr, addr, ETH_ALEN);
 | |
| 
 | |
| 	return octep_send_mbox_req(oct, &d, wait_for_response);
 | |
| }
 | |
| 
 | |
| int octep_ctrl_net_get_mtu(struct octep_device *oct, int vfid)
 | |
| {
 | |
| 	struct octep_ctrl_net_wait_data d = {};
 | |
| 	struct octep_ctrl_net_h2f_req *req;
 | |
| 	int err;
 | |
| 
 | |
| 	req = &d.data.req;
 | |
| 	init_send_req(&d.msg, req, mtu_sz, vfid);
 | |
| 	req->hdr.s.cmd = OCTEP_CTRL_NET_H2F_CMD_MTU;
 | |
| 	req->mtu.cmd = OCTEP_CTRL_NET_CMD_GET;
 | |
| 
 | |
| 	err = octep_send_mbox_req(oct, &d, true);
 | |
| 	if (err < 0)
 | |
| 		return err;
 | |
| 
 | |
| 	return d.data.resp.mtu.val;
 | |
| }
 | |
| 
 | |
| int octep_ctrl_net_set_mtu(struct octep_device *oct, int vfid, int mtu,
 | |
| 			   bool wait_for_response)
 | |
| {
 | |
| 	struct octep_ctrl_net_wait_data d = {};
 | |
| 	struct octep_ctrl_net_h2f_req *req = &d.data.req;
 | |
| 
 | |
| 	init_send_req(&d.msg, req, mtu_sz, vfid);
 | |
| 	req->hdr.s.cmd = OCTEP_CTRL_NET_H2F_CMD_MTU;
 | |
| 	req->mtu.cmd = OCTEP_CTRL_NET_CMD_SET;
 | |
| 	req->mtu.val = mtu;
 | |
| 
 | |
| 	return octep_send_mbox_req(oct, &d, wait_for_response);
 | |
| }
 | |
| 
 | |
| int octep_ctrl_net_get_if_stats(struct octep_device *oct, int vfid,
 | |
| 				struct octep_iface_rx_stats *rx_stats,
 | |
| 				struct octep_iface_tx_stats *tx_stats)
 | |
| {
 | |
| 	struct octep_ctrl_net_wait_data d = {};
 | |
| 	struct octep_ctrl_net_h2f_req *req = &d.data.req;
 | |
| 	struct octep_ctrl_net_h2f_resp *resp;
 | |
| 	int err;
 | |
| 
 | |
| 	init_send_req(&d.msg, req, 0, vfid);
 | |
| 	req->hdr.s.cmd = OCTEP_CTRL_NET_H2F_CMD_GET_IF_STATS;
 | |
| 	err = octep_send_mbox_req(oct, &d, true);
 | |
| 	if (err < 0)
 | |
| 		return err;
 | |
| 
 | |
| 	resp = &d.data.resp;
 | |
| 	memcpy(rx_stats, &resp->if_stats.rx_stats, sizeof(struct octep_iface_rx_stats));
 | |
| 	memcpy(tx_stats, &resp->if_stats.tx_stats, sizeof(struct octep_iface_tx_stats));
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int octep_ctrl_net_get_link_info(struct octep_device *oct, int vfid,
 | |
| 				 struct octep_iface_link_info *link_info)
 | |
| {
 | |
| 	struct octep_ctrl_net_wait_data d = {};
 | |
| 	struct octep_ctrl_net_h2f_req *req = &d.data.req;
 | |
| 	struct octep_ctrl_net_h2f_resp *resp;
 | |
| 	int err;
 | |
| 
 | |
| 	init_send_req(&d.msg, req, link_info_sz, vfid);
 | |
| 	req->hdr.s.cmd = OCTEP_CTRL_NET_H2F_CMD_LINK_INFO;
 | |
| 	req->link_info.cmd = OCTEP_CTRL_NET_CMD_GET;
 | |
| 	err = octep_send_mbox_req(oct, &d, true);
 | |
| 	if (err < 0)
 | |
| 		return err;
 | |
| 
 | |
| 	resp = &d.data.resp;
 | |
| 	link_info->supported_modes = resp->link_info.supported_modes;
 | |
| 	link_info->advertised_modes = resp->link_info.advertised_modes;
 | |
| 	link_info->autoneg = resp->link_info.autoneg;
 | |
| 	link_info->pause = resp->link_info.pause;
 | |
| 	link_info->speed = resp->link_info.speed;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int octep_ctrl_net_set_link_info(struct octep_device *oct, int vfid,
 | |
| 				 struct octep_iface_link_info *link_info,
 | |
| 				 bool wait_for_response)
 | |
| {
 | |
| 	struct octep_ctrl_net_wait_data d = {};
 | |
| 	struct octep_ctrl_net_h2f_req *req = &d.data.req;
 | |
| 
 | |
| 	init_send_req(&d.msg, req, link_info_sz, vfid);
 | |
| 	req->hdr.s.cmd = OCTEP_CTRL_NET_H2F_CMD_LINK_INFO;
 | |
| 	req->link_info.cmd = OCTEP_CTRL_NET_CMD_SET;
 | |
| 	req->link_info.info.advertised_modes = link_info->advertised_modes;
 | |
| 	req->link_info.info.autoneg = link_info->autoneg;
 | |
| 	req->link_info.info.pause = link_info->pause;
 | |
| 	req->link_info.info.speed = link_info->speed;
 | |
| 
 | |
| 	return octep_send_mbox_req(oct, &d, wait_for_response);
 | |
| }
 | |
| 
 | |
| static void process_mbox_resp(struct octep_device *oct,
 | |
| 			      struct octep_ctrl_mbox_msg *msg)
 | |
| {
 | |
| 	struct octep_ctrl_net_wait_data *pos, *n;
 | |
| 
 | |
| 	list_for_each_entry_safe(pos, n, &oct->ctrl_req_wait_list, list) {
 | |
| 		if (pos->msg.hdr.s.msg_id == msg->hdr.s.msg_id) {
 | |
| 			memcpy(&pos->data.resp,
 | |
| 			       msg->sg_list[0].msg,
 | |
| 			       msg->hdr.s.sz);
 | |
| 			pos->done = 1;
 | |
| 			wake_up_interruptible_all(&oct->ctrl_req_wait_q);
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int process_mbox_notify(struct octep_device *oct,
 | |
| 			       struct octep_ctrl_mbox_msg *msg)
 | |
| {
 | |
| 	struct net_device *netdev = oct->netdev;
 | |
| 	struct octep_ctrl_net_f2h_req *req;
 | |
| 	int cmd;
 | |
| 
 | |
| 	req = (struct octep_ctrl_net_f2h_req *)msg->sg_list[0].msg;
 | |
| 	cmd = req->hdr.s.cmd;
 | |
| 
 | |
| 	/* check if we support this command */
 | |
| 	if (octep_ctrl_net_f2h_cmd_versions[cmd] > OCTEP_CP_VERSION_CURRENT ||
 | |
| 	    octep_ctrl_net_f2h_cmd_versions[cmd] < OCTEP_CP_VERSION_CURRENT)
 | |
| 		return -EOPNOTSUPP;
 | |
| 
 | |
| 	if (msg->hdr.s.is_vf) {
 | |
| 		octep_pfvf_notify(oct, msg);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	switch (cmd) {
 | |
| 	case OCTEP_CTRL_NET_F2H_CMD_LINK_STATUS:
 | |
| 		if (netif_running(netdev)) {
 | |
| 			if (req->link.state) {
 | |
| 				dev_info(&oct->pdev->dev, "netif_carrier_on\n");
 | |
| 				netif_carrier_on(netdev);
 | |
| 			} else {
 | |
| 				dev_info(&oct->pdev->dev, "netif_carrier_off\n");
 | |
| 				netif_carrier_off(netdev);
 | |
| 			}
 | |
| 		}
 | |
| 		break;
 | |
| 	default:
 | |
| 		pr_info("Unknown mbox req : %u\n", req->hdr.s.cmd);
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| void octep_ctrl_net_recv_fw_messages(struct octep_device *oct)
 | |
| {
 | |
| 	static u16 msg_sz = sizeof(union octep_ctrl_net_max_data);
 | |
| 	union octep_ctrl_net_max_data data = {};
 | |
| 	struct octep_ctrl_mbox_msg msg = {};
 | |
| 	int ret;
 | |
| 
 | |
| 	msg.hdr.s.sz = msg_sz;
 | |
| 	msg.sg_num = 1;
 | |
| 	msg.sg_list[0].sz = msg_sz;
 | |
| 	msg.sg_list[0].msg = &data;
 | |
| 	while (true) {
 | |
| 		/* mbox will overwrite msg.hdr.s.sz so initialize it */
 | |
| 		msg.hdr.s.sz = msg_sz;
 | |
| 		ret = octep_ctrl_mbox_recv(&oct->ctrl_mbox, (struct octep_ctrl_mbox_msg *)&msg);
 | |
| 		if (ret < 0)
 | |
| 			break;
 | |
| 
 | |
| 		if (msg.hdr.s.flags & OCTEP_CTRL_MBOX_MSG_HDR_FLAG_RESP)
 | |
| 			process_mbox_resp(oct, &msg);
 | |
| 		else if (msg.hdr.s.flags & OCTEP_CTRL_MBOX_MSG_HDR_FLAG_NOTIFY)
 | |
| 			process_mbox_notify(oct, &msg);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| int octep_ctrl_net_get_info(struct octep_device *oct, int vfid,
 | |
| 			    struct octep_fw_info *info)
 | |
| {
 | |
| 	struct octep_ctrl_net_wait_data d = {};
 | |
| 	struct octep_ctrl_net_h2f_resp *resp;
 | |
| 	struct octep_ctrl_net_h2f_req *req;
 | |
| 	int err;
 | |
| 
 | |
| 	req = &d.data.req;
 | |
| 	init_send_req(&d.msg, req, 0, vfid);
 | |
| 	req->hdr.s.cmd = OCTEP_CTRL_NET_H2F_CMD_GET_INFO;
 | |
| 	req->link_info.cmd = OCTEP_CTRL_NET_CMD_GET;
 | |
| 	err = octep_send_mbox_req(oct, &d, true);
 | |
| 	if (err < 0)
 | |
| 		return err;
 | |
| 
 | |
| 	resp = &d.data.resp;
 | |
| 	memcpy(info, &resp->info.fw_info, sizeof(struct octep_fw_info));
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int octep_ctrl_net_dev_remove(struct octep_device *oct, int vfid)
 | |
| {
 | |
| 	struct octep_ctrl_net_wait_data d = {};
 | |
| 	struct octep_ctrl_net_h2f_req *req;
 | |
| 
 | |
| 	req = &d.data.req;
 | |
| 	dev_dbg(&oct->pdev->dev, "Sending dev_unload msg to fw\n");
 | |
| 	init_send_req(&d.msg, req, sizeof(int), vfid);
 | |
| 	req->hdr.s.cmd = OCTEP_CTRL_NET_H2F_CMD_DEV_REMOVE;
 | |
| 
 | |
| 	return octep_send_mbox_req(oct, &d, false);
 | |
| }
 | |
| 
 | |
| int octep_ctrl_net_set_offloads(struct octep_device *oct, int vfid,
 | |
| 				struct octep_ctrl_net_offloads *offloads,
 | |
| 				bool wait_for_response)
 | |
| {
 | |
| 	struct octep_ctrl_net_wait_data d = {};
 | |
| 	struct octep_ctrl_net_h2f_req *req;
 | |
| 
 | |
| 	req = &d.data.req;
 | |
| 	init_send_req(&d.msg, req, offloads_sz, vfid);
 | |
| 	req->hdr.s.cmd = OCTEP_CTRL_NET_H2F_CMD_OFFLOADS;
 | |
| 	req->offloads.cmd = OCTEP_CTRL_NET_CMD_SET;
 | |
| 	req->offloads.offloads = *offloads;
 | |
| 
 | |
| 	return octep_send_mbox_req(oct, &d, wait_for_response);
 | |
| }
 | |
| 
 | |
| int octep_ctrl_net_uninit(struct octep_device *oct)
 | |
| {
 | |
| 	struct octep_ctrl_net_wait_data *pos, *n;
 | |
| 
 | |
| 	octep_ctrl_net_dev_remove(oct, OCTEP_CTRL_NET_INVALID_VFID);
 | |
| 
 | |
| 	list_for_each_entry_safe(pos, n, &oct->ctrl_req_wait_list, list)
 | |
| 		pos->done = 1;
 | |
| 
 | |
| 	wake_up_interruptible_all(&oct->ctrl_req_wait_q);
 | |
| 
 | |
| 	octep_ctrl_mbox_uninit(&oct->ctrl_mbox);
 | |
| 
 | |
| 	return 0;
 | |
| }
 |