15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
208a1cb0fSAndrzej Pietrasiewicz /* Target based USB-Gadget
308a1cb0fSAndrzej Pietrasiewicz  *
408a1cb0fSAndrzej Pietrasiewicz  * UAS protocol handling, target callbacks, configfs handling,
508a1cb0fSAndrzej Pietrasiewicz  * BBB (USB Mass Storage Class Bulk-Only (BBB) and Transport protocol handling.
608a1cb0fSAndrzej Pietrasiewicz  *
708a1cb0fSAndrzej Pietrasiewicz  * Author: Sebastian Andrzej Siewior <bigeasy at linutronix dot de>
808a1cb0fSAndrzej Pietrasiewicz  */
908a1cb0fSAndrzej Pietrasiewicz #include <linux/kernel.h>
1008a1cb0fSAndrzej Pietrasiewicz #include <linux/module.h>
1108a1cb0fSAndrzej Pietrasiewicz #include <linux/types.h>
1208a1cb0fSAndrzej Pietrasiewicz #include <linux/string.h>
1308a1cb0fSAndrzej Pietrasiewicz #include <linux/configfs.h>
1408a1cb0fSAndrzej Pietrasiewicz #include <linux/ctype.h>
1508a1cb0fSAndrzej Pietrasiewicz #include <linux/usb/ch9.h>
1608a1cb0fSAndrzej Pietrasiewicz #include <linux/usb/composite.h>
1708a1cb0fSAndrzej Pietrasiewicz #include <linux/usb/gadget.h>
1808a1cb0fSAndrzej Pietrasiewicz #include <linux/usb/storage.h>
1908a1cb0fSAndrzej Pietrasiewicz #include <scsi/scsi_tcq.h>
2008a1cb0fSAndrzej Pietrasiewicz #include <target/target_core_base.h>
2108a1cb0fSAndrzej Pietrasiewicz #include <target/target_core_fabric.h>
2208a1cb0fSAndrzej Pietrasiewicz #include <asm/unaligned.h>
2308a1cb0fSAndrzej Pietrasiewicz 
2408a1cb0fSAndrzej Pietrasiewicz #include "tcm.h"
25dc8c46a5SAndrzej Pietrasiewicz #include "u_tcm.h"
264bb8548dSAndrzej Pietrasiewicz #include "configfs.h"
27dc8c46a5SAndrzej Pietrasiewicz 
28dc8c46a5SAndrzej Pietrasiewicz #define TPG_INSTANCES		1
29dc8c46a5SAndrzej Pietrasiewicz 
30dc8c46a5SAndrzej Pietrasiewicz struct tpg_instance {
31dc8c46a5SAndrzej Pietrasiewicz 	struct usb_function_instance	*func_inst;
32dc8c46a5SAndrzej Pietrasiewicz 	struct usbg_tpg			*tpg;
33dc8c46a5SAndrzej Pietrasiewicz };
34dc8c46a5SAndrzej Pietrasiewicz 
35dc8c46a5SAndrzej Pietrasiewicz static struct tpg_instance tpg_instances[TPG_INSTANCES];
36dc8c46a5SAndrzej Pietrasiewicz 
37dc8c46a5SAndrzej Pietrasiewicz static DEFINE_MUTEX(tpg_instances_lock);
3808a1cb0fSAndrzej Pietrasiewicz 
to_f_uas(struct usb_function * f)3908a1cb0fSAndrzej Pietrasiewicz static inline struct f_uas *to_f_uas(struct usb_function *f)
4008a1cb0fSAndrzej Pietrasiewicz {
4108a1cb0fSAndrzej Pietrasiewicz 	return container_of(f, struct f_uas, function);
4208a1cb0fSAndrzej Pietrasiewicz }
4308a1cb0fSAndrzej Pietrasiewicz 
4408a1cb0fSAndrzej Pietrasiewicz /* Start bot.c code */
4508a1cb0fSAndrzej Pietrasiewicz 
bot_enqueue_cmd_cbw(struct f_uas * fu)4608a1cb0fSAndrzej Pietrasiewicz static int bot_enqueue_cmd_cbw(struct f_uas *fu)
4708a1cb0fSAndrzej Pietrasiewicz {
4808a1cb0fSAndrzej Pietrasiewicz 	int ret;
4908a1cb0fSAndrzej Pietrasiewicz 
5008a1cb0fSAndrzej Pietrasiewicz 	if (fu->flags & USBG_BOT_CMD_PEND)
5108a1cb0fSAndrzej Pietrasiewicz 		return 0;
5208a1cb0fSAndrzej Pietrasiewicz 
5308a1cb0fSAndrzej Pietrasiewicz 	ret = usb_ep_queue(fu->ep_out, fu->cmd.req, GFP_ATOMIC);
5408a1cb0fSAndrzej Pietrasiewicz 	if (!ret)
5508a1cb0fSAndrzej Pietrasiewicz 		fu->flags |= USBG_BOT_CMD_PEND;
5608a1cb0fSAndrzej Pietrasiewicz 	return ret;
5708a1cb0fSAndrzej Pietrasiewicz }
5808a1cb0fSAndrzej Pietrasiewicz 
bot_status_complete(struct usb_ep * ep,struct usb_request * req)5908a1cb0fSAndrzej Pietrasiewicz static void bot_status_complete(struct usb_ep *ep, struct usb_request *req)
6008a1cb0fSAndrzej Pietrasiewicz {
6108a1cb0fSAndrzej Pietrasiewicz 	struct usbg_cmd *cmd = req->context;
6208a1cb0fSAndrzej Pietrasiewicz 	struct f_uas *fu = cmd->fu;
6308a1cb0fSAndrzej Pietrasiewicz 
64cff834c1SNicholas Bellinger 	transport_generic_free_cmd(&cmd->se_cmd, 0);
6508a1cb0fSAndrzej Pietrasiewicz 	if (req->status < 0) {
6608a1cb0fSAndrzej Pietrasiewicz 		pr_err("ERR %s(%d)\n", __func__, __LINE__);
6708a1cb0fSAndrzej Pietrasiewicz 		return;
6808a1cb0fSAndrzej Pietrasiewicz 	}
6908a1cb0fSAndrzej Pietrasiewicz 
7008a1cb0fSAndrzej Pietrasiewicz 	/* CSW completed, wait for next CBW */
7108a1cb0fSAndrzej Pietrasiewicz 	bot_enqueue_cmd_cbw(fu);
7208a1cb0fSAndrzej Pietrasiewicz }
7308a1cb0fSAndrzej Pietrasiewicz 
bot_enqueue_sense_code(struct f_uas * fu,struct usbg_cmd * cmd)7408a1cb0fSAndrzej Pietrasiewicz static void bot_enqueue_sense_code(struct f_uas *fu, struct usbg_cmd *cmd)
7508a1cb0fSAndrzej Pietrasiewicz {
7608a1cb0fSAndrzej Pietrasiewicz 	struct bulk_cs_wrap *csw = &fu->bot_status.csw;
7708a1cb0fSAndrzej Pietrasiewicz 	int ret;
7808a1cb0fSAndrzej Pietrasiewicz 	unsigned int csw_stat;
7908a1cb0fSAndrzej Pietrasiewicz 
8008a1cb0fSAndrzej Pietrasiewicz 	csw_stat = cmd->csw_code;
8108a1cb0fSAndrzej Pietrasiewicz 	csw->Tag = cmd->bot_tag;
8208a1cb0fSAndrzej Pietrasiewicz 	csw->Status = csw_stat;
8308a1cb0fSAndrzej Pietrasiewicz 	fu->bot_status.req->context = cmd;
8408a1cb0fSAndrzej Pietrasiewicz 	ret = usb_ep_queue(fu->ep_in, fu->bot_status.req, GFP_ATOMIC);
8508a1cb0fSAndrzej Pietrasiewicz 	if (ret)
8608a1cb0fSAndrzej Pietrasiewicz 		pr_err("%s(%d) ERR: %d\n", __func__, __LINE__, ret);
8708a1cb0fSAndrzej Pietrasiewicz }
8808a1cb0fSAndrzej Pietrasiewicz 
bot_err_compl(struct usb_ep * ep,struct usb_request * req)8908a1cb0fSAndrzej Pietrasiewicz static void bot_err_compl(struct usb_ep *ep, struct usb_request *req)
9008a1cb0fSAndrzej Pietrasiewicz {
9108a1cb0fSAndrzej Pietrasiewicz 	struct usbg_cmd *cmd = req->context;
9208a1cb0fSAndrzej Pietrasiewicz 	struct f_uas *fu = cmd->fu;
9308a1cb0fSAndrzej Pietrasiewicz 
9408a1cb0fSAndrzej Pietrasiewicz 	if (req->status < 0)
9508a1cb0fSAndrzej Pietrasiewicz 		pr_err("ERR %s(%d)\n", __func__, __LINE__);
9608a1cb0fSAndrzej Pietrasiewicz 
9708a1cb0fSAndrzej Pietrasiewicz 	if (cmd->data_len) {
9808a1cb0fSAndrzej Pietrasiewicz 		if (cmd->data_len > ep->maxpacket) {
9908a1cb0fSAndrzej Pietrasiewicz 			req->length = ep->maxpacket;
10008a1cb0fSAndrzej Pietrasiewicz 			cmd->data_len -= ep->maxpacket;
10108a1cb0fSAndrzej Pietrasiewicz 		} else {
10208a1cb0fSAndrzej Pietrasiewicz 			req->length = cmd->data_len;
10308a1cb0fSAndrzej Pietrasiewicz 			cmd->data_len = 0;
10408a1cb0fSAndrzej Pietrasiewicz 		}
10508a1cb0fSAndrzej Pietrasiewicz 
10608a1cb0fSAndrzej Pietrasiewicz 		usb_ep_queue(ep, req, GFP_ATOMIC);
10708a1cb0fSAndrzej Pietrasiewicz 		return;
10808a1cb0fSAndrzej Pietrasiewicz 	}
10908a1cb0fSAndrzej Pietrasiewicz 	bot_enqueue_sense_code(fu, cmd);
11008a1cb0fSAndrzej Pietrasiewicz }
11108a1cb0fSAndrzej Pietrasiewicz 
bot_send_bad_status(struct usbg_cmd * cmd)11208a1cb0fSAndrzej Pietrasiewicz static void bot_send_bad_status(struct usbg_cmd *cmd)
11308a1cb0fSAndrzej Pietrasiewicz {
11408a1cb0fSAndrzej Pietrasiewicz 	struct f_uas *fu = cmd->fu;
11508a1cb0fSAndrzej Pietrasiewicz 	struct bulk_cs_wrap *csw = &fu->bot_status.csw;
11608a1cb0fSAndrzej Pietrasiewicz 	struct usb_request *req;
11708a1cb0fSAndrzej Pietrasiewicz 	struct usb_ep *ep;
11808a1cb0fSAndrzej Pietrasiewicz 
11908a1cb0fSAndrzej Pietrasiewicz 	csw->Residue = cpu_to_le32(cmd->data_len);
12008a1cb0fSAndrzej Pietrasiewicz 
12108a1cb0fSAndrzej Pietrasiewicz 	if (cmd->data_len) {
12208a1cb0fSAndrzej Pietrasiewicz 		if (cmd->is_read) {
12308a1cb0fSAndrzej Pietrasiewicz 			ep = fu->ep_in;
12408a1cb0fSAndrzej Pietrasiewicz 			req = fu->bot_req_in;
12508a1cb0fSAndrzej Pietrasiewicz 		} else {
12608a1cb0fSAndrzej Pietrasiewicz 			ep = fu->ep_out;
12708a1cb0fSAndrzej Pietrasiewicz 			req = fu->bot_req_out;
12808a1cb0fSAndrzej Pietrasiewicz 		}
12908a1cb0fSAndrzej Pietrasiewicz 
13008a1cb0fSAndrzej Pietrasiewicz 		if (cmd->data_len > fu->ep_in->maxpacket) {
13108a1cb0fSAndrzej Pietrasiewicz 			req->length = ep->maxpacket;
13208a1cb0fSAndrzej Pietrasiewicz 			cmd->data_len -= ep->maxpacket;
13308a1cb0fSAndrzej Pietrasiewicz 		} else {
13408a1cb0fSAndrzej Pietrasiewicz 			req->length = cmd->data_len;
13508a1cb0fSAndrzej Pietrasiewicz 			cmd->data_len = 0;
13608a1cb0fSAndrzej Pietrasiewicz 		}
13708a1cb0fSAndrzej Pietrasiewicz 		req->complete = bot_err_compl;
13808a1cb0fSAndrzej Pietrasiewicz 		req->context = cmd;
13908a1cb0fSAndrzej Pietrasiewicz 		req->buf = fu->cmd.buf;
14008a1cb0fSAndrzej Pietrasiewicz 		usb_ep_queue(ep, req, GFP_KERNEL);
14108a1cb0fSAndrzej Pietrasiewicz 	} else {
14208a1cb0fSAndrzej Pietrasiewicz 		bot_enqueue_sense_code(fu, cmd);
14308a1cb0fSAndrzej Pietrasiewicz 	}
14408a1cb0fSAndrzej Pietrasiewicz }
14508a1cb0fSAndrzej Pietrasiewicz 
bot_send_status(struct usbg_cmd * cmd,bool moved_data)14608a1cb0fSAndrzej Pietrasiewicz static int bot_send_status(struct usbg_cmd *cmd, bool moved_data)
14708a1cb0fSAndrzej Pietrasiewicz {
14808a1cb0fSAndrzej Pietrasiewicz 	struct f_uas *fu = cmd->fu;
14908a1cb0fSAndrzej Pietrasiewicz 	struct bulk_cs_wrap *csw = &fu->bot_status.csw;
15008a1cb0fSAndrzej Pietrasiewicz 	int ret;
15108a1cb0fSAndrzej Pietrasiewicz 
15208a1cb0fSAndrzej Pietrasiewicz 	if (cmd->se_cmd.scsi_status == SAM_STAT_GOOD) {
15308a1cb0fSAndrzej Pietrasiewicz 		if (!moved_data && cmd->data_len) {
15408a1cb0fSAndrzej Pietrasiewicz 			/*
15508a1cb0fSAndrzej Pietrasiewicz 			 * the host wants to move data, we don't. Fill / empty
15608a1cb0fSAndrzej Pietrasiewicz 			 * the pipe and then send the csw with reside set.
15708a1cb0fSAndrzej Pietrasiewicz 			 */
15808a1cb0fSAndrzej Pietrasiewicz 			cmd->csw_code = US_BULK_STAT_OK;
15908a1cb0fSAndrzej Pietrasiewicz 			bot_send_bad_status(cmd);
16008a1cb0fSAndrzej Pietrasiewicz 			return 0;
16108a1cb0fSAndrzej Pietrasiewicz 		}
16208a1cb0fSAndrzej Pietrasiewicz 
16308a1cb0fSAndrzej Pietrasiewicz 		csw->Tag = cmd->bot_tag;
16408a1cb0fSAndrzej Pietrasiewicz 		csw->Residue = cpu_to_le32(0);
16508a1cb0fSAndrzej Pietrasiewicz 		csw->Status = US_BULK_STAT_OK;
16608a1cb0fSAndrzej Pietrasiewicz 		fu->bot_status.req->context = cmd;
16708a1cb0fSAndrzej Pietrasiewicz 
16808a1cb0fSAndrzej Pietrasiewicz 		ret = usb_ep_queue(fu->ep_in, fu->bot_status.req, GFP_KERNEL);
16908a1cb0fSAndrzej Pietrasiewicz 		if (ret)
17008a1cb0fSAndrzej Pietrasiewicz 			pr_err("%s(%d) ERR: %d\n", __func__, __LINE__, ret);
17108a1cb0fSAndrzej Pietrasiewicz 	} else {
17208a1cb0fSAndrzej Pietrasiewicz 		cmd->csw_code = US_BULK_STAT_FAIL;
17308a1cb0fSAndrzej Pietrasiewicz 		bot_send_bad_status(cmd);
17408a1cb0fSAndrzej Pietrasiewicz 	}
17508a1cb0fSAndrzej Pietrasiewicz 	return 0;
17608a1cb0fSAndrzej Pietrasiewicz }
17708a1cb0fSAndrzej Pietrasiewicz 
17808a1cb0fSAndrzej Pietrasiewicz /*
17908a1cb0fSAndrzej Pietrasiewicz  * Called after command (no data transfer) or after the write (to device)
18008a1cb0fSAndrzej Pietrasiewicz  * operation is completed
18108a1cb0fSAndrzej Pietrasiewicz  */
bot_send_status_response(struct usbg_cmd * cmd)18208a1cb0fSAndrzej Pietrasiewicz static int bot_send_status_response(struct usbg_cmd *cmd)
18308a1cb0fSAndrzej Pietrasiewicz {
18408a1cb0fSAndrzej Pietrasiewicz 	bool moved_data = false;
18508a1cb0fSAndrzej Pietrasiewicz 
18608a1cb0fSAndrzej Pietrasiewicz 	if (!cmd->is_read)
18708a1cb0fSAndrzej Pietrasiewicz 		moved_data = true;
18808a1cb0fSAndrzej Pietrasiewicz 	return bot_send_status(cmd, moved_data);
18908a1cb0fSAndrzej Pietrasiewicz }
19008a1cb0fSAndrzej Pietrasiewicz 
19108a1cb0fSAndrzej Pietrasiewicz /* Read request completed, now we have to send the CSW */
bot_read_compl(struct usb_ep * ep,struct usb_request * req)19208a1cb0fSAndrzej Pietrasiewicz static void bot_read_compl(struct usb_ep *ep, struct usb_request *req)
19308a1cb0fSAndrzej Pietrasiewicz {
19408a1cb0fSAndrzej Pietrasiewicz 	struct usbg_cmd *cmd = req->context;
19508a1cb0fSAndrzej Pietrasiewicz 
19608a1cb0fSAndrzej Pietrasiewicz 	if (req->status < 0)
19708a1cb0fSAndrzej Pietrasiewicz 		pr_err("ERR %s(%d)\n", __func__, __LINE__);
19808a1cb0fSAndrzej Pietrasiewicz 
19908a1cb0fSAndrzej Pietrasiewicz 	bot_send_status(cmd, true);
20008a1cb0fSAndrzej Pietrasiewicz }
20108a1cb0fSAndrzej Pietrasiewicz 
bot_send_read_response(struct usbg_cmd * cmd)20208a1cb0fSAndrzej Pietrasiewicz static int bot_send_read_response(struct usbg_cmd *cmd)
20308a1cb0fSAndrzej Pietrasiewicz {
20408a1cb0fSAndrzej Pietrasiewicz 	struct f_uas *fu = cmd->fu;
20508a1cb0fSAndrzej Pietrasiewicz 	struct se_cmd *se_cmd = &cmd->se_cmd;
20608a1cb0fSAndrzej Pietrasiewicz 	struct usb_gadget *gadget = fuas_to_gadget(fu);
20708a1cb0fSAndrzej Pietrasiewicz 	int ret;
20808a1cb0fSAndrzej Pietrasiewicz 
20908a1cb0fSAndrzej Pietrasiewicz 	if (!cmd->data_len) {
21008a1cb0fSAndrzej Pietrasiewicz 		cmd->csw_code = US_BULK_STAT_PHASE;
21108a1cb0fSAndrzej Pietrasiewicz 		bot_send_bad_status(cmd);
21208a1cb0fSAndrzej Pietrasiewicz 		return 0;
21308a1cb0fSAndrzej Pietrasiewicz 	}
21408a1cb0fSAndrzej Pietrasiewicz 
21508a1cb0fSAndrzej Pietrasiewicz 	if (!gadget->sg_supported) {
21608a1cb0fSAndrzej Pietrasiewicz 		cmd->data_buf = kmalloc(se_cmd->data_length, GFP_ATOMIC);
21708a1cb0fSAndrzej Pietrasiewicz 		if (!cmd->data_buf)
21808a1cb0fSAndrzej Pietrasiewicz 			return -ENOMEM;
21908a1cb0fSAndrzej Pietrasiewicz 
22008a1cb0fSAndrzej Pietrasiewicz 		sg_copy_to_buffer(se_cmd->t_data_sg,
22108a1cb0fSAndrzej Pietrasiewicz 				se_cmd->t_data_nents,
22208a1cb0fSAndrzej Pietrasiewicz 				cmd->data_buf,
22308a1cb0fSAndrzej Pietrasiewicz 				se_cmd->data_length);
22408a1cb0fSAndrzej Pietrasiewicz 
22508a1cb0fSAndrzej Pietrasiewicz 		fu->bot_req_in->buf = cmd->data_buf;
22608a1cb0fSAndrzej Pietrasiewicz 	} else {
22708a1cb0fSAndrzej Pietrasiewicz 		fu->bot_req_in->buf = NULL;
22808a1cb0fSAndrzej Pietrasiewicz 		fu->bot_req_in->num_sgs = se_cmd->t_data_nents;
22908a1cb0fSAndrzej Pietrasiewicz 		fu->bot_req_in->sg = se_cmd->t_data_sg;
23008a1cb0fSAndrzej Pietrasiewicz 	}
23108a1cb0fSAndrzej Pietrasiewicz 
23208a1cb0fSAndrzej Pietrasiewicz 	fu->bot_req_in->complete = bot_read_compl;
23308a1cb0fSAndrzej Pietrasiewicz 	fu->bot_req_in->length = se_cmd->data_length;
23408a1cb0fSAndrzej Pietrasiewicz 	fu->bot_req_in->context = cmd;
23508a1cb0fSAndrzej Pietrasiewicz 	ret = usb_ep_queue(fu->ep_in, fu->bot_req_in, GFP_ATOMIC);
23608a1cb0fSAndrzej Pietrasiewicz 	if (ret)
23708a1cb0fSAndrzej Pietrasiewicz 		pr_err("%s(%d)\n", __func__, __LINE__);
23808a1cb0fSAndrzej Pietrasiewicz 	return 0;
23908a1cb0fSAndrzej Pietrasiewicz }
24008a1cb0fSAndrzej Pietrasiewicz 
24108a1cb0fSAndrzej Pietrasiewicz static void usbg_data_write_cmpl(struct usb_ep *, struct usb_request *);
24208a1cb0fSAndrzej Pietrasiewicz static int usbg_prepare_w_request(struct usbg_cmd *, struct usb_request *);
24308a1cb0fSAndrzej Pietrasiewicz 
bot_send_write_request(struct usbg_cmd * cmd)24408a1cb0fSAndrzej Pietrasiewicz static int bot_send_write_request(struct usbg_cmd *cmd)
24508a1cb0fSAndrzej Pietrasiewicz {
24608a1cb0fSAndrzej Pietrasiewicz 	struct f_uas *fu = cmd->fu;
24708a1cb0fSAndrzej Pietrasiewicz 	struct se_cmd *se_cmd = &cmd->se_cmd;
24808a1cb0fSAndrzej Pietrasiewicz 	struct usb_gadget *gadget = fuas_to_gadget(fu);
24908a1cb0fSAndrzej Pietrasiewicz 	int ret;
25008a1cb0fSAndrzej Pietrasiewicz 
25108a1cb0fSAndrzej Pietrasiewicz 	init_completion(&cmd->write_complete);
25208a1cb0fSAndrzej Pietrasiewicz 	cmd->fu = fu;
25308a1cb0fSAndrzej Pietrasiewicz 
25408a1cb0fSAndrzej Pietrasiewicz 	if (!cmd->data_len) {
25508a1cb0fSAndrzej Pietrasiewicz 		cmd->csw_code = US_BULK_STAT_PHASE;
25608a1cb0fSAndrzej Pietrasiewicz 		return -EINVAL;
25708a1cb0fSAndrzej Pietrasiewicz 	}
25808a1cb0fSAndrzej Pietrasiewicz 
25908a1cb0fSAndrzej Pietrasiewicz 	if (!gadget->sg_supported) {
26008a1cb0fSAndrzej Pietrasiewicz 		cmd->data_buf = kmalloc(se_cmd->data_length, GFP_KERNEL);
26108a1cb0fSAndrzej Pietrasiewicz 		if (!cmd->data_buf)
26208a1cb0fSAndrzej Pietrasiewicz 			return -ENOMEM;
26308a1cb0fSAndrzej Pietrasiewicz 
26408a1cb0fSAndrzej Pietrasiewicz 		fu->bot_req_out->buf = cmd->data_buf;
26508a1cb0fSAndrzej Pietrasiewicz 	} else {
26608a1cb0fSAndrzej Pietrasiewicz 		fu->bot_req_out->buf = NULL;
26708a1cb0fSAndrzej Pietrasiewicz 		fu->bot_req_out->num_sgs = se_cmd->t_data_nents;
26808a1cb0fSAndrzej Pietrasiewicz 		fu->bot_req_out->sg = se_cmd->t_data_sg;
26908a1cb0fSAndrzej Pietrasiewicz 	}
27008a1cb0fSAndrzej Pietrasiewicz 
27108a1cb0fSAndrzej Pietrasiewicz 	fu->bot_req_out->complete = usbg_data_write_cmpl;
27208a1cb0fSAndrzej Pietrasiewicz 	fu->bot_req_out->length = se_cmd->data_length;
27308a1cb0fSAndrzej Pietrasiewicz 	fu->bot_req_out->context = cmd;
27408a1cb0fSAndrzej Pietrasiewicz 
27508a1cb0fSAndrzej Pietrasiewicz 	ret = usbg_prepare_w_request(cmd, fu->bot_req_out);
27608a1cb0fSAndrzej Pietrasiewicz 	if (ret)
27708a1cb0fSAndrzej Pietrasiewicz 		goto cleanup;
27808a1cb0fSAndrzej Pietrasiewicz 	ret = usb_ep_queue(fu->ep_out, fu->bot_req_out, GFP_KERNEL);
27908a1cb0fSAndrzej Pietrasiewicz 	if (ret)
28008a1cb0fSAndrzej Pietrasiewicz 		pr_err("%s(%d)\n", __func__, __LINE__);
28108a1cb0fSAndrzej Pietrasiewicz 
28208a1cb0fSAndrzej Pietrasiewicz 	wait_for_completion(&cmd->write_complete);
28308a1cb0fSAndrzej Pietrasiewicz 	target_execute_cmd(se_cmd);
28408a1cb0fSAndrzej Pietrasiewicz cleanup:
28508a1cb0fSAndrzej Pietrasiewicz 	return ret;
28608a1cb0fSAndrzej Pietrasiewicz }
28708a1cb0fSAndrzej Pietrasiewicz 
28808a1cb0fSAndrzej Pietrasiewicz static int bot_submit_command(struct f_uas *, void *, unsigned int);
28908a1cb0fSAndrzej Pietrasiewicz 
bot_cmd_complete(struct usb_ep * ep,struct usb_request * req)29008a1cb0fSAndrzej Pietrasiewicz static void bot_cmd_complete(struct usb_ep *ep, struct usb_request *req)
29108a1cb0fSAndrzej Pietrasiewicz {
29208a1cb0fSAndrzej Pietrasiewicz 	struct f_uas *fu = req->context;
29308a1cb0fSAndrzej Pietrasiewicz 	int ret;
29408a1cb0fSAndrzej Pietrasiewicz 
29508a1cb0fSAndrzej Pietrasiewicz 	fu->flags &= ~USBG_BOT_CMD_PEND;
29608a1cb0fSAndrzej Pietrasiewicz 
29708a1cb0fSAndrzej Pietrasiewicz 	if (req->status < 0)
29808a1cb0fSAndrzej Pietrasiewicz 		return;
29908a1cb0fSAndrzej Pietrasiewicz 
30008a1cb0fSAndrzej Pietrasiewicz 	ret = bot_submit_command(fu, req->buf, req->actual);
30108a1cb0fSAndrzej Pietrasiewicz 	if (ret)
30208a1cb0fSAndrzej Pietrasiewicz 		pr_err("%s(%d): %d\n", __func__, __LINE__, ret);
30308a1cb0fSAndrzej Pietrasiewicz }
30408a1cb0fSAndrzej Pietrasiewicz 
bot_prepare_reqs(struct f_uas * fu)30508a1cb0fSAndrzej Pietrasiewicz static int bot_prepare_reqs(struct f_uas *fu)
30608a1cb0fSAndrzej Pietrasiewicz {
30708a1cb0fSAndrzej Pietrasiewicz 	int ret;
30808a1cb0fSAndrzej Pietrasiewicz 
30908a1cb0fSAndrzej Pietrasiewicz 	fu->bot_req_in = usb_ep_alloc_request(fu->ep_in, GFP_KERNEL);
31008a1cb0fSAndrzej Pietrasiewicz 	if (!fu->bot_req_in)
31108a1cb0fSAndrzej Pietrasiewicz 		goto err;
31208a1cb0fSAndrzej Pietrasiewicz 
31308a1cb0fSAndrzej Pietrasiewicz 	fu->bot_req_out = usb_ep_alloc_request(fu->ep_out, GFP_KERNEL);
31408a1cb0fSAndrzej Pietrasiewicz 	if (!fu->bot_req_out)
31508a1cb0fSAndrzej Pietrasiewicz 		goto err_out;
31608a1cb0fSAndrzej Pietrasiewicz 
31708a1cb0fSAndrzej Pietrasiewicz 	fu->cmd.req = usb_ep_alloc_request(fu->ep_out, GFP_KERNEL);
31808a1cb0fSAndrzej Pietrasiewicz 	if (!fu->cmd.req)
31908a1cb0fSAndrzej Pietrasiewicz 		goto err_cmd;
32008a1cb0fSAndrzej Pietrasiewicz 
32108a1cb0fSAndrzej Pietrasiewicz 	fu->bot_status.req = usb_ep_alloc_request(fu->ep_in, GFP_KERNEL);
32208a1cb0fSAndrzej Pietrasiewicz 	if (!fu->bot_status.req)
32308a1cb0fSAndrzej Pietrasiewicz 		goto err_sts;
32408a1cb0fSAndrzej Pietrasiewicz 
32508a1cb0fSAndrzej Pietrasiewicz 	fu->bot_status.req->buf = &fu->bot_status.csw;
32608a1cb0fSAndrzej Pietrasiewicz 	fu->bot_status.req->length = US_BULK_CS_WRAP_LEN;
32708a1cb0fSAndrzej Pietrasiewicz 	fu->bot_status.req->complete = bot_status_complete;
32808a1cb0fSAndrzej Pietrasiewicz 	fu->bot_status.csw.Signature = cpu_to_le32(US_BULK_CS_SIGN);
32908a1cb0fSAndrzej Pietrasiewicz 
33008a1cb0fSAndrzej Pietrasiewicz 	fu->cmd.buf = kmalloc(fu->ep_out->maxpacket, GFP_KERNEL);
33108a1cb0fSAndrzej Pietrasiewicz 	if (!fu->cmd.buf)
33208a1cb0fSAndrzej Pietrasiewicz 		goto err_buf;
33308a1cb0fSAndrzej Pietrasiewicz 
33408a1cb0fSAndrzej Pietrasiewicz 	fu->cmd.req->complete = bot_cmd_complete;
33508a1cb0fSAndrzej Pietrasiewicz 	fu->cmd.req->buf = fu->cmd.buf;
33608a1cb0fSAndrzej Pietrasiewicz 	fu->cmd.req->length = fu->ep_out->maxpacket;
33708a1cb0fSAndrzej Pietrasiewicz 	fu->cmd.req->context = fu;
33808a1cb0fSAndrzej Pietrasiewicz 
33908a1cb0fSAndrzej Pietrasiewicz 	ret = bot_enqueue_cmd_cbw(fu);
34008a1cb0fSAndrzej Pietrasiewicz 	if (ret)
34108a1cb0fSAndrzej Pietrasiewicz 		goto err_queue;
34208a1cb0fSAndrzej Pietrasiewicz 	return 0;
34308a1cb0fSAndrzej Pietrasiewicz err_queue:
34408a1cb0fSAndrzej Pietrasiewicz 	kfree(fu->cmd.buf);
34508a1cb0fSAndrzej Pietrasiewicz 	fu->cmd.buf = NULL;
34608a1cb0fSAndrzej Pietrasiewicz err_buf:
34708a1cb0fSAndrzej Pietrasiewicz 	usb_ep_free_request(fu->ep_in, fu->bot_status.req);
34808a1cb0fSAndrzej Pietrasiewicz err_sts:
34908a1cb0fSAndrzej Pietrasiewicz 	usb_ep_free_request(fu->ep_out, fu->cmd.req);
35008a1cb0fSAndrzej Pietrasiewicz 	fu->cmd.req = NULL;
35108a1cb0fSAndrzej Pietrasiewicz err_cmd:
35208a1cb0fSAndrzej Pietrasiewicz 	usb_ep_free_request(fu->ep_out, fu->bot_req_out);
35308a1cb0fSAndrzej Pietrasiewicz 	fu->bot_req_out = NULL;
35408a1cb0fSAndrzej Pietrasiewicz err_out:
35508a1cb0fSAndrzej Pietrasiewicz 	usb_ep_free_request(fu->ep_in, fu->bot_req_in);
35608a1cb0fSAndrzej Pietrasiewicz 	fu->bot_req_in = NULL;
35708a1cb0fSAndrzej Pietrasiewicz err:
35808a1cb0fSAndrzej Pietrasiewicz 	pr_err("BOT: endpoint setup failed\n");
35908a1cb0fSAndrzej Pietrasiewicz 	return -ENOMEM;
36008a1cb0fSAndrzej Pietrasiewicz }
36108a1cb0fSAndrzej Pietrasiewicz 
bot_cleanup_old_alt(struct f_uas * fu)36208a1cb0fSAndrzej Pietrasiewicz static void bot_cleanup_old_alt(struct f_uas *fu)
36308a1cb0fSAndrzej Pietrasiewicz {
36408a1cb0fSAndrzej Pietrasiewicz 	if (!(fu->flags & USBG_ENABLED))
36508a1cb0fSAndrzej Pietrasiewicz 		return;
36608a1cb0fSAndrzej Pietrasiewicz 
36708a1cb0fSAndrzej Pietrasiewicz 	usb_ep_disable(fu->ep_in);
36808a1cb0fSAndrzej Pietrasiewicz 	usb_ep_disable(fu->ep_out);
36908a1cb0fSAndrzej Pietrasiewicz 
37008a1cb0fSAndrzej Pietrasiewicz 	if (!fu->bot_req_in)
37108a1cb0fSAndrzej Pietrasiewicz 		return;
37208a1cb0fSAndrzej Pietrasiewicz 
37308a1cb0fSAndrzej Pietrasiewicz 	usb_ep_free_request(fu->ep_in, fu->bot_req_in);
37408a1cb0fSAndrzej Pietrasiewicz 	usb_ep_free_request(fu->ep_out, fu->bot_req_out);
37508a1cb0fSAndrzej Pietrasiewicz 	usb_ep_free_request(fu->ep_out, fu->cmd.req);
376afea03fcSManish Narani 	usb_ep_free_request(fu->ep_in, fu->bot_status.req);
37708a1cb0fSAndrzej Pietrasiewicz 
37808a1cb0fSAndrzej Pietrasiewicz 	kfree(fu->cmd.buf);
37908a1cb0fSAndrzej Pietrasiewicz 
38008a1cb0fSAndrzej Pietrasiewicz 	fu->bot_req_in = NULL;
38108a1cb0fSAndrzej Pietrasiewicz 	fu->bot_req_out = NULL;
38208a1cb0fSAndrzej Pietrasiewicz 	fu->cmd.req = NULL;
38308a1cb0fSAndrzej Pietrasiewicz 	fu->bot_status.req = NULL;
38408a1cb0fSAndrzej Pietrasiewicz 	fu->cmd.buf = NULL;
38508a1cb0fSAndrzej Pietrasiewicz }
38608a1cb0fSAndrzej Pietrasiewicz 
bot_set_alt(struct f_uas * fu)38708a1cb0fSAndrzej Pietrasiewicz static void bot_set_alt(struct f_uas *fu)
38808a1cb0fSAndrzej Pietrasiewicz {
38908a1cb0fSAndrzej Pietrasiewicz 	struct usb_function *f = &fu->function;
39008a1cb0fSAndrzej Pietrasiewicz 	struct usb_gadget *gadget = f->config->cdev->gadget;
39108a1cb0fSAndrzej Pietrasiewicz 	int ret;
39208a1cb0fSAndrzej Pietrasiewicz 
39308a1cb0fSAndrzej Pietrasiewicz 	fu->flags = USBG_IS_BOT;
39408a1cb0fSAndrzej Pietrasiewicz 
395864bc7e7SPawel Laszczak 	config_ep_by_speed_and_alt(gadget, f, fu->ep_in, USB_G_ALT_INT_BBB);
39608a1cb0fSAndrzej Pietrasiewicz 	ret = usb_ep_enable(fu->ep_in);
39708a1cb0fSAndrzej Pietrasiewicz 	if (ret)
39808a1cb0fSAndrzej Pietrasiewicz 		goto err_b_in;
39908a1cb0fSAndrzej Pietrasiewicz 
400864bc7e7SPawel Laszczak 	config_ep_by_speed_and_alt(gadget, f, fu->ep_out, USB_G_ALT_INT_BBB);
40108a1cb0fSAndrzej Pietrasiewicz 	ret = usb_ep_enable(fu->ep_out);
40208a1cb0fSAndrzej Pietrasiewicz 	if (ret)
40308a1cb0fSAndrzej Pietrasiewicz 		goto err_b_out;
40408a1cb0fSAndrzej Pietrasiewicz 
40508a1cb0fSAndrzej Pietrasiewicz 	ret = bot_prepare_reqs(fu);
40608a1cb0fSAndrzej Pietrasiewicz 	if (ret)
40708a1cb0fSAndrzej Pietrasiewicz 		goto err_wq;
40808a1cb0fSAndrzej Pietrasiewicz 	fu->flags |= USBG_ENABLED;
40908a1cb0fSAndrzej Pietrasiewicz 	pr_info("Using the BOT protocol\n");
41008a1cb0fSAndrzej Pietrasiewicz 	return;
41108a1cb0fSAndrzej Pietrasiewicz err_wq:
41208a1cb0fSAndrzej Pietrasiewicz 	usb_ep_disable(fu->ep_out);
41308a1cb0fSAndrzej Pietrasiewicz err_b_out:
41408a1cb0fSAndrzej Pietrasiewicz 	usb_ep_disable(fu->ep_in);
41508a1cb0fSAndrzej Pietrasiewicz err_b_in:
41608a1cb0fSAndrzej Pietrasiewicz 	fu->flags = USBG_IS_BOT;
41708a1cb0fSAndrzej Pietrasiewicz }
41808a1cb0fSAndrzej Pietrasiewicz 
usbg_bot_setup(struct usb_function * f,const struct usb_ctrlrequest * ctrl)41908a1cb0fSAndrzej Pietrasiewicz static int usbg_bot_setup(struct usb_function *f,
42008a1cb0fSAndrzej Pietrasiewicz 		const struct usb_ctrlrequest *ctrl)
42108a1cb0fSAndrzej Pietrasiewicz {
42208a1cb0fSAndrzej Pietrasiewicz 	struct f_uas *fu = to_f_uas(f);
42308a1cb0fSAndrzej Pietrasiewicz 	struct usb_composite_dev *cdev = f->config->cdev;
42408a1cb0fSAndrzej Pietrasiewicz 	u16 w_value = le16_to_cpu(ctrl->wValue);
42508a1cb0fSAndrzej Pietrasiewicz 	u16 w_length = le16_to_cpu(ctrl->wLength);
42608a1cb0fSAndrzej Pietrasiewicz 	int luns;
42708a1cb0fSAndrzej Pietrasiewicz 	u8 *ret_lun;
42808a1cb0fSAndrzej Pietrasiewicz 
42908a1cb0fSAndrzej Pietrasiewicz 	switch (ctrl->bRequest) {
43008a1cb0fSAndrzej Pietrasiewicz 	case US_BULK_GET_MAX_LUN:
43108a1cb0fSAndrzej Pietrasiewicz 		if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_CLASS |
43208a1cb0fSAndrzej Pietrasiewicz 					USB_RECIP_INTERFACE))
43308a1cb0fSAndrzej Pietrasiewicz 			return -ENOTSUPP;
43408a1cb0fSAndrzej Pietrasiewicz 
43508a1cb0fSAndrzej Pietrasiewicz 		if (w_length < 1)
43608a1cb0fSAndrzej Pietrasiewicz 			return -EINVAL;
43708a1cb0fSAndrzej Pietrasiewicz 		if (w_value != 0)
43808a1cb0fSAndrzej Pietrasiewicz 			return -EINVAL;
43908a1cb0fSAndrzej Pietrasiewicz 		luns = atomic_read(&fu->tpg->tpg_port_count);
44008a1cb0fSAndrzej Pietrasiewicz 		if (!luns) {
44108a1cb0fSAndrzej Pietrasiewicz 			pr_err("No LUNs configured?\n");
44208a1cb0fSAndrzej Pietrasiewicz 			return -EINVAL;
44308a1cb0fSAndrzej Pietrasiewicz 		}
44408a1cb0fSAndrzej Pietrasiewicz 		/*
44508a1cb0fSAndrzej Pietrasiewicz 		 * If 4 LUNs are present we return 3 i.e. LUN 0..3 can be
44608a1cb0fSAndrzej Pietrasiewicz 		 * accessed. The upper limit is 0xf
44708a1cb0fSAndrzej Pietrasiewicz 		 */
44808a1cb0fSAndrzej Pietrasiewicz 		luns--;
44908a1cb0fSAndrzej Pietrasiewicz 		if (luns > 0xf) {
45008a1cb0fSAndrzej Pietrasiewicz 			pr_info_once("Limiting the number of luns to 16\n");
45108a1cb0fSAndrzej Pietrasiewicz 			luns = 0xf;
45208a1cb0fSAndrzej Pietrasiewicz 		}
45308a1cb0fSAndrzej Pietrasiewicz 		ret_lun = cdev->req->buf;
45408a1cb0fSAndrzej Pietrasiewicz 		*ret_lun = luns;
45508a1cb0fSAndrzej Pietrasiewicz 		cdev->req->length = 1;
45608a1cb0fSAndrzej Pietrasiewicz 		return usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC);
45708a1cb0fSAndrzej Pietrasiewicz 
45808a1cb0fSAndrzej Pietrasiewicz 	case US_BULK_RESET_REQUEST:
45908a1cb0fSAndrzej Pietrasiewicz 		/* XXX maybe we should remove previous requests for IN + OUT */
46008a1cb0fSAndrzej Pietrasiewicz 		bot_enqueue_cmd_cbw(fu);
46108a1cb0fSAndrzej Pietrasiewicz 		return 0;
46208a1cb0fSAndrzej Pietrasiewicz 	}
46308a1cb0fSAndrzej Pietrasiewicz 	return -ENOTSUPP;
46408a1cb0fSAndrzej Pietrasiewicz }
46508a1cb0fSAndrzej Pietrasiewicz 
46608a1cb0fSAndrzej Pietrasiewicz /* Start uas.c code */
46708a1cb0fSAndrzej Pietrasiewicz 
uasp_cleanup_one_stream(struct f_uas * fu,struct uas_stream * stream)46808a1cb0fSAndrzej Pietrasiewicz static void uasp_cleanup_one_stream(struct f_uas *fu, struct uas_stream *stream)
46908a1cb0fSAndrzej Pietrasiewicz {
47008a1cb0fSAndrzej Pietrasiewicz 	/* We have either all three allocated or none */
47108a1cb0fSAndrzej Pietrasiewicz 	if (!stream->req_in)
47208a1cb0fSAndrzej Pietrasiewicz 		return;
47308a1cb0fSAndrzej Pietrasiewicz 
47408a1cb0fSAndrzej Pietrasiewicz 	usb_ep_free_request(fu->ep_in, stream->req_in);
47508a1cb0fSAndrzej Pietrasiewicz 	usb_ep_free_request(fu->ep_out, stream->req_out);
47608a1cb0fSAndrzej Pietrasiewicz 	usb_ep_free_request(fu->ep_status, stream->req_status);
47708a1cb0fSAndrzej Pietrasiewicz 
47808a1cb0fSAndrzej Pietrasiewicz 	stream->req_in = NULL;
47908a1cb0fSAndrzej Pietrasiewicz 	stream->req_out = NULL;
48008a1cb0fSAndrzej Pietrasiewicz 	stream->req_status = NULL;
48108a1cb0fSAndrzej Pietrasiewicz }
48208a1cb0fSAndrzej Pietrasiewicz 
uasp_free_cmdreq(struct f_uas * fu)48308a1cb0fSAndrzej Pietrasiewicz static void uasp_free_cmdreq(struct f_uas *fu)
48408a1cb0fSAndrzej Pietrasiewicz {
48508a1cb0fSAndrzej Pietrasiewicz 	usb_ep_free_request(fu->ep_cmd, fu->cmd.req);
48608a1cb0fSAndrzej Pietrasiewicz 	kfree(fu->cmd.buf);
48708a1cb0fSAndrzej Pietrasiewicz 	fu->cmd.req = NULL;
48808a1cb0fSAndrzej Pietrasiewicz 	fu->cmd.buf = NULL;
48908a1cb0fSAndrzej Pietrasiewicz }
49008a1cb0fSAndrzej Pietrasiewicz 
uasp_cleanup_old_alt(struct f_uas * fu)49108a1cb0fSAndrzej Pietrasiewicz static void uasp_cleanup_old_alt(struct f_uas *fu)
49208a1cb0fSAndrzej Pietrasiewicz {
49308a1cb0fSAndrzej Pietrasiewicz 	int i;
49408a1cb0fSAndrzej Pietrasiewicz 
49508a1cb0fSAndrzej Pietrasiewicz 	if (!(fu->flags & USBG_ENABLED))
49608a1cb0fSAndrzej Pietrasiewicz 		return;
49708a1cb0fSAndrzej Pietrasiewicz 
49808a1cb0fSAndrzej Pietrasiewicz 	usb_ep_disable(fu->ep_in);
49908a1cb0fSAndrzej Pietrasiewicz 	usb_ep_disable(fu->ep_out);
50008a1cb0fSAndrzej Pietrasiewicz 	usb_ep_disable(fu->ep_status);
50108a1cb0fSAndrzej Pietrasiewicz 	usb_ep_disable(fu->ep_cmd);
50208a1cb0fSAndrzej Pietrasiewicz 
50308a1cb0fSAndrzej Pietrasiewicz 	for (i = 0; i < UASP_SS_EP_COMP_NUM_STREAMS; i++)
50408a1cb0fSAndrzej Pietrasiewicz 		uasp_cleanup_one_stream(fu, &fu->stream[i]);
50508a1cb0fSAndrzej Pietrasiewicz 	uasp_free_cmdreq(fu);
50608a1cb0fSAndrzej Pietrasiewicz }
50708a1cb0fSAndrzej Pietrasiewicz 
50808a1cb0fSAndrzej Pietrasiewicz static void uasp_status_data_cmpl(struct usb_ep *ep, struct usb_request *req);
50908a1cb0fSAndrzej Pietrasiewicz 
uasp_prepare_r_request(struct usbg_cmd * cmd)51008a1cb0fSAndrzej Pietrasiewicz static int uasp_prepare_r_request(struct usbg_cmd *cmd)
51108a1cb0fSAndrzej Pietrasiewicz {
51208a1cb0fSAndrzej Pietrasiewicz 	struct se_cmd *se_cmd = &cmd->se_cmd;
51308a1cb0fSAndrzej Pietrasiewicz 	struct f_uas *fu = cmd->fu;
51408a1cb0fSAndrzej Pietrasiewicz 	struct usb_gadget *gadget = fuas_to_gadget(fu);
51508a1cb0fSAndrzej Pietrasiewicz 	struct uas_stream *stream = cmd->stream;
51608a1cb0fSAndrzej Pietrasiewicz 
51708a1cb0fSAndrzej Pietrasiewicz 	if (!gadget->sg_supported) {
51808a1cb0fSAndrzej Pietrasiewicz 		cmd->data_buf = kmalloc(se_cmd->data_length, GFP_ATOMIC);
51908a1cb0fSAndrzej Pietrasiewicz 		if (!cmd->data_buf)
52008a1cb0fSAndrzej Pietrasiewicz 			return -ENOMEM;
52108a1cb0fSAndrzej Pietrasiewicz 
52208a1cb0fSAndrzej Pietrasiewicz 		sg_copy_to_buffer(se_cmd->t_data_sg,
52308a1cb0fSAndrzej Pietrasiewicz 				se_cmd->t_data_nents,
52408a1cb0fSAndrzej Pietrasiewicz 				cmd->data_buf,
52508a1cb0fSAndrzej Pietrasiewicz 				se_cmd->data_length);
52608a1cb0fSAndrzej Pietrasiewicz 
52708a1cb0fSAndrzej Pietrasiewicz 		stream->req_in->buf = cmd->data_buf;
52808a1cb0fSAndrzej Pietrasiewicz 	} else {
52908a1cb0fSAndrzej Pietrasiewicz 		stream->req_in->buf = NULL;
53008a1cb0fSAndrzej Pietrasiewicz 		stream->req_in->num_sgs = se_cmd->t_data_nents;
53108a1cb0fSAndrzej Pietrasiewicz 		stream->req_in->sg = se_cmd->t_data_sg;
53208a1cb0fSAndrzej Pietrasiewicz 	}
53308a1cb0fSAndrzej Pietrasiewicz 
53427b31b91SThinh Nguyen 	stream->req_in->is_last = 1;
53508a1cb0fSAndrzej Pietrasiewicz 	stream->req_in->complete = uasp_status_data_cmpl;
53608a1cb0fSAndrzej Pietrasiewicz 	stream->req_in->length = se_cmd->data_length;
53708a1cb0fSAndrzej Pietrasiewicz 	stream->req_in->context = cmd;
53808a1cb0fSAndrzej Pietrasiewicz 
53908a1cb0fSAndrzej Pietrasiewicz 	cmd->state = UASP_SEND_STATUS;
54008a1cb0fSAndrzej Pietrasiewicz 	return 0;
54108a1cb0fSAndrzej Pietrasiewicz }
54208a1cb0fSAndrzej Pietrasiewicz 
uasp_prepare_status(struct usbg_cmd * cmd)54308a1cb0fSAndrzej Pietrasiewicz static void uasp_prepare_status(struct usbg_cmd *cmd)
54408a1cb0fSAndrzej Pietrasiewicz {
54508a1cb0fSAndrzej Pietrasiewicz 	struct se_cmd *se_cmd = &cmd->se_cmd;
54608a1cb0fSAndrzej Pietrasiewicz 	struct sense_iu *iu = &cmd->sense_iu;
54708a1cb0fSAndrzej Pietrasiewicz 	struct uas_stream *stream = cmd->stream;
54808a1cb0fSAndrzej Pietrasiewicz 
54908a1cb0fSAndrzej Pietrasiewicz 	cmd->state = UASP_QUEUE_COMMAND;
55008a1cb0fSAndrzej Pietrasiewicz 	iu->iu_id = IU_ID_STATUS;
55108a1cb0fSAndrzej Pietrasiewicz 	iu->tag = cpu_to_be16(cmd->tag);
55208a1cb0fSAndrzej Pietrasiewicz 
55308a1cb0fSAndrzej Pietrasiewicz 	/*
55408a1cb0fSAndrzej Pietrasiewicz 	 * iu->status_qual = cpu_to_be16(STATUS QUALIFIER SAM-4. Where R U?);
55508a1cb0fSAndrzej Pietrasiewicz 	 */
55608a1cb0fSAndrzej Pietrasiewicz 	iu->len = cpu_to_be16(se_cmd->scsi_sense_length);
55708a1cb0fSAndrzej Pietrasiewicz 	iu->status = se_cmd->scsi_status;
55827b31b91SThinh Nguyen 	stream->req_status->is_last = 1;
55908a1cb0fSAndrzej Pietrasiewicz 	stream->req_status->context = cmd;
56008a1cb0fSAndrzej Pietrasiewicz 	stream->req_status->length = se_cmd->scsi_sense_length + 16;
56108a1cb0fSAndrzej Pietrasiewicz 	stream->req_status->buf = iu;
56208a1cb0fSAndrzej Pietrasiewicz 	stream->req_status->complete = uasp_status_data_cmpl;
56308a1cb0fSAndrzej Pietrasiewicz }
56408a1cb0fSAndrzej Pietrasiewicz 
uasp_status_data_cmpl(struct usb_ep * ep,struct usb_request * req)56508a1cb0fSAndrzej Pietrasiewicz static void uasp_status_data_cmpl(struct usb_ep *ep, struct usb_request *req)
56608a1cb0fSAndrzej Pietrasiewicz {
56708a1cb0fSAndrzej Pietrasiewicz 	struct usbg_cmd *cmd = req->context;
56808a1cb0fSAndrzej Pietrasiewicz 	struct uas_stream *stream = cmd->stream;
56908a1cb0fSAndrzej Pietrasiewicz 	struct f_uas *fu = cmd->fu;
57008a1cb0fSAndrzej Pietrasiewicz 	int ret;
57108a1cb0fSAndrzej Pietrasiewicz 
57208a1cb0fSAndrzej Pietrasiewicz 	if (req->status < 0)
57308a1cb0fSAndrzej Pietrasiewicz 		goto cleanup;
57408a1cb0fSAndrzej Pietrasiewicz 
57508a1cb0fSAndrzej Pietrasiewicz 	switch (cmd->state) {
57608a1cb0fSAndrzej Pietrasiewicz 	case UASP_SEND_DATA:
57708a1cb0fSAndrzej Pietrasiewicz 		ret = uasp_prepare_r_request(cmd);
57808a1cb0fSAndrzej Pietrasiewicz 		if (ret)
57908a1cb0fSAndrzej Pietrasiewicz 			goto cleanup;
58008a1cb0fSAndrzej Pietrasiewicz 		ret = usb_ep_queue(fu->ep_in, stream->req_in, GFP_ATOMIC);
58108a1cb0fSAndrzej Pietrasiewicz 		if (ret)
58208a1cb0fSAndrzej Pietrasiewicz 			pr_err("%s(%d) => %d\n", __func__, __LINE__, ret);
58308a1cb0fSAndrzej Pietrasiewicz 		break;
58408a1cb0fSAndrzej Pietrasiewicz 
58508a1cb0fSAndrzej Pietrasiewicz 	case UASP_RECEIVE_DATA:
58608a1cb0fSAndrzej Pietrasiewicz 		ret = usbg_prepare_w_request(cmd, stream->req_out);
58708a1cb0fSAndrzej Pietrasiewicz 		if (ret)
58808a1cb0fSAndrzej Pietrasiewicz 			goto cleanup;
58908a1cb0fSAndrzej Pietrasiewicz 		ret = usb_ep_queue(fu->ep_out, stream->req_out, GFP_ATOMIC);
59008a1cb0fSAndrzej Pietrasiewicz 		if (ret)
59108a1cb0fSAndrzej Pietrasiewicz 			pr_err("%s(%d) => %d\n", __func__, __LINE__, ret);
59208a1cb0fSAndrzej Pietrasiewicz 		break;
59308a1cb0fSAndrzej Pietrasiewicz 
59408a1cb0fSAndrzej Pietrasiewicz 	case UASP_SEND_STATUS:
59508a1cb0fSAndrzej Pietrasiewicz 		uasp_prepare_status(cmd);
59608a1cb0fSAndrzej Pietrasiewicz 		ret = usb_ep_queue(fu->ep_status, stream->req_status,
59708a1cb0fSAndrzej Pietrasiewicz 				GFP_ATOMIC);
59808a1cb0fSAndrzej Pietrasiewicz 		if (ret)
59908a1cb0fSAndrzej Pietrasiewicz 			pr_err("%s(%d) => %d\n", __func__, __LINE__, ret);
60008a1cb0fSAndrzej Pietrasiewicz 		break;
60108a1cb0fSAndrzej Pietrasiewicz 
60208a1cb0fSAndrzej Pietrasiewicz 	case UASP_QUEUE_COMMAND:
603cff834c1SNicholas Bellinger 		transport_generic_free_cmd(&cmd->se_cmd, 0);
60408a1cb0fSAndrzej Pietrasiewicz 		usb_ep_queue(fu->ep_cmd, fu->cmd.req, GFP_ATOMIC);
60508a1cb0fSAndrzej Pietrasiewicz 		break;
60608a1cb0fSAndrzej Pietrasiewicz 
60708a1cb0fSAndrzej Pietrasiewicz 	default:
60808a1cb0fSAndrzej Pietrasiewicz 		BUG();
60908a1cb0fSAndrzej Pietrasiewicz 	}
61008a1cb0fSAndrzej Pietrasiewicz 	return;
61108a1cb0fSAndrzej Pietrasiewicz 
61208a1cb0fSAndrzej Pietrasiewicz cleanup:
613cff834c1SNicholas Bellinger 	transport_generic_free_cmd(&cmd->se_cmd, 0);
61408a1cb0fSAndrzej Pietrasiewicz }
61508a1cb0fSAndrzej Pietrasiewicz 
uasp_send_status_response(struct usbg_cmd * cmd)61608a1cb0fSAndrzej Pietrasiewicz static int uasp_send_status_response(struct usbg_cmd *cmd)
61708a1cb0fSAndrzej Pietrasiewicz {
61808a1cb0fSAndrzej Pietrasiewicz 	struct f_uas *fu = cmd->fu;
61908a1cb0fSAndrzej Pietrasiewicz 	struct uas_stream *stream = cmd->stream;
62008a1cb0fSAndrzej Pietrasiewicz 	struct sense_iu *iu = &cmd->sense_iu;
62108a1cb0fSAndrzej Pietrasiewicz 
62208a1cb0fSAndrzej Pietrasiewicz 	iu->tag = cpu_to_be16(cmd->tag);
62308a1cb0fSAndrzej Pietrasiewicz 	stream->req_status->complete = uasp_status_data_cmpl;
62408a1cb0fSAndrzej Pietrasiewicz 	stream->req_status->context = cmd;
62508a1cb0fSAndrzej Pietrasiewicz 	cmd->fu = fu;
62608a1cb0fSAndrzej Pietrasiewicz 	uasp_prepare_status(cmd);
62708a1cb0fSAndrzej Pietrasiewicz 	return usb_ep_queue(fu->ep_status, stream->req_status, GFP_ATOMIC);
62808a1cb0fSAndrzej Pietrasiewicz }
62908a1cb0fSAndrzej Pietrasiewicz 
uasp_send_read_response(struct usbg_cmd * cmd)63008a1cb0fSAndrzej Pietrasiewicz static int uasp_send_read_response(struct usbg_cmd *cmd)
63108a1cb0fSAndrzej Pietrasiewicz {
63208a1cb0fSAndrzej Pietrasiewicz 	struct f_uas *fu = cmd->fu;
63308a1cb0fSAndrzej Pietrasiewicz 	struct uas_stream *stream = cmd->stream;
63408a1cb0fSAndrzej Pietrasiewicz 	struct sense_iu *iu = &cmd->sense_iu;
63508a1cb0fSAndrzej Pietrasiewicz 	int ret;
63608a1cb0fSAndrzej Pietrasiewicz 
63708a1cb0fSAndrzej Pietrasiewicz 	cmd->fu = fu;
63808a1cb0fSAndrzej Pietrasiewicz 
63908a1cb0fSAndrzej Pietrasiewicz 	iu->tag = cpu_to_be16(cmd->tag);
64008a1cb0fSAndrzej Pietrasiewicz 	if (fu->flags & USBG_USE_STREAMS) {
64108a1cb0fSAndrzej Pietrasiewicz 
64208a1cb0fSAndrzej Pietrasiewicz 		ret = uasp_prepare_r_request(cmd);
64308a1cb0fSAndrzej Pietrasiewicz 		if (ret)
64408a1cb0fSAndrzej Pietrasiewicz 			goto out;
64508a1cb0fSAndrzej Pietrasiewicz 		ret = usb_ep_queue(fu->ep_in, stream->req_in, GFP_ATOMIC);
64608a1cb0fSAndrzej Pietrasiewicz 		if (ret) {
64708a1cb0fSAndrzej Pietrasiewicz 			pr_err("%s(%d) => %d\n", __func__, __LINE__, ret);
64808a1cb0fSAndrzej Pietrasiewicz 			kfree(cmd->data_buf);
64908a1cb0fSAndrzej Pietrasiewicz 			cmd->data_buf = NULL;
65008a1cb0fSAndrzej Pietrasiewicz 		}
65108a1cb0fSAndrzej Pietrasiewicz 
65208a1cb0fSAndrzej Pietrasiewicz 	} else {
65308a1cb0fSAndrzej Pietrasiewicz 
65408a1cb0fSAndrzej Pietrasiewicz 		iu->iu_id = IU_ID_READ_READY;
65508a1cb0fSAndrzej Pietrasiewicz 		iu->tag = cpu_to_be16(cmd->tag);
65608a1cb0fSAndrzej Pietrasiewicz 
65708a1cb0fSAndrzej Pietrasiewicz 		stream->req_status->complete = uasp_status_data_cmpl;
65808a1cb0fSAndrzej Pietrasiewicz 		stream->req_status->context = cmd;
65908a1cb0fSAndrzej Pietrasiewicz 
66008a1cb0fSAndrzej Pietrasiewicz 		cmd->state = UASP_SEND_DATA;
66108a1cb0fSAndrzej Pietrasiewicz 		stream->req_status->buf = iu;
66208a1cb0fSAndrzej Pietrasiewicz 		stream->req_status->length = sizeof(struct iu);
66308a1cb0fSAndrzej Pietrasiewicz 
66408a1cb0fSAndrzej Pietrasiewicz 		ret = usb_ep_queue(fu->ep_status, stream->req_status,
66508a1cb0fSAndrzej Pietrasiewicz 				GFP_ATOMIC);
66608a1cb0fSAndrzej Pietrasiewicz 		if (ret)
66708a1cb0fSAndrzej Pietrasiewicz 			pr_err("%s(%d) => %d\n", __func__, __LINE__, ret);
66808a1cb0fSAndrzej Pietrasiewicz 	}
66908a1cb0fSAndrzej Pietrasiewicz out:
67008a1cb0fSAndrzej Pietrasiewicz 	return ret;
67108a1cb0fSAndrzej Pietrasiewicz }
67208a1cb0fSAndrzej Pietrasiewicz 
uasp_send_write_request(struct usbg_cmd * cmd)67308a1cb0fSAndrzej Pietrasiewicz static int uasp_send_write_request(struct usbg_cmd *cmd)
67408a1cb0fSAndrzej Pietrasiewicz {
67508a1cb0fSAndrzej Pietrasiewicz 	struct f_uas *fu = cmd->fu;
67608a1cb0fSAndrzej Pietrasiewicz 	struct se_cmd *se_cmd = &cmd->se_cmd;
67708a1cb0fSAndrzej Pietrasiewicz 	struct uas_stream *stream = cmd->stream;
67808a1cb0fSAndrzej Pietrasiewicz 	struct sense_iu *iu = &cmd->sense_iu;
67908a1cb0fSAndrzej Pietrasiewicz 	int ret;
68008a1cb0fSAndrzej Pietrasiewicz 
68108a1cb0fSAndrzej Pietrasiewicz 	init_completion(&cmd->write_complete);
68208a1cb0fSAndrzej Pietrasiewicz 	cmd->fu = fu;
68308a1cb0fSAndrzej Pietrasiewicz 
68408a1cb0fSAndrzej Pietrasiewicz 	iu->tag = cpu_to_be16(cmd->tag);
68508a1cb0fSAndrzej Pietrasiewicz 
68608a1cb0fSAndrzej Pietrasiewicz 	if (fu->flags & USBG_USE_STREAMS) {
68708a1cb0fSAndrzej Pietrasiewicz 
68808a1cb0fSAndrzej Pietrasiewicz 		ret = usbg_prepare_w_request(cmd, stream->req_out);
68908a1cb0fSAndrzej Pietrasiewicz 		if (ret)
69008a1cb0fSAndrzej Pietrasiewicz 			goto cleanup;
69108a1cb0fSAndrzej Pietrasiewicz 		ret = usb_ep_queue(fu->ep_out, stream->req_out, GFP_ATOMIC);
69208a1cb0fSAndrzej Pietrasiewicz 		if (ret)
69308a1cb0fSAndrzej Pietrasiewicz 			pr_err("%s(%d)\n", __func__, __LINE__);
69408a1cb0fSAndrzej Pietrasiewicz 
69508a1cb0fSAndrzej Pietrasiewicz 	} else {
69608a1cb0fSAndrzej Pietrasiewicz 
69708a1cb0fSAndrzej Pietrasiewicz 		iu->iu_id = IU_ID_WRITE_READY;
69808a1cb0fSAndrzej Pietrasiewicz 		iu->tag = cpu_to_be16(cmd->tag);
69908a1cb0fSAndrzej Pietrasiewicz 
70008a1cb0fSAndrzej Pietrasiewicz 		stream->req_status->complete = uasp_status_data_cmpl;
70108a1cb0fSAndrzej Pietrasiewicz 		stream->req_status->context = cmd;
70208a1cb0fSAndrzej Pietrasiewicz 
70308a1cb0fSAndrzej Pietrasiewicz 		cmd->state = UASP_RECEIVE_DATA;
70408a1cb0fSAndrzej Pietrasiewicz 		stream->req_status->buf = iu;
70508a1cb0fSAndrzej Pietrasiewicz 		stream->req_status->length = sizeof(struct iu);
70608a1cb0fSAndrzej Pietrasiewicz 
70708a1cb0fSAndrzej Pietrasiewicz 		ret = usb_ep_queue(fu->ep_status, stream->req_status,
70808a1cb0fSAndrzej Pietrasiewicz 				GFP_ATOMIC);
70908a1cb0fSAndrzej Pietrasiewicz 		if (ret)
71008a1cb0fSAndrzej Pietrasiewicz 			pr_err("%s(%d)\n", __func__, __LINE__);
71108a1cb0fSAndrzej Pietrasiewicz 	}
71208a1cb0fSAndrzej Pietrasiewicz 
71308a1cb0fSAndrzej Pietrasiewicz 	wait_for_completion(&cmd->write_complete);
71408a1cb0fSAndrzej Pietrasiewicz 	target_execute_cmd(se_cmd);
71508a1cb0fSAndrzej Pietrasiewicz cleanup:
71608a1cb0fSAndrzej Pietrasiewicz 	return ret;
71708a1cb0fSAndrzej Pietrasiewicz }
71808a1cb0fSAndrzej Pietrasiewicz 
71908a1cb0fSAndrzej Pietrasiewicz static int usbg_submit_command(struct f_uas *, void *, unsigned int);
72008a1cb0fSAndrzej Pietrasiewicz 
uasp_cmd_complete(struct usb_ep * ep,struct usb_request * req)72108a1cb0fSAndrzej Pietrasiewicz static void uasp_cmd_complete(struct usb_ep *ep, struct usb_request *req)
72208a1cb0fSAndrzej Pietrasiewicz {
72308a1cb0fSAndrzej Pietrasiewicz 	struct f_uas *fu = req->context;
72408a1cb0fSAndrzej Pietrasiewicz 	int ret;
72508a1cb0fSAndrzej Pietrasiewicz 
72608a1cb0fSAndrzej Pietrasiewicz 	if (req->status < 0)
72708a1cb0fSAndrzej Pietrasiewicz 		return;
72808a1cb0fSAndrzej Pietrasiewicz 
72908a1cb0fSAndrzej Pietrasiewicz 	ret = usbg_submit_command(fu, req->buf, req->actual);
73008a1cb0fSAndrzej Pietrasiewicz 	/*
73108a1cb0fSAndrzej Pietrasiewicz 	 * Once we tune for performance enqueue the command req here again so
73208a1cb0fSAndrzej Pietrasiewicz 	 * we can receive a second command while we processing this one. Pay
73308a1cb0fSAndrzej Pietrasiewicz 	 * attention to properly sync STAUS endpoint with DATA IN + OUT so you
73408a1cb0fSAndrzej Pietrasiewicz 	 * don't break HS.
73508a1cb0fSAndrzej Pietrasiewicz 	 */
73608a1cb0fSAndrzej Pietrasiewicz 	if (!ret)
73708a1cb0fSAndrzej Pietrasiewicz 		return;
73808a1cb0fSAndrzej Pietrasiewicz 	usb_ep_queue(fu->ep_cmd, fu->cmd.req, GFP_ATOMIC);
73908a1cb0fSAndrzej Pietrasiewicz }
74008a1cb0fSAndrzej Pietrasiewicz 
uasp_alloc_stream_res(struct f_uas * fu,struct uas_stream * stream)74108a1cb0fSAndrzej Pietrasiewicz static int uasp_alloc_stream_res(struct f_uas *fu, struct uas_stream *stream)
74208a1cb0fSAndrzej Pietrasiewicz {
74308a1cb0fSAndrzej Pietrasiewicz 	stream->req_in = usb_ep_alloc_request(fu->ep_in, GFP_KERNEL);
74408a1cb0fSAndrzej Pietrasiewicz 	if (!stream->req_in)
74508a1cb0fSAndrzej Pietrasiewicz 		goto out;
74608a1cb0fSAndrzej Pietrasiewicz 
74708a1cb0fSAndrzej Pietrasiewicz 	stream->req_out = usb_ep_alloc_request(fu->ep_out, GFP_KERNEL);
74808a1cb0fSAndrzej Pietrasiewicz 	if (!stream->req_out)
74908a1cb0fSAndrzej Pietrasiewicz 		goto err_out;
75008a1cb0fSAndrzej Pietrasiewicz 
75108a1cb0fSAndrzej Pietrasiewicz 	stream->req_status = usb_ep_alloc_request(fu->ep_status, GFP_KERNEL);
75208a1cb0fSAndrzej Pietrasiewicz 	if (!stream->req_status)
75308a1cb0fSAndrzej Pietrasiewicz 		goto err_sts;
75408a1cb0fSAndrzej Pietrasiewicz 
75508a1cb0fSAndrzej Pietrasiewicz 	return 0;
75607c84341SChristophe JAILLET 
75708a1cb0fSAndrzej Pietrasiewicz err_sts:
75808a1cb0fSAndrzej Pietrasiewicz 	usb_ep_free_request(fu->ep_out, stream->req_out);
75908a1cb0fSAndrzej Pietrasiewicz 	stream->req_out = NULL;
76007c84341SChristophe JAILLET err_out:
76107c84341SChristophe JAILLET 	usb_ep_free_request(fu->ep_in, stream->req_in);
76207c84341SChristophe JAILLET 	stream->req_in = NULL;
76308a1cb0fSAndrzej Pietrasiewicz out:
76408a1cb0fSAndrzej Pietrasiewicz 	return -ENOMEM;
76508a1cb0fSAndrzej Pietrasiewicz }
76608a1cb0fSAndrzej Pietrasiewicz 
uasp_alloc_cmd(struct f_uas * fu)76708a1cb0fSAndrzej Pietrasiewicz static int uasp_alloc_cmd(struct f_uas *fu)
76808a1cb0fSAndrzej Pietrasiewicz {
76908a1cb0fSAndrzej Pietrasiewicz 	fu->cmd.req = usb_ep_alloc_request(fu->ep_cmd, GFP_KERNEL);
77008a1cb0fSAndrzej Pietrasiewicz 	if (!fu->cmd.req)
77108a1cb0fSAndrzej Pietrasiewicz 		goto err;
77208a1cb0fSAndrzej Pietrasiewicz 
77308a1cb0fSAndrzej Pietrasiewicz 	fu->cmd.buf = kmalloc(fu->ep_cmd->maxpacket, GFP_KERNEL);
77408a1cb0fSAndrzej Pietrasiewicz 	if (!fu->cmd.buf)
77508a1cb0fSAndrzej Pietrasiewicz 		goto err_buf;
77608a1cb0fSAndrzej Pietrasiewicz 
77708a1cb0fSAndrzej Pietrasiewicz 	fu->cmd.req->complete = uasp_cmd_complete;
77808a1cb0fSAndrzej Pietrasiewicz 	fu->cmd.req->buf = fu->cmd.buf;
77908a1cb0fSAndrzej Pietrasiewicz 	fu->cmd.req->length = fu->ep_cmd->maxpacket;
78008a1cb0fSAndrzej Pietrasiewicz 	fu->cmd.req->context = fu;
78108a1cb0fSAndrzej Pietrasiewicz 	return 0;
78208a1cb0fSAndrzej Pietrasiewicz 
78308a1cb0fSAndrzej Pietrasiewicz err_buf:
78408a1cb0fSAndrzej Pietrasiewicz 	usb_ep_free_request(fu->ep_cmd, fu->cmd.req);
78508a1cb0fSAndrzej Pietrasiewicz err:
78608a1cb0fSAndrzej Pietrasiewicz 	return -ENOMEM;
78708a1cb0fSAndrzej Pietrasiewicz }
78808a1cb0fSAndrzej Pietrasiewicz 
uasp_setup_stream_res(struct f_uas * fu,int max_streams)78908a1cb0fSAndrzej Pietrasiewicz static void uasp_setup_stream_res(struct f_uas *fu, int max_streams)
79008a1cb0fSAndrzej Pietrasiewicz {
79108a1cb0fSAndrzej Pietrasiewicz 	int i;
79208a1cb0fSAndrzej Pietrasiewicz 
79308a1cb0fSAndrzej Pietrasiewicz 	for (i = 0; i < max_streams; i++) {
79408a1cb0fSAndrzej Pietrasiewicz 		struct uas_stream *s = &fu->stream[i];
79508a1cb0fSAndrzej Pietrasiewicz 
79608a1cb0fSAndrzej Pietrasiewicz 		s->req_in->stream_id = i + 1;
79708a1cb0fSAndrzej Pietrasiewicz 		s->req_out->stream_id = i + 1;
79808a1cb0fSAndrzej Pietrasiewicz 		s->req_status->stream_id = i + 1;
79908a1cb0fSAndrzej Pietrasiewicz 	}
80008a1cb0fSAndrzej Pietrasiewicz }
80108a1cb0fSAndrzej Pietrasiewicz 
uasp_prepare_reqs(struct f_uas * fu)80208a1cb0fSAndrzej Pietrasiewicz static int uasp_prepare_reqs(struct f_uas *fu)
80308a1cb0fSAndrzej Pietrasiewicz {
80408a1cb0fSAndrzej Pietrasiewicz 	int ret;
80508a1cb0fSAndrzej Pietrasiewicz 	int i;
80608a1cb0fSAndrzej Pietrasiewicz 	int max_streams;
80708a1cb0fSAndrzej Pietrasiewicz 
80808a1cb0fSAndrzej Pietrasiewicz 	if (fu->flags & USBG_USE_STREAMS)
80908a1cb0fSAndrzej Pietrasiewicz 		max_streams = UASP_SS_EP_COMP_NUM_STREAMS;
81008a1cb0fSAndrzej Pietrasiewicz 	else
81108a1cb0fSAndrzej Pietrasiewicz 		max_streams = 1;
81208a1cb0fSAndrzej Pietrasiewicz 
81308a1cb0fSAndrzej Pietrasiewicz 	for (i = 0; i < max_streams; i++) {
81408a1cb0fSAndrzej Pietrasiewicz 		ret = uasp_alloc_stream_res(fu, &fu->stream[i]);
81508a1cb0fSAndrzej Pietrasiewicz 		if (ret)
81608a1cb0fSAndrzej Pietrasiewicz 			goto err_cleanup;
81708a1cb0fSAndrzej Pietrasiewicz 	}
81808a1cb0fSAndrzej Pietrasiewicz 
81908a1cb0fSAndrzej Pietrasiewicz 	ret = uasp_alloc_cmd(fu);
82008a1cb0fSAndrzej Pietrasiewicz 	if (ret)
82108a1cb0fSAndrzej Pietrasiewicz 		goto err_free_stream;
82208a1cb0fSAndrzej Pietrasiewicz 	uasp_setup_stream_res(fu, max_streams);
82308a1cb0fSAndrzej Pietrasiewicz 
82408a1cb0fSAndrzej Pietrasiewicz 	ret = usb_ep_queue(fu->ep_cmd, fu->cmd.req, GFP_ATOMIC);
82508a1cb0fSAndrzej Pietrasiewicz 	if (ret)
82608a1cb0fSAndrzej Pietrasiewicz 		goto err_free_stream;
82708a1cb0fSAndrzej Pietrasiewicz 
82808a1cb0fSAndrzej Pietrasiewicz 	return 0;
82908a1cb0fSAndrzej Pietrasiewicz 
83008a1cb0fSAndrzej Pietrasiewicz err_free_stream:
83108a1cb0fSAndrzej Pietrasiewicz 	uasp_free_cmdreq(fu);
83208a1cb0fSAndrzej Pietrasiewicz 
83308a1cb0fSAndrzej Pietrasiewicz err_cleanup:
83408a1cb0fSAndrzej Pietrasiewicz 	if (i) {
83508a1cb0fSAndrzej Pietrasiewicz 		do {
83608a1cb0fSAndrzej Pietrasiewicz 			uasp_cleanup_one_stream(fu, &fu->stream[i - 1]);
83708a1cb0fSAndrzej Pietrasiewicz 			i--;
83808a1cb0fSAndrzej Pietrasiewicz 		} while (i);
83908a1cb0fSAndrzej Pietrasiewicz 	}
84008a1cb0fSAndrzej Pietrasiewicz 	pr_err("UASP: endpoint setup failed\n");
84108a1cb0fSAndrzej Pietrasiewicz 	return ret;
84208a1cb0fSAndrzej Pietrasiewicz }
84308a1cb0fSAndrzej Pietrasiewicz 
uasp_set_alt(struct f_uas * fu)84408a1cb0fSAndrzej Pietrasiewicz static void uasp_set_alt(struct f_uas *fu)
84508a1cb0fSAndrzej Pietrasiewicz {
84608a1cb0fSAndrzej Pietrasiewicz 	struct usb_function *f = &fu->function;
84708a1cb0fSAndrzej Pietrasiewicz 	struct usb_gadget *gadget = f->config->cdev->gadget;
84808a1cb0fSAndrzej Pietrasiewicz 	int ret;
84908a1cb0fSAndrzej Pietrasiewicz 
85008a1cb0fSAndrzej Pietrasiewicz 	fu->flags = USBG_IS_UAS;
85108a1cb0fSAndrzej Pietrasiewicz 
8520b8b1a1fSJayshri Pawar 	if (gadget->speed >= USB_SPEED_SUPER)
85308a1cb0fSAndrzej Pietrasiewicz 		fu->flags |= USBG_USE_STREAMS;
85408a1cb0fSAndrzej Pietrasiewicz 
855864bc7e7SPawel Laszczak 	config_ep_by_speed_and_alt(gadget, f, fu->ep_in, USB_G_ALT_INT_UAS);
85608a1cb0fSAndrzej Pietrasiewicz 	ret = usb_ep_enable(fu->ep_in);
85708a1cb0fSAndrzej Pietrasiewicz 	if (ret)
85808a1cb0fSAndrzej Pietrasiewicz 		goto err_b_in;
85908a1cb0fSAndrzej Pietrasiewicz 
860864bc7e7SPawel Laszczak 	config_ep_by_speed_and_alt(gadget, f, fu->ep_out, USB_G_ALT_INT_UAS);
86108a1cb0fSAndrzej Pietrasiewicz 	ret = usb_ep_enable(fu->ep_out);
86208a1cb0fSAndrzej Pietrasiewicz 	if (ret)
86308a1cb0fSAndrzej Pietrasiewicz 		goto err_b_out;
86408a1cb0fSAndrzej Pietrasiewicz 
865864bc7e7SPawel Laszczak 	config_ep_by_speed_and_alt(gadget, f, fu->ep_cmd, USB_G_ALT_INT_UAS);
86608a1cb0fSAndrzej Pietrasiewicz 	ret = usb_ep_enable(fu->ep_cmd);
86708a1cb0fSAndrzej Pietrasiewicz 	if (ret)
86808a1cb0fSAndrzej Pietrasiewicz 		goto err_cmd;
869864bc7e7SPawel Laszczak 	config_ep_by_speed_and_alt(gadget, f, fu->ep_status, USB_G_ALT_INT_UAS);
87008a1cb0fSAndrzej Pietrasiewicz 	ret = usb_ep_enable(fu->ep_status);
87108a1cb0fSAndrzej Pietrasiewicz 	if (ret)
87208a1cb0fSAndrzej Pietrasiewicz 		goto err_status;
87308a1cb0fSAndrzej Pietrasiewicz 
87408a1cb0fSAndrzej Pietrasiewicz 	ret = uasp_prepare_reqs(fu);
87508a1cb0fSAndrzej Pietrasiewicz 	if (ret)
87608a1cb0fSAndrzej Pietrasiewicz 		goto err_wq;
87708a1cb0fSAndrzej Pietrasiewicz 	fu->flags |= USBG_ENABLED;
87808a1cb0fSAndrzej Pietrasiewicz 
87908a1cb0fSAndrzej Pietrasiewicz 	pr_info("Using the UAS protocol\n");
88008a1cb0fSAndrzej Pietrasiewicz 	return;
88108a1cb0fSAndrzej Pietrasiewicz err_wq:
88208a1cb0fSAndrzej Pietrasiewicz 	usb_ep_disable(fu->ep_status);
88308a1cb0fSAndrzej Pietrasiewicz err_status:
88408a1cb0fSAndrzej Pietrasiewicz 	usb_ep_disable(fu->ep_cmd);
88508a1cb0fSAndrzej Pietrasiewicz err_cmd:
88608a1cb0fSAndrzej Pietrasiewicz 	usb_ep_disable(fu->ep_out);
88708a1cb0fSAndrzej Pietrasiewicz err_b_out:
88808a1cb0fSAndrzej Pietrasiewicz 	usb_ep_disable(fu->ep_in);
88908a1cb0fSAndrzej Pietrasiewicz err_b_in:
89008a1cb0fSAndrzej Pietrasiewicz 	fu->flags = 0;
89108a1cb0fSAndrzej Pietrasiewicz }
89208a1cb0fSAndrzej Pietrasiewicz 
get_cmd_dir(const unsigned char * cdb)89308a1cb0fSAndrzej Pietrasiewicz static int get_cmd_dir(const unsigned char *cdb)
89408a1cb0fSAndrzej Pietrasiewicz {
89508a1cb0fSAndrzej Pietrasiewicz 	int ret;
89608a1cb0fSAndrzej Pietrasiewicz 
89708a1cb0fSAndrzej Pietrasiewicz 	switch (cdb[0]) {
89808a1cb0fSAndrzej Pietrasiewicz 	case READ_6:
89908a1cb0fSAndrzej Pietrasiewicz 	case READ_10:
90008a1cb0fSAndrzej Pietrasiewicz 	case READ_12:
90108a1cb0fSAndrzej Pietrasiewicz 	case READ_16:
90208a1cb0fSAndrzej Pietrasiewicz 	case INQUIRY:
90308a1cb0fSAndrzej Pietrasiewicz 	case MODE_SENSE:
90408a1cb0fSAndrzej Pietrasiewicz 	case MODE_SENSE_10:
90508a1cb0fSAndrzej Pietrasiewicz 	case SERVICE_ACTION_IN_16:
90608a1cb0fSAndrzej Pietrasiewicz 	case MAINTENANCE_IN:
90708a1cb0fSAndrzej Pietrasiewicz 	case PERSISTENT_RESERVE_IN:
90808a1cb0fSAndrzej Pietrasiewicz 	case SECURITY_PROTOCOL_IN:
90908a1cb0fSAndrzej Pietrasiewicz 	case ACCESS_CONTROL_IN:
91008a1cb0fSAndrzej Pietrasiewicz 	case REPORT_LUNS:
91108a1cb0fSAndrzej Pietrasiewicz 	case READ_BLOCK_LIMITS:
91208a1cb0fSAndrzej Pietrasiewicz 	case READ_POSITION:
91308a1cb0fSAndrzej Pietrasiewicz 	case READ_CAPACITY:
91408a1cb0fSAndrzej Pietrasiewicz 	case READ_TOC:
91508a1cb0fSAndrzej Pietrasiewicz 	case READ_FORMAT_CAPACITIES:
91608a1cb0fSAndrzej Pietrasiewicz 	case REQUEST_SENSE:
91708a1cb0fSAndrzej Pietrasiewicz 		ret = DMA_FROM_DEVICE;
91808a1cb0fSAndrzej Pietrasiewicz 		break;
91908a1cb0fSAndrzej Pietrasiewicz 
92008a1cb0fSAndrzej Pietrasiewicz 	case WRITE_6:
92108a1cb0fSAndrzej Pietrasiewicz 	case WRITE_10:
92208a1cb0fSAndrzej Pietrasiewicz 	case WRITE_12:
92308a1cb0fSAndrzej Pietrasiewicz 	case WRITE_16:
92408a1cb0fSAndrzej Pietrasiewicz 	case MODE_SELECT:
92508a1cb0fSAndrzej Pietrasiewicz 	case MODE_SELECT_10:
92608a1cb0fSAndrzej Pietrasiewicz 	case WRITE_VERIFY:
92708a1cb0fSAndrzej Pietrasiewicz 	case WRITE_VERIFY_12:
92808a1cb0fSAndrzej Pietrasiewicz 	case PERSISTENT_RESERVE_OUT:
92908a1cb0fSAndrzej Pietrasiewicz 	case MAINTENANCE_OUT:
93008a1cb0fSAndrzej Pietrasiewicz 	case SECURITY_PROTOCOL_OUT:
93108a1cb0fSAndrzej Pietrasiewicz 	case ACCESS_CONTROL_OUT:
93208a1cb0fSAndrzej Pietrasiewicz 		ret = DMA_TO_DEVICE;
93308a1cb0fSAndrzej Pietrasiewicz 		break;
93408a1cb0fSAndrzej Pietrasiewicz 	case ALLOW_MEDIUM_REMOVAL:
93508a1cb0fSAndrzej Pietrasiewicz 	case TEST_UNIT_READY:
93608a1cb0fSAndrzej Pietrasiewicz 	case SYNCHRONIZE_CACHE:
93708a1cb0fSAndrzej Pietrasiewicz 	case START_STOP:
93808a1cb0fSAndrzej Pietrasiewicz 	case ERASE:
93908a1cb0fSAndrzej Pietrasiewicz 	case REZERO_UNIT:
94008a1cb0fSAndrzej Pietrasiewicz 	case SEEK_10:
94108a1cb0fSAndrzej Pietrasiewicz 	case SPACE:
94208a1cb0fSAndrzej Pietrasiewicz 	case VERIFY:
94308a1cb0fSAndrzej Pietrasiewicz 	case WRITE_FILEMARKS:
94408a1cb0fSAndrzej Pietrasiewicz 		ret = DMA_NONE;
94508a1cb0fSAndrzej Pietrasiewicz 		break;
94608a1cb0fSAndrzej Pietrasiewicz 	default:
94708a1cb0fSAndrzej Pietrasiewicz #define CMD_DIR_MSG "target: Unknown data direction for SCSI Opcode 0x%02x\n"
94808a1cb0fSAndrzej Pietrasiewicz 		pr_warn(CMD_DIR_MSG, cdb[0]);
94908a1cb0fSAndrzej Pietrasiewicz #undef CMD_DIR_MSG
95008a1cb0fSAndrzej Pietrasiewicz 		ret = -EINVAL;
95108a1cb0fSAndrzej Pietrasiewicz 	}
95208a1cb0fSAndrzej Pietrasiewicz 	return ret;
95308a1cb0fSAndrzej Pietrasiewicz }
95408a1cb0fSAndrzej Pietrasiewicz 
usbg_data_write_cmpl(struct usb_ep * ep,struct usb_request * req)95508a1cb0fSAndrzej Pietrasiewicz static void usbg_data_write_cmpl(struct usb_ep *ep, struct usb_request *req)
95608a1cb0fSAndrzej Pietrasiewicz {
95708a1cb0fSAndrzej Pietrasiewicz 	struct usbg_cmd *cmd = req->context;
95808a1cb0fSAndrzej Pietrasiewicz 	struct se_cmd *se_cmd = &cmd->se_cmd;
95908a1cb0fSAndrzej Pietrasiewicz 
96008a1cb0fSAndrzej Pietrasiewicz 	if (req->status < 0) {
96108a1cb0fSAndrzej Pietrasiewicz 		pr_err("%s() state %d transfer failed\n", __func__, cmd->state);
96208a1cb0fSAndrzej Pietrasiewicz 		goto cleanup;
96308a1cb0fSAndrzej Pietrasiewicz 	}
96408a1cb0fSAndrzej Pietrasiewicz 
96508a1cb0fSAndrzej Pietrasiewicz 	if (req->num_sgs == 0) {
96608a1cb0fSAndrzej Pietrasiewicz 		sg_copy_from_buffer(se_cmd->t_data_sg,
96708a1cb0fSAndrzej Pietrasiewicz 				se_cmd->t_data_nents,
96808a1cb0fSAndrzej Pietrasiewicz 				cmd->data_buf,
96908a1cb0fSAndrzej Pietrasiewicz 				se_cmd->data_length);
97008a1cb0fSAndrzej Pietrasiewicz 	}
97108a1cb0fSAndrzej Pietrasiewicz 
97208a1cb0fSAndrzej Pietrasiewicz 	complete(&cmd->write_complete);
97308a1cb0fSAndrzej Pietrasiewicz 	return;
97408a1cb0fSAndrzej Pietrasiewicz 
97508a1cb0fSAndrzej Pietrasiewicz cleanup:
976cff834c1SNicholas Bellinger 	transport_generic_free_cmd(&cmd->se_cmd, 0);
97708a1cb0fSAndrzej Pietrasiewicz }
97808a1cb0fSAndrzej Pietrasiewicz 
usbg_prepare_w_request(struct usbg_cmd * cmd,struct usb_request * req)97908a1cb0fSAndrzej Pietrasiewicz static int usbg_prepare_w_request(struct usbg_cmd *cmd, struct usb_request *req)
98008a1cb0fSAndrzej Pietrasiewicz {
98108a1cb0fSAndrzej Pietrasiewicz 	struct se_cmd *se_cmd = &cmd->se_cmd;
98208a1cb0fSAndrzej Pietrasiewicz 	struct f_uas *fu = cmd->fu;
98308a1cb0fSAndrzej Pietrasiewicz 	struct usb_gadget *gadget = fuas_to_gadget(fu);
98408a1cb0fSAndrzej Pietrasiewicz 
98508a1cb0fSAndrzej Pietrasiewicz 	if (!gadget->sg_supported) {
98608a1cb0fSAndrzej Pietrasiewicz 		cmd->data_buf = kmalloc(se_cmd->data_length, GFP_ATOMIC);
98708a1cb0fSAndrzej Pietrasiewicz 		if (!cmd->data_buf)
98808a1cb0fSAndrzej Pietrasiewicz 			return -ENOMEM;
98908a1cb0fSAndrzej Pietrasiewicz 
99008a1cb0fSAndrzej Pietrasiewicz 		req->buf = cmd->data_buf;
99108a1cb0fSAndrzej Pietrasiewicz 	} else {
99208a1cb0fSAndrzej Pietrasiewicz 		req->buf = NULL;
99308a1cb0fSAndrzej Pietrasiewicz 		req->num_sgs = se_cmd->t_data_nents;
99408a1cb0fSAndrzej Pietrasiewicz 		req->sg = se_cmd->t_data_sg;
99508a1cb0fSAndrzej Pietrasiewicz 	}
99608a1cb0fSAndrzej Pietrasiewicz 
99727b31b91SThinh Nguyen 	req->is_last = 1;
99808a1cb0fSAndrzej Pietrasiewicz 	req->complete = usbg_data_write_cmpl;
99908a1cb0fSAndrzej Pietrasiewicz 	req->length = se_cmd->data_length;
100008a1cb0fSAndrzej Pietrasiewicz 	req->context = cmd;
100108a1cb0fSAndrzej Pietrasiewicz 	return 0;
100208a1cb0fSAndrzej Pietrasiewicz }
100308a1cb0fSAndrzej Pietrasiewicz 
usbg_send_status_response(struct se_cmd * se_cmd)100408a1cb0fSAndrzej Pietrasiewicz static int usbg_send_status_response(struct se_cmd *se_cmd)
100508a1cb0fSAndrzej Pietrasiewicz {
100608a1cb0fSAndrzej Pietrasiewicz 	struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd,
100708a1cb0fSAndrzej Pietrasiewicz 			se_cmd);
100808a1cb0fSAndrzej Pietrasiewicz 	struct f_uas *fu = cmd->fu;
100908a1cb0fSAndrzej Pietrasiewicz 
101008a1cb0fSAndrzej Pietrasiewicz 	if (fu->flags & USBG_IS_BOT)
101108a1cb0fSAndrzej Pietrasiewicz 		return bot_send_status_response(cmd);
101208a1cb0fSAndrzej Pietrasiewicz 	else
101308a1cb0fSAndrzej Pietrasiewicz 		return uasp_send_status_response(cmd);
101408a1cb0fSAndrzej Pietrasiewicz }
101508a1cb0fSAndrzej Pietrasiewicz 
usbg_send_write_request(struct se_cmd * se_cmd)101608a1cb0fSAndrzej Pietrasiewicz static int usbg_send_write_request(struct se_cmd *se_cmd)
101708a1cb0fSAndrzej Pietrasiewicz {
101808a1cb0fSAndrzej Pietrasiewicz 	struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd,
101908a1cb0fSAndrzej Pietrasiewicz 			se_cmd);
102008a1cb0fSAndrzej Pietrasiewicz 	struct f_uas *fu = cmd->fu;
102108a1cb0fSAndrzej Pietrasiewicz 
102208a1cb0fSAndrzej Pietrasiewicz 	if (fu->flags & USBG_IS_BOT)
102308a1cb0fSAndrzej Pietrasiewicz 		return bot_send_write_request(cmd);
102408a1cb0fSAndrzej Pietrasiewicz 	else
102508a1cb0fSAndrzej Pietrasiewicz 		return uasp_send_write_request(cmd);
102608a1cb0fSAndrzej Pietrasiewicz }
102708a1cb0fSAndrzej Pietrasiewicz 
usbg_send_read_response(struct se_cmd * se_cmd)102808a1cb0fSAndrzej Pietrasiewicz static int usbg_send_read_response(struct se_cmd *se_cmd)
102908a1cb0fSAndrzej Pietrasiewicz {
103008a1cb0fSAndrzej Pietrasiewicz 	struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd,
103108a1cb0fSAndrzej Pietrasiewicz 			se_cmd);
103208a1cb0fSAndrzej Pietrasiewicz 	struct f_uas *fu = cmd->fu;
103308a1cb0fSAndrzej Pietrasiewicz 
103408a1cb0fSAndrzej Pietrasiewicz 	if (fu->flags & USBG_IS_BOT)
103508a1cb0fSAndrzej Pietrasiewicz 		return bot_send_read_response(cmd);
103608a1cb0fSAndrzej Pietrasiewicz 	else
103708a1cb0fSAndrzej Pietrasiewicz 		return uasp_send_read_response(cmd);
103808a1cb0fSAndrzej Pietrasiewicz }
103908a1cb0fSAndrzej Pietrasiewicz 
usbg_cmd_work(struct work_struct * work)104008a1cb0fSAndrzej Pietrasiewicz static void usbg_cmd_work(struct work_struct *work)
104108a1cb0fSAndrzej Pietrasiewicz {
104208a1cb0fSAndrzej Pietrasiewicz 	struct usbg_cmd *cmd = container_of(work, struct usbg_cmd, work);
104308a1cb0fSAndrzej Pietrasiewicz 	struct se_cmd *se_cmd;
104408a1cb0fSAndrzej Pietrasiewicz 	struct tcm_usbg_nexus *tv_nexus;
104508a1cb0fSAndrzej Pietrasiewicz 	struct usbg_tpg *tpg;
1046cff834c1SNicholas Bellinger 	int dir, flags = (TARGET_SCF_UNKNOWN_SIZE | TARGET_SCF_ACK_KREF);
104708a1cb0fSAndrzej Pietrasiewicz 
104808a1cb0fSAndrzej Pietrasiewicz 	se_cmd = &cmd->se_cmd;
104908a1cb0fSAndrzej Pietrasiewicz 	tpg = cmd->fu->tpg;
105008a1cb0fSAndrzej Pietrasiewicz 	tv_nexus = tpg->tpg_nexus;
105108a1cb0fSAndrzej Pietrasiewicz 	dir = get_cmd_dir(cmd->cmd_buf);
105208a1cb0fSAndrzej Pietrasiewicz 	if (dir < 0) {
1053a78b7136SMike Christie 		__target_init_cmd(se_cmd,
105408a1cb0fSAndrzej Pietrasiewicz 				  tv_nexus->tvn_se_sess->se_tpg->se_tpg_tfo,
105508a1cb0fSAndrzej Pietrasiewicz 				  tv_nexus->tvn_se_sess, cmd->data_len, DMA_NONE,
1056a36840d8SSudhakar Panneerselvam 				  cmd->prio_attr, cmd->sense_iu.sense,
1057*8e288be8SMike Christie 				  cmd->unpacked_lun, NULL);
105808a1cb0fSAndrzej Pietrasiewicz 		goto out;
105908a1cb0fSAndrzej Pietrasiewicz 	}
106008a1cb0fSAndrzej Pietrasiewicz 
106112340930SMike Christie 	target_submit_cmd(se_cmd, tv_nexus->tvn_se_sess, cmd->cmd_buf,
1062cff834c1SNicholas Bellinger 			  cmd->sense_iu.sense, cmd->unpacked_lun, 0,
106312340930SMike Christie 			  cmd->prio_attr, dir, flags);
106408a1cb0fSAndrzej Pietrasiewicz 	return;
106508a1cb0fSAndrzej Pietrasiewicz 
106608a1cb0fSAndrzej Pietrasiewicz out:
106708a1cb0fSAndrzej Pietrasiewicz 	transport_send_check_condition_and_sense(se_cmd,
106808a1cb0fSAndrzej Pietrasiewicz 			TCM_UNSUPPORTED_SCSI_OPCODE, 1);
1069cff834c1SNicholas Bellinger 	transport_generic_free_cmd(&cmd->se_cmd, 0);
107008a1cb0fSAndrzej Pietrasiewicz }
107108a1cb0fSAndrzej Pietrasiewicz 
usbg_get_cmd(struct f_uas * fu,struct tcm_usbg_nexus * tv_nexus,u32 scsi_tag)107271e7ae8eSNicholas Bellinger static struct usbg_cmd *usbg_get_cmd(struct f_uas *fu,
107371e7ae8eSNicholas Bellinger 		struct tcm_usbg_nexus *tv_nexus, u32 scsi_tag)
107471e7ae8eSNicholas Bellinger {
107571e7ae8eSNicholas Bellinger 	struct se_session *se_sess = tv_nexus->tvn_se_sess;
107671e7ae8eSNicholas Bellinger 	struct usbg_cmd *cmd;
107710e9cbb6SMatthew Wilcox 	int tag, cpu;
107871e7ae8eSNicholas Bellinger 
107910e9cbb6SMatthew Wilcox 	tag = sbitmap_queue_get(&se_sess->sess_tag_pool, &cpu);
108071e7ae8eSNicholas Bellinger 	if (tag < 0)
108171e7ae8eSNicholas Bellinger 		return ERR_PTR(-ENOMEM);
108271e7ae8eSNicholas Bellinger 
108371e7ae8eSNicholas Bellinger 	cmd = &((struct usbg_cmd *)se_sess->sess_cmd_map)[tag];
108471e7ae8eSNicholas Bellinger 	memset(cmd, 0, sizeof(*cmd));
108571e7ae8eSNicholas Bellinger 	cmd->se_cmd.map_tag = tag;
108610e9cbb6SMatthew Wilcox 	cmd->se_cmd.map_cpu = cpu;
108771e7ae8eSNicholas Bellinger 	cmd->se_cmd.tag = cmd->tag = scsi_tag;
108871e7ae8eSNicholas Bellinger 	cmd->fu = fu;
108971e7ae8eSNicholas Bellinger 
109071e7ae8eSNicholas Bellinger 	return cmd;
109171e7ae8eSNicholas Bellinger }
109271e7ae8eSNicholas Bellinger 
109371e7ae8eSNicholas Bellinger static void usbg_release_cmd(struct se_cmd *);
109471e7ae8eSNicholas Bellinger 
usbg_submit_command(struct f_uas * fu,void * cmdbuf,unsigned int len)109508a1cb0fSAndrzej Pietrasiewicz static int usbg_submit_command(struct f_uas *fu,
109608a1cb0fSAndrzej Pietrasiewicz 		void *cmdbuf, unsigned int len)
109708a1cb0fSAndrzej Pietrasiewicz {
109808a1cb0fSAndrzej Pietrasiewicz 	struct command_iu *cmd_iu = cmdbuf;
109908a1cb0fSAndrzej Pietrasiewicz 	struct usbg_cmd *cmd;
110071e7ae8eSNicholas Bellinger 	struct usbg_tpg *tpg = fu->tpg;
1101a127f4f2SColin Ian King 	struct tcm_usbg_nexus *tv_nexus;
110208a1cb0fSAndrzej Pietrasiewicz 	u32 cmd_len;
110371e7ae8eSNicholas Bellinger 	u16 scsi_tag;
110408a1cb0fSAndrzej Pietrasiewicz 
110508a1cb0fSAndrzej Pietrasiewicz 	if (cmd_iu->iu_id != IU_ID_COMMAND) {
110608a1cb0fSAndrzej Pietrasiewicz 		pr_err("Unsupported type %d\n", cmd_iu->iu_id);
110708a1cb0fSAndrzej Pietrasiewicz 		return -EINVAL;
110808a1cb0fSAndrzej Pietrasiewicz 	}
110908a1cb0fSAndrzej Pietrasiewicz 
111071e7ae8eSNicholas Bellinger 	tv_nexus = tpg->tpg_nexus;
111171e7ae8eSNicholas Bellinger 	if (!tv_nexus) {
111271e7ae8eSNicholas Bellinger 		pr_err("Missing nexus, ignoring command\n");
111371e7ae8eSNicholas Bellinger 		return -EINVAL;
111471e7ae8eSNicholas Bellinger 	}
111508a1cb0fSAndrzej Pietrasiewicz 
111608a1cb0fSAndrzej Pietrasiewicz 	cmd_len = (cmd_iu->len & ~0x3) + 16;
111708a1cb0fSAndrzej Pietrasiewicz 	if (cmd_len > USBG_MAX_CMD)
111871e7ae8eSNicholas Bellinger 		return -EINVAL;
111908a1cb0fSAndrzej Pietrasiewicz 
112071e7ae8eSNicholas Bellinger 	scsi_tag = be16_to_cpup(&cmd_iu->tag);
112171e7ae8eSNicholas Bellinger 	cmd = usbg_get_cmd(fu, tv_nexus, scsi_tag);
112271e7ae8eSNicholas Bellinger 	if (IS_ERR(cmd)) {
112371e7ae8eSNicholas Bellinger 		pr_err("usbg_get_cmd failed\n");
112471e7ae8eSNicholas Bellinger 		return -ENOMEM;
112571e7ae8eSNicholas Bellinger 	}
112608a1cb0fSAndrzej Pietrasiewicz 	memcpy(cmd->cmd_buf, cmd_iu->cdb, cmd_len);
112708a1cb0fSAndrzej Pietrasiewicz 
112808a1cb0fSAndrzej Pietrasiewicz 	if (fu->flags & USBG_USE_STREAMS) {
112908a1cb0fSAndrzej Pietrasiewicz 		if (cmd->tag > UASP_SS_EP_COMP_NUM_STREAMS)
113008a1cb0fSAndrzej Pietrasiewicz 			goto err;
113108a1cb0fSAndrzej Pietrasiewicz 		if (!cmd->tag)
113208a1cb0fSAndrzej Pietrasiewicz 			cmd->stream = &fu->stream[0];
113308a1cb0fSAndrzej Pietrasiewicz 		else
113408a1cb0fSAndrzej Pietrasiewicz 			cmd->stream = &fu->stream[cmd->tag - 1];
113508a1cb0fSAndrzej Pietrasiewicz 	} else {
113608a1cb0fSAndrzej Pietrasiewicz 		cmd->stream = &fu->stream[0];
113708a1cb0fSAndrzej Pietrasiewicz 	}
113808a1cb0fSAndrzej Pietrasiewicz 
113908a1cb0fSAndrzej Pietrasiewicz 	switch (cmd_iu->prio_attr & 0x7) {
114008a1cb0fSAndrzej Pietrasiewicz 	case UAS_HEAD_TAG:
114108a1cb0fSAndrzej Pietrasiewicz 		cmd->prio_attr = TCM_HEAD_TAG;
114208a1cb0fSAndrzej Pietrasiewicz 		break;
114308a1cb0fSAndrzej Pietrasiewicz 	case UAS_ORDERED_TAG:
114408a1cb0fSAndrzej Pietrasiewicz 		cmd->prio_attr = TCM_ORDERED_TAG;
114508a1cb0fSAndrzej Pietrasiewicz 		break;
114608a1cb0fSAndrzej Pietrasiewicz 	case UAS_ACA:
114708a1cb0fSAndrzej Pietrasiewicz 		cmd->prio_attr = TCM_ACA_TAG;
114808a1cb0fSAndrzej Pietrasiewicz 		break;
114908a1cb0fSAndrzej Pietrasiewicz 	default:
115008a1cb0fSAndrzej Pietrasiewicz 		pr_debug_once("Unsupported prio_attr: %02x.\n",
115108a1cb0fSAndrzej Pietrasiewicz 				cmd_iu->prio_attr);
1152a74005abSGustavo A. R. Silva 		fallthrough;
115308a1cb0fSAndrzej Pietrasiewicz 	case UAS_SIMPLE_TAG:
115408a1cb0fSAndrzej Pietrasiewicz 		cmd->prio_attr = TCM_SIMPLE_TAG;
115508a1cb0fSAndrzej Pietrasiewicz 		break;
115608a1cb0fSAndrzej Pietrasiewicz 	}
115708a1cb0fSAndrzej Pietrasiewicz 
115808a1cb0fSAndrzej Pietrasiewicz 	cmd->unpacked_lun = scsilun_to_int(&cmd_iu->lun);
115908a1cb0fSAndrzej Pietrasiewicz 
116008a1cb0fSAndrzej Pietrasiewicz 	INIT_WORK(&cmd->work, usbg_cmd_work);
116108a1cb0fSAndrzej Pietrasiewicz 	queue_work(tpg->workqueue, &cmd->work);
116208a1cb0fSAndrzej Pietrasiewicz 
116308a1cb0fSAndrzej Pietrasiewicz 	return 0;
116408a1cb0fSAndrzej Pietrasiewicz err:
116571e7ae8eSNicholas Bellinger 	usbg_release_cmd(&cmd->se_cmd);
116608a1cb0fSAndrzej Pietrasiewicz 	return -EINVAL;
116708a1cb0fSAndrzej Pietrasiewicz }
116808a1cb0fSAndrzej Pietrasiewicz 
bot_cmd_work(struct work_struct * work)116908a1cb0fSAndrzej Pietrasiewicz static void bot_cmd_work(struct work_struct *work)
117008a1cb0fSAndrzej Pietrasiewicz {
117108a1cb0fSAndrzej Pietrasiewicz 	struct usbg_cmd *cmd = container_of(work, struct usbg_cmd, work);
117208a1cb0fSAndrzej Pietrasiewicz 	struct se_cmd *se_cmd;
117308a1cb0fSAndrzej Pietrasiewicz 	struct tcm_usbg_nexus *tv_nexus;
117408a1cb0fSAndrzej Pietrasiewicz 	struct usbg_tpg *tpg;
117508a1cb0fSAndrzej Pietrasiewicz 	int dir;
117608a1cb0fSAndrzej Pietrasiewicz 
117708a1cb0fSAndrzej Pietrasiewicz 	se_cmd = &cmd->se_cmd;
117808a1cb0fSAndrzej Pietrasiewicz 	tpg = cmd->fu->tpg;
117908a1cb0fSAndrzej Pietrasiewicz 	tv_nexus = tpg->tpg_nexus;
118008a1cb0fSAndrzej Pietrasiewicz 	dir = get_cmd_dir(cmd->cmd_buf);
118108a1cb0fSAndrzej Pietrasiewicz 	if (dir < 0) {
1182a78b7136SMike Christie 		__target_init_cmd(se_cmd,
118308a1cb0fSAndrzej Pietrasiewicz 				  tv_nexus->tvn_se_sess->se_tpg->se_tpg_tfo,
118408a1cb0fSAndrzej Pietrasiewicz 				  tv_nexus->tvn_se_sess, cmd->data_len, DMA_NONE,
1185a36840d8SSudhakar Panneerselvam 				  cmd->prio_attr, cmd->sense_iu.sense,
1186*8e288be8SMike Christie 				  cmd->unpacked_lun, NULL);
118708a1cb0fSAndrzej Pietrasiewicz 		goto out;
118808a1cb0fSAndrzej Pietrasiewicz 	}
118908a1cb0fSAndrzej Pietrasiewicz 
119012340930SMike Christie 	target_submit_cmd(se_cmd, tv_nexus->tvn_se_sess,
119108a1cb0fSAndrzej Pietrasiewicz 			  cmd->cmd_buf, cmd->sense_iu.sense, cmd->unpacked_lun,
119212340930SMike Christie 			  cmd->data_len, cmd->prio_attr, dir, 0);
119308a1cb0fSAndrzej Pietrasiewicz 	return;
119408a1cb0fSAndrzej Pietrasiewicz 
119508a1cb0fSAndrzej Pietrasiewicz out:
119608a1cb0fSAndrzej Pietrasiewicz 	transport_send_check_condition_and_sense(se_cmd,
119708a1cb0fSAndrzej Pietrasiewicz 				TCM_UNSUPPORTED_SCSI_OPCODE, 1);
1198cff834c1SNicholas Bellinger 	transport_generic_free_cmd(&cmd->se_cmd, 0);
119908a1cb0fSAndrzej Pietrasiewicz }
120008a1cb0fSAndrzej Pietrasiewicz 
bot_submit_command(struct f_uas * fu,void * cmdbuf,unsigned int len)120108a1cb0fSAndrzej Pietrasiewicz static int bot_submit_command(struct f_uas *fu,
120208a1cb0fSAndrzej Pietrasiewicz 		void *cmdbuf, unsigned int len)
120308a1cb0fSAndrzej Pietrasiewicz {
120408a1cb0fSAndrzej Pietrasiewicz 	struct bulk_cb_wrap *cbw = cmdbuf;
120508a1cb0fSAndrzej Pietrasiewicz 	struct usbg_cmd *cmd;
120671e7ae8eSNicholas Bellinger 	struct usbg_tpg *tpg = fu->tpg;
120708a1cb0fSAndrzej Pietrasiewicz 	struct tcm_usbg_nexus *tv_nexus;
120808a1cb0fSAndrzej Pietrasiewicz 	u32 cmd_len;
120908a1cb0fSAndrzej Pietrasiewicz 
121008a1cb0fSAndrzej Pietrasiewicz 	if (cbw->Signature != cpu_to_le32(US_BULK_CB_SIGN)) {
121108a1cb0fSAndrzej Pietrasiewicz 		pr_err("Wrong signature on CBW\n");
121208a1cb0fSAndrzej Pietrasiewicz 		return -EINVAL;
121308a1cb0fSAndrzej Pietrasiewicz 	}
121408a1cb0fSAndrzej Pietrasiewicz 	if (len != 31) {
121508a1cb0fSAndrzej Pietrasiewicz 		pr_err("Wrong length for CBW\n");
121608a1cb0fSAndrzej Pietrasiewicz 		return -EINVAL;
121708a1cb0fSAndrzej Pietrasiewicz 	}
121808a1cb0fSAndrzej Pietrasiewicz 
121908a1cb0fSAndrzej Pietrasiewicz 	cmd_len = cbw->Length;
122008a1cb0fSAndrzej Pietrasiewicz 	if (cmd_len < 1 || cmd_len > 16)
122108a1cb0fSAndrzej Pietrasiewicz 		return -EINVAL;
122208a1cb0fSAndrzej Pietrasiewicz 
122308a1cb0fSAndrzej Pietrasiewicz 	tv_nexus = tpg->tpg_nexus;
122408a1cb0fSAndrzej Pietrasiewicz 	if (!tv_nexus) {
122508a1cb0fSAndrzej Pietrasiewicz 		pr_err("Missing nexus, ignoring command\n");
122671e7ae8eSNicholas Bellinger 		return -ENODEV;
122708a1cb0fSAndrzej Pietrasiewicz 	}
122808a1cb0fSAndrzej Pietrasiewicz 
122971e7ae8eSNicholas Bellinger 	cmd = usbg_get_cmd(fu, tv_nexus, cbw->Tag);
123071e7ae8eSNicholas Bellinger 	if (IS_ERR(cmd)) {
123171e7ae8eSNicholas Bellinger 		pr_err("usbg_get_cmd failed\n");
123271e7ae8eSNicholas Bellinger 		return -ENOMEM;
123371e7ae8eSNicholas Bellinger 	}
123408a1cb0fSAndrzej Pietrasiewicz 	memcpy(cmd->cmd_buf, cbw->CDB, cmd_len);
123508a1cb0fSAndrzej Pietrasiewicz 
123608a1cb0fSAndrzej Pietrasiewicz 	cmd->bot_tag = cbw->Tag;
123708a1cb0fSAndrzej Pietrasiewicz 	cmd->prio_attr = TCM_SIMPLE_TAG;
123808a1cb0fSAndrzej Pietrasiewicz 	cmd->unpacked_lun = cbw->Lun;
123908a1cb0fSAndrzej Pietrasiewicz 	cmd->is_read = cbw->Flags & US_BULK_FLAG_IN ? 1 : 0;
124008a1cb0fSAndrzej Pietrasiewicz 	cmd->data_len = le32_to_cpu(cbw->DataTransferLength);
124108a1cb0fSAndrzej Pietrasiewicz 	cmd->se_cmd.tag = le32_to_cpu(cmd->bot_tag);
124208a1cb0fSAndrzej Pietrasiewicz 
124308a1cb0fSAndrzej Pietrasiewicz 	INIT_WORK(&cmd->work, bot_cmd_work);
124408a1cb0fSAndrzej Pietrasiewicz 	queue_work(tpg->workqueue, &cmd->work);
124508a1cb0fSAndrzej Pietrasiewicz 
124608a1cb0fSAndrzej Pietrasiewicz 	return 0;
124708a1cb0fSAndrzej Pietrasiewicz }
124808a1cb0fSAndrzej Pietrasiewicz 
124908a1cb0fSAndrzej Pietrasiewicz /* Start fabric.c code */
125008a1cb0fSAndrzej Pietrasiewicz 
usbg_check_true(struct se_portal_group * se_tpg)125108a1cb0fSAndrzej Pietrasiewicz static int usbg_check_true(struct se_portal_group *se_tpg)
125208a1cb0fSAndrzej Pietrasiewicz {
125308a1cb0fSAndrzej Pietrasiewicz 	return 1;
125408a1cb0fSAndrzej Pietrasiewicz }
125508a1cb0fSAndrzej Pietrasiewicz 
usbg_get_fabric_wwn(struct se_portal_group * se_tpg)125608a1cb0fSAndrzej Pietrasiewicz static char *usbg_get_fabric_wwn(struct se_portal_group *se_tpg)
125708a1cb0fSAndrzej Pietrasiewicz {
125808a1cb0fSAndrzej Pietrasiewicz 	struct usbg_tpg *tpg = container_of(se_tpg,
125908a1cb0fSAndrzej Pietrasiewicz 				struct usbg_tpg, se_tpg);
126008a1cb0fSAndrzej Pietrasiewicz 	struct usbg_tport *tport = tpg->tport;
126108a1cb0fSAndrzej Pietrasiewicz 
126208a1cb0fSAndrzej Pietrasiewicz 	return &tport->tport_name[0];
126308a1cb0fSAndrzej Pietrasiewicz }
126408a1cb0fSAndrzej Pietrasiewicz 
usbg_get_tag(struct se_portal_group * se_tpg)126508a1cb0fSAndrzej Pietrasiewicz static u16 usbg_get_tag(struct se_portal_group *se_tpg)
126608a1cb0fSAndrzej Pietrasiewicz {
126708a1cb0fSAndrzej Pietrasiewicz 	struct usbg_tpg *tpg = container_of(se_tpg,
126808a1cb0fSAndrzej Pietrasiewicz 				struct usbg_tpg, se_tpg);
126908a1cb0fSAndrzej Pietrasiewicz 	return tpg->tport_tpgt;
127008a1cb0fSAndrzej Pietrasiewicz }
127108a1cb0fSAndrzej Pietrasiewicz 
usbg_release_cmd(struct se_cmd * se_cmd)127208a1cb0fSAndrzej Pietrasiewicz static void usbg_release_cmd(struct se_cmd *se_cmd)
127308a1cb0fSAndrzej Pietrasiewicz {
127408a1cb0fSAndrzej Pietrasiewicz 	struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd,
127508a1cb0fSAndrzej Pietrasiewicz 			se_cmd);
127608a1cb0fSAndrzej Pietrasiewicz 	struct se_session *se_sess = se_cmd->se_sess;
127708a1cb0fSAndrzej Pietrasiewicz 
127808a1cb0fSAndrzej Pietrasiewicz 	kfree(cmd->data_buf);
127908a1cb0fSAndrzej Pietrasiewicz 	target_free_tag(se_sess, se_cmd);
128008a1cb0fSAndrzej Pietrasiewicz }
128108a1cb0fSAndrzej Pietrasiewicz 
usbg_queue_tm_rsp(struct se_cmd * se_cmd)128208a1cb0fSAndrzej Pietrasiewicz static void usbg_queue_tm_rsp(struct se_cmd *se_cmd)
128308a1cb0fSAndrzej Pietrasiewicz {
128408a1cb0fSAndrzej Pietrasiewicz }
128508a1cb0fSAndrzej Pietrasiewicz 
usbg_aborted_task(struct se_cmd * se_cmd)128671e7ae8eSNicholas Bellinger static void usbg_aborted_task(struct se_cmd *se_cmd)
128771e7ae8eSNicholas Bellinger {
128808a1cb0fSAndrzej Pietrasiewicz }
128983c2b54bSMatthew Wilcox 
usbg_check_wwn(const char * name)129008a1cb0fSAndrzej Pietrasiewicz static const char *usbg_check_wwn(const char *name)
129108a1cb0fSAndrzej Pietrasiewicz {
129208a1cb0fSAndrzej Pietrasiewicz 	const char *n;
129308a1cb0fSAndrzej Pietrasiewicz 	unsigned int len;
129408a1cb0fSAndrzej Pietrasiewicz 
129508a1cb0fSAndrzej Pietrasiewicz 	n = strstr(name, "naa.");
129608a1cb0fSAndrzej Pietrasiewicz 	if (!n)
129708a1cb0fSAndrzej Pietrasiewicz 		return NULL;
129808a1cb0fSAndrzej Pietrasiewicz 	n += 4;
129908a1cb0fSAndrzej Pietrasiewicz 	len = strlen(n);
130008a1cb0fSAndrzej Pietrasiewicz 	if (len == 0 || len > USBG_NAMELEN - 1)
130108a1cb0fSAndrzej Pietrasiewicz 		return NULL;
130208a1cb0fSAndrzej Pietrasiewicz 	return n;
130308a1cb0fSAndrzej Pietrasiewicz }
130408a1cb0fSAndrzej Pietrasiewicz 
usbg_init_nodeacl(struct se_node_acl * se_nacl,const char * name)130508a1cb0fSAndrzej Pietrasiewicz static int usbg_init_nodeacl(struct se_node_acl *se_nacl, const char *name)
130608a1cb0fSAndrzej Pietrasiewicz {
130708a1cb0fSAndrzej Pietrasiewicz 	if (!usbg_check_wwn(name))
130808a1cb0fSAndrzej Pietrasiewicz 		return -EINVAL;
130908a1cb0fSAndrzej Pietrasiewicz 	return 0;
131008a1cb0fSAndrzej Pietrasiewicz }
131108a1cb0fSAndrzej Pietrasiewicz 
usbg_make_tpg(struct se_wwn * wwn,const char * name)131208a1cb0fSAndrzej Pietrasiewicz static struct se_portal_group *usbg_make_tpg(struct se_wwn *wwn,
131308a1cb0fSAndrzej Pietrasiewicz 					     const char *name)
131408a1cb0fSAndrzej Pietrasiewicz {
131508a1cb0fSAndrzej Pietrasiewicz 	struct usbg_tport *tport = container_of(wwn, struct usbg_tport,
131608a1cb0fSAndrzej Pietrasiewicz 			tport_wwn);
131708a1cb0fSAndrzej Pietrasiewicz 	struct usbg_tpg *tpg;
131808a1cb0fSAndrzej Pietrasiewicz 	unsigned long tpgt;
131908a1cb0fSAndrzej Pietrasiewicz 	int ret;
132008a1cb0fSAndrzej Pietrasiewicz 	struct f_tcm_opts *opts;
132108a1cb0fSAndrzej Pietrasiewicz 	unsigned i;
132208a1cb0fSAndrzej Pietrasiewicz 
132308a1cb0fSAndrzej Pietrasiewicz 	if (strstr(name, "tpgt_") != name)
132408a1cb0fSAndrzej Pietrasiewicz 		return ERR_PTR(-EINVAL);
132508a1cb0fSAndrzej Pietrasiewicz 	if (kstrtoul(name + 5, 0, &tpgt) || tpgt > UINT_MAX)
132608a1cb0fSAndrzej Pietrasiewicz 		return ERR_PTR(-EINVAL);
132708a1cb0fSAndrzej Pietrasiewicz 	ret = -ENODEV;
132808a1cb0fSAndrzej Pietrasiewicz 	mutex_lock(&tpg_instances_lock);
132908a1cb0fSAndrzej Pietrasiewicz 	for (i = 0; i < TPG_INSTANCES; ++i)
133008a1cb0fSAndrzej Pietrasiewicz 		if (tpg_instances[i].func_inst && !tpg_instances[i].tpg)
133108a1cb0fSAndrzej Pietrasiewicz 			break;
133208a1cb0fSAndrzej Pietrasiewicz 	if (i == TPG_INSTANCES)
133308a1cb0fSAndrzej Pietrasiewicz 		goto unlock_inst;
133408a1cb0fSAndrzej Pietrasiewicz 
133508a1cb0fSAndrzej Pietrasiewicz 	opts = container_of(tpg_instances[i].func_inst, struct f_tcm_opts,
1336aa090eabSBart Van Assche 		func_inst);
133708a1cb0fSAndrzej Pietrasiewicz 	mutex_lock(&opts->dep_lock);
133808a1cb0fSAndrzej Pietrasiewicz 	if (!opts->ready)
133908a1cb0fSAndrzej Pietrasiewicz 		goto unlock_dep;
134008a1cb0fSAndrzej Pietrasiewicz 
134108a1cb0fSAndrzej Pietrasiewicz 	if (opts->has_dep) {
134208a1cb0fSAndrzej Pietrasiewicz 		if (!try_module_get(opts->dependent))
134308a1cb0fSAndrzej Pietrasiewicz 			goto unlock_dep;
1344dc8c46a5SAndrzej Pietrasiewicz 	} else {
1345dc8c46a5SAndrzej Pietrasiewicz 		ret = configfs_depend_item_unlocked(
134608a1cb0fSAndrzej Pietrasiewicz 			wwn->wwn_group.cg_subsys,
134708a1cb0fSAndrzej Pietrasiewicz 			&opts->func_inst.group.cg_item);
134808a1cb0fSAndrzej Pietrasiewicz 		if (ret)
134908a1cb0fSAndrzej Pietrasiewicz 			goto unlock_dep;
135008a1cb0fSAndrzej Pietrasiewicz 	}
1351dc8c46a5SAndrzej Pietrasiewicz 
1352dc8c46a5SAndrzej Pietrasiewicz 	tpg = kzalloc(sizeof(struct usbg_tpg), GFP_KERNEL);
1353dc8c46a5SAndrzej Pietrasiewicz 	ret = -ENOMEM;
1354dc8c46a5SAndrzej Pietrasiewicz 	if (!tpg)
1355dc8c46a5SAndrzej Pietrasiewicz 		goto unref_dep;
1356dc8c46a5SAndrzej Pietrasiewicz 	mutex_init(&tpg->tpg_mutex);
1357dc8c46a5SAndrzej Pietrasiewicz 	atomic_set(&tpg->tpg_port_count, 0);
1358dc8c46a5SAndrzej Pietrasiewicz 	tpg->workqueue = alloc_workqueue("tcm_usb_gadget", 0, 1);
1359dc8c46a5SAndrzej Pietrasiewicz 	if (!tpg->workqueue)
1360dc8c46a5SAndrzej Pietrasiewicz 		goto free_tpg;
1361dc8c46a5SAndrzej Pietrasiewicz 
1362dc8c46a5SAndrzej Pietrasiewicz 	tpg->tport = tport;
1363dc8c46a5SAndrzej Pietrasiewicz 	tpg->tport_tpgt = tpgt;
1364dc8c46a5SAndrzej Pietrasiewicz 
13654bb8548dSAndrzej Pietrasiewicz 	/*
13664bb8548dSAndrzej Pietrasiewicz 	 * SPC doesn't assign a protocol identifier for USB-SCSI, so we
1367dc8c46a5SAndrzej Pietrasiewicz 	 * pretend to be SAS..
13684bb8548dSAndrzej Pietrasiewicz 	 */
13694bb8548dSAndrzej Pietrasiewicz 	ret = core_tpg_register(wwn, &tpg->se_tpg, SCSI_PROTOCOL_SAS);
1370aa090eabSBart Van Assche 	if (ret < 0)
13714bb8548dSAndrzej Pietrasiewicz 		goto free_workqueue;
13724bb8548dSAndrzej Pietrasiewicz 
13734bb8548dSAndrzej Pietrasiewicz 	tpg_instances[i].tpg = tpg;
13744bb8548dSAndrzej Pietrasiewicz 	tpg->fi = tpg_instances[i].func_inst;
137508a1cb0fSAndrzej Pietrasiewicz 	mutex_unlock(&opts->dep_lock);
137608a1cb0fSAndrzej Pietrasiewicz 	mutex_unlock(&tpg_instances_lock);
1377dc8c46a5SAndrzej Pietrasiewicz 	return &tpg->se_tpg;
137808a1cb0fSAndrzej Pietrasiewicz 
1379dc8c46a5SAndrzej Pietrasiewicz free_workqueue:
138008a1cb0fSAndrzej Pietrasiewicz 	destroy_workqueue(tpg->workqueue);
138108a1cb0fSAndrzej Pietrasiewicz free_tpg:
138208a1cb0fSAndrzej Pietrasiewicz 	kfree(tpg);
1383e5587ea1SAndrzej Pietrasiewicz unref_dep:
1384dc8c46a5SAndrzej Pietrasiewicz 	if (opts->has_dep)
138508a1cb0fSAndrzej Pietrasiewicz 		module_put(opts->dependent);
138608a1cb0fSAndrzej Pietrasiewicz 	else
138708a1cb0fSAndrzej Pietrasiewicz 		configfs_undepend_item_unlocked(&opts->func_inst.group.cg_item);
138808a1cb0fSAndrzej Pietrasiewicz unlock_dep:
138908a1cb0fSAndrzej Pietrasiewicz 	mutex_unlock(&opts->dep_lock);
139008a1cb0fSAndrzej Pietrasiewicz unlock_inst:
139108a1cb0fSAndrzej Pietrasiewicz 	mutex_unlock(&tpg_instances_lock);
139208a1cb0fSAndrzej Pietrasiewicz 
139308a1cb0fSAndrzej Pietrasiewicz 	return ERR_PTR(ret);
1394e5587ea1SAndrzej Pietrasiewicz }
1395dc8c46a5SAndrzej Pietrasiewicz 
1396e5587ea1SAndrzej Pietrasiewicz static int tcm_usbg_drop_nexus(struct usbg_tpg *);
1397dc8c46a5SAndrzej Pietrasiewicz 
usbg_drop_tpg(struct se_portal_group * se_tpg)1398dc8c46a5SAndrzej Pietrasiewicz static void usbg_drop_tpg(struct se_portal_group *se_tpg)
1399dc8c46a5SAndrzej Pietrasiewicz {
1400dc8c46a5SAndrzej Pietrasiewicz 	struct usbg_tpg *tpg = container_of(se_tpg,
140108a1cb0fSAndrzej Pietrasiewicz 				struct usbg_tpg, se_tpg);
1402e5587ea1SAndrzej Pietrasiewicz 	unsigned i;
1403dc8c46a5SAndrzej Pietrasiewicz 	struct f_tcm_opts *opts;
1404dc8c46a5SAndrzej Pietrasiewicz 
1405dc8c46a5SAndrzej Pietrasiewicz 	tcm_usbg_drop_nexus(tpg);
1406dc8c46a5SAndrzej Pietrasiewicz 	core_tpg_deregister(se_tpg);
1407dc8c46a5SAndrzej Pietrasiewicz 	destroy_workqueue(tpg->workqueue);
14084bb8548dSAndrzej Pietrasiewicz 
1409dc8c46a5SAndrzej Pietrasiewicz 	mutex_lock(&tpg_instances_lock);
14104bb8548dSAndrzej Pietrasiewicz 	for (i = 0; i < TPG_INSTANCES; ++i)
14114bb8548dSAndrzej Pietrasiewicz 		if (tpg_instances[i].tpg == tpg)
1412dc8c46a5SAndrzej Pietrasiewicz 			break;
1413dc8c46a5SAndrzej Pietrasiewicz 	if (i < TPG_INSTANCES) {
1414dc8c46a5SAndrzej Pietrasiewicz 		tpg_instances[i].tpg = NULL;
1415dc8c46a5SAndrzej Pietrasiewicz 		opts = container_of(tpg_instances[i].func_inst,
1416dc8c46a5SAndrzej Pietrasiewicz 			struct f_tcm_opts, func_inst);
1417dc8c46a5SAndrzej Pietrasiewicz 		mutex_lock(&opts->dep_lock);
141808a1cb0fSAndrzej Pietrasiewicz 		if (opts->has_dep)
141908a1cb0fSAndrzej Pietrasiewicz 			module_put(opts->dependent);
142008a1cb0fSAndrzej Pietrasiewicz 		else
142108a1cb0fSAndrzej Pietrasiewicz 			configfs_undepend_item_unlocked(
142208a1cb0fSAndrzej Pietrasiewicz 				&opts->func_inst.group.cg_item);
142308a1cb0fSAndrzej Pietrasiewicz 		mutex_unlock(&opts->dep_lock);
142408a1cb0fSAndrzej Pietrasiewicz 	}
142508a1cb0fSAndrzej Pietrasiewicz 	mutex_unlock(&tpg_instances_lock);
1426dc8c46a5SAndrzej Pietrasiewicz 
1427dc8c46a5SAndrzej Pietrasiewicz 	kfree(tpg);
142808a1cb0fSAndrzej Pietrasiewicz }
142908a1cb0fSAndrzej Pietrasiewicz 
usbg_make_tport(struct target_fabric_configfs * tf,struct config_group * group,const char * name)143008a1cb0fSAndrzej Pietrasiewicz static struct se_wwn *usbg_make_tport(
143108a1cb0fSAndrzej Pietrasiewicz 	struct target_fabric_configfs *tf,
1432dc8c46a5SAndrzej Pietrasiewicz 	struct config_group *group,
1433dc8c46a5SAndrzej Pietrasiewicz 	const char *name)
1434dc8c46a5SAndrzej Pietrasiewicz {
1435dc8c46a5SAndrzej Pietrasiewicz 	struct usbg_tport *tport;
1436dc8c46a5SAndrzej Pietrasiewicz 	const char *wnn_name;
1437e877b729SHeinrich Schuchardt 	u64 wwpn = 0;
1438dc8c46a5SAndrzej Pietrasiewicz 
1439dc8c46a5SAndrzej Pietrasiewicz 	wnn_name = usbg_check_wwn(name);
1440dc8c46a5SAndrzej Pietrasiewicz 	if (!wnn_name)
1441dc8c46a5SAndrzej Pietrasiewicz 		return ERR_PTR(-EINVAL);
14424bb8548dSAndrzej Pietrasiewicz 
1443dc8c46a5SAndrzej Pietrasiewicz 	tport = kzalloc(sizeof(struct usbg_tport), GFP_KERNEL);
14444bb8548dSAndrzej Pietrasiewicz 	if (!(tport))
1445e877b729SHeinrich Schuchardt 		return ERR_PTR(-ENOMEM);
1446e877b729SHeinrich Schuchardt 
1447dc8c46a5SAndrzej Pietrasiewicz 	tport->tport_wwpn = wwpn;
1448e877b729SHeinrich Schuchardt 	snprintf(tport->tport_name, sizeof(tport->tport_name), "%s", wnn_name);
1449dc8c46a5SAndrzej Pietrasiewicz 	return &tport->tport_wwn;
1450e5587ea1SAndrzej Pietrasiewicz }
145108a1cb0fSAndrzej Pietrasiewicz 
usbg_drop_tport(struct se_wwn * wwn)145208a1cb0fSAndrzej Pietrasiewicz static void usbg_drop_tport(struct se_wwn *wwn)
145308a1cb0fSAndrzej Pietrasiewicz {
145408a1cb0fSAndrzej Pietrasiewicz 	struct usbg_tport *tport = container_of(wwn,
145508a1cb0fSAndrzej Pietrasiewicz 				struct usbg_tport, tport_wwn);
145608a1cb0fSAndrzej Pietrasiewicz 	kfree(tport);
145708a1cb0fSAndrzej Pietrasiewicz }
145808a1cb0fSAndrzej Pietrasiewicz 
145908a1cb0fSAndrzej Pietrasiewicz /*
146008a1cb0fSAndrzej Pietrasiewicz  * If somebody feels like dropping the version property, go ahead.
146108a1cb0fSAndrzej Pietrasiewicz  */
usbg_wwn_version_show(struct config_item * item,char * page)146208a1cb0fSAndrzej Pietrasiewicz static ssize_t usbg_wwn_version_show(struct config_item *item,  char *page)
146308a1cb0fSAndrzej Pietrasiewicz {
146408a1cb0fSAndrzej Pietrasiewicz 	return sprintf(page, "usb-gadget fabric module\n");
146508a1cb0fSAndrzej Pietrasiewicz }
146608a1cb0fSAndrzej Pietrasiewicz 
146708a1cb0fSAndrzej Pietrasiewicz CONFIGFS_ATTR_RO(usbg_wwn_, version);
146808a1cb0fSAndrzej Pietrasiewicz 
146908a1cb0fSAndrzej Pietrasiewicz static struct configfs_attribute *usbg_wwn_attrs[] = {
1470dc8c46a5SAndrzej Pietrasiewicz 	&usbg_wwn_attr_version,
147108a1cb0fSAndrzej Pietrasiewicz 	NULL,
147208a1cb0fSAndrzej Pietrasiewicz };
147308a1cb0fSAndrzej Pietrasiewicz 
147408a1cb0fSAndrzej Pietrasiewicz static int usbg_attach(struct usbg_tpg *);
147508a1cb0fSAndrzej Pietrasiewicz static void usbg_detach(struct usbg_tpg *);
147608a1cb0fSAndrzej Pietrasiewicz 
usbg_enable_tpg(struct se_portal_group * se_tpg,bool enable)147708a1cb0fSAndrzej Pietrasiewicz static int usbg_enable_tpg(struct se_portal_group *se_tpg, bool enable)
147808a1cb0fSAndrzej Pietrasiewicz {
147908a1cb0fSAndrzej Pietrasiewicz 	struct usbg_tpg  *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg);
148008a1cb0fSAndrzej Pietrasiewicz 	int ret = 0;
148108a1cb0fSAndrzej Pietrasiewicz 
148208a1cb0fSAndrzej Pietrasiewicz 	if (enable)
148308a1cb0fSAndrzej Pietrasiewicz 		ret = usbg_attach(tpg);
148408a1cb0fSAndrzej Pietrasiewicz 	else
148508a1cb0fSAndrzej Pietrasiewicz 		usbg_detach(tpg);
148608a1cb0fSAndrzej Pietrasiewicz 	if (ret)
148708a1cb0fSAndrzej Pietrasiewicz 		return ret;
148808a1cb0fSAndrzej Pietrasiewicz 
148908a1cb0fSAndrzej Pietrasiewicz 	tpg->gadget_connect = enable;
149008a1cb0fSAndrzej Pietrasiewicz 
149108a1cb0fSAndrzej Pietrasiewicz 	return 0;
149208a1cb0fSAndrzej Pietrasiewicz }
149308a1cb0fSAndrzej Pietrasiewicz 
tcm_usbg_tpg_nexus_show(struct config_item * item,char * page)149408a1cb0fSAndrzej Pietrasiewicz static ssize_t tcm_usbg_tpg_nexus_show(struct config_item *item, char *page)
149508a1cb0fSAndrzej Pietrasiewicz {
149608a1cb0fSAndrzej Pietrasiewicz 	struct se_portal_group *se_tpg = to_tpg(item);
149708a1cb0fSAndrzej Pietrasiewicz 	struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg);
149808a1cb0fSAndrzej Pietrasiewicz 	struct tcm_usbg_nexus *tv_nexus;
149908a1cb0fSAndrzej Pietrasiewicz 	ssize_t ret;
150008a1cb0fSAndrzej Pietrasiewicz 
15015384ee08SDmitry Bogdanov 	mutex_lock(&tpg->tpg_mutex);
150208a1cb0fSAndrzej Pietrasiewicz 	tv_nexus = tpg->tpg_nexus;
150308a1cb0fSAndrzej Pietrasiewicz 	if (!tv_nexus) {
15045384ee08SDmitry Bogdanov 		ret = -ENODEV;
150508a1cb0fSAndrzej Pietrasiewicz 		goto out;
15065384ee08SDmitry Bogdanov 	}
150708a1cb0fSAndrzej Pietrasiewicz 	ret = snprintf(page, PAGE_SIZE, "%s\n",
150808a1cb0fSAndrzej Pietrasiewicz 			tv_nexus->tvn_se_sess->se_node_acl->initiatorname);
150908a1cb0fSAndrzej Pietrasiewicz out:
151008a1cb0fSAndrzej Pietrasiewicz 	mutex_unlock(&tpg->tpg_mutex);
151108a1cb0fSAndrzej Pietrasiewicz 	return ret;
151208a1cb0fSAndrzej Pietrasiewicz }
15135384ee08SDmitry Bogdanov 
usbg_alloc_sess_cb(struct se_portal_group * se_tpg,struct se_session * se_sess,void * p)151408a1cb0fSAndrzej Pietrasiewicz static int usbg_alloc_sess_cb(struct se_portal_group *se_tpg,
15155384ee08SDmitry Bogdanov 			      struct se_session *se_sess, void *p)
151608a1cb0fSAndrzej Pietrasiewicz {
151708a1cb0fSAndrzej Pietrasiewicz 	struct usbg_tpg *tpg = container_of(se_tpg,
151808a1cb0fSAndrzej Pietrasiewicz 				struct usbg_tpg, se_tpg);
151908a1cb0fSAndrzej Pietrasiewicz 
152008a1cb0fSAndrzej Pietrasiewicz 	tpg->tpg_nexus = p;
152108a1cb0fSAndrzej Pietrasiewicz 	return 0;
152208a1cb0fSAndrzej Pietrasiewicz }
152308a1cb0fSAndrzej Pietrasiewicz 
tcm_usbg_make_nexus(struct usbg_tpg * tpg,char * name)152408a1cb0fSAndrzej Pietrasiewicz static int tcm_usbg_make_nexus(struct usbg_tpg *tpg, char *name)
152508a1cb0fSAndrzej Pietrasiewicz {
152608a1cb0fSAndrzej Pietrasiewicz 	struct tcm_usbg_nexus *tv_nexus;
152708a1cb0fSAndrzej Pietrasiewicz 	int ret = 0;
152808a1cb0fSAndrzej Pietrasiewicz 
152908a1cb0fSAndrzej Pietrasiewicz 	mutex_lock(&tpg->tpg_mutex);
153008a1cb0fSAndrzej Pietrasiewicz 	if (tpg->tpg_nexus) {
153108a1cb0fSAndrzej Pietrasiewicz 		ret = -EEXIST;
153208a1cb0fSAndrzej Pietrasiewicz 		pr_debug("tpg->tpg_nexus already exists\n");
153308a1cb0fSAndrzej Pietrasiewicz 		goto out_unlock;
153408a1cb0fSAndrzej Pietrasiewicz 	}
153508a1cb0fSAndrzej Pietrasiewicz 
153608a1cb0fSAndrzej Pietrasiewicz 	tv_nexus = kzalloc(sizeof(*tv_nexus), GFP_KERNEL);
153708a1cb0fSAndrzej Pietrasiewicz 	if (!tv_nexus) {
1538fb444abeSChristoph Hellwig 		ret = -ENOMEM;
1539fb444abeSChristoph Hellwig 		goto out_unlock;
1540fb444abeSChristoph Hellwig 	}
1541fb444abeSChristoph Hellwig 
1542fb444abeSChristoph Hellwig 	tv_nexus->tvn_se_sess = target_setup_session(&tpg->se_tpg,
1543fb444abeSChristoph Hellwig 						     USB_G_DEFAULT_SESSION_TAGS,
1544fb444abeSChristoph Hellwig 						     sizeof(struct usbg_cmd),
1545fb444abeSChristoph Hellwig 						     TARGET_PROT_NORMAL, name,
1546fb444abeSChristoph Hellwig 						     tv_nexus, usbg_alloc_sess_cb);
1547fb444abeSChristoph Hellwig 	if (IS_ERR(tv_nexus->tvn_se_sess)) {
154808a1cb0fSAndrzej Pietrasiewicz #define MAKE_NEXUS_MSG "core_tpg_check_initiator_node_acl() failed for %s\n"
154908a1cb0fSAndrzej Pietrasiewicz 		pr_debug(MAKE_NEXUS_MSG, name);
155008a1cb0fSAndrzej Pietrasiewicz #undef MAKE_NEXUS_MSG
1551fb444abeSChristoph Hellwig 		ret = PTR_ERR(tv_nexus->tvn_se_sess);
155208a1cb0fSAndrzej Pietrasiewicz 		kfree(tv_nexus);
155308a1cb0fSAndrzej Pietrasiewicz 	}
155408a1cb0fSAndrzej Pietrasiewicz 
155508a1cb0fSAndrzej Pietrasiewicz out_unlock:
155608a1cb0fSAndrzej Pietrasiewicz 	mutex_unlock(&tpg->tpg_mutex);
1557fb444abeSChristoph Hellwig 	return ret;
155808a1cb0fSAndrzej Pietrasiewicz }
155908a1cb0fSAndrzej Pietrasiewicz 
tcm_usbg_drop_nexus(struct usbg_tpg * tpg)156008a1cb0fSAndrzej Pietrasiewicz static int tcm_usbg_drop_nexus(struct usbg_tpg *tpg)
1561fb444abeSChristoph Hellwig {
1562fb444abeSChristoph Hellwig 	struct se_session *se_sess;
1563fb444abeSChristoph Hellwig 	struct tcm_usbg_nexus *tv_nexus;
1564fb444abeSChristoph Hellwig 	int ret = -ENODEV;
156508a1cb0fSAndrzej Pietrasiewicz 
1566fa834287SMike Christie 	mutex_lock(&tpg->tpg_mutex);
156771e7ae8eSNicholas Bellinger 	tv_nexus = tpg->tpg_nexus;
156871e7ae8eSNicholas Bellinger 	if (!tv_nexus)
1569fb444abeSChristoph Hellwig 		goto out;
1570fb444abeSChristoph Hellwig 
1571fb444abeSChristoph Hellwig 	se_sess = tv_nexus->tvn_se_sess;
157208a1cb0fSAndrzej Pietrasiewicz 	if (!se_sess)
157308a1cb0fSAndrzej Pietrasiewicz 		goto out;
157408a1cb0fSAndrzej Pietrasiewicz 
1575fb444abeSChristoph Hellwig 	if (atomic_read(&tpg->tpg_port_count)) {
157608a1cb0fSAndrzej Pietrasiewicz 		ret = -EPERM;
1577fb444abeSChristoph Hellwig #define MSG "Unable to remove Host I_T Nexus with active TPG port count: %d\n"
1578fb444abeSChristoph Hellwig 		pr_err(MSG, atomic_read(&tpg->tpg_port_count));
1579fb444abeSChristoph Hellwig #undef MSG
158008a1cb0fSAndrzej Pietrasiewicz 		goto out;
158108a1cb0fSAndrzej Pietrasiewicz 	}
158208a1cb0fSAndrzej Pietrasiewicz 
158308a1cb0fSAndrzej Pietrasiewicz 	pr_debug("Removing I_T Nexus to Initiator Port: %s\n",
158408a1cb0fSAndrzej Pietrasiewicz 			tv_nexus->tvn_se_sess->se_node_acl->initiatorname);
158508a1cb0fSAndrzej Pietrasiewicz 	/*
158608a1cb0fSAndrzej Pietrasiewicz 	 * Release the SCSI I_T Nexus to the emulated vHost Target Port
158708a1cb0fSAndrzej Pietrasiewicz 	 */
158808a1cb0fSAndrzej Pietrasiewicz 	target_remove_session(se_sess);
158908a1cb0fSAndrzej Pietrasiewicz 	tpg->tpg_nexus = NULL;
159008a1cb0fSAndrzej Pietrasiewicz 
159108a1cb0fSAndrzej Pietrasiewicz 	kfree(tv_nexus);
159208a1cb0fSAndrzej Pietrasiewicz 	ret = 0;
159308a1cb0fSAndrzej Pietrasiewicz out:
159408a1cb0fSAndrzej Pietrasiewicz 	mutex_unlock(&tpg->tpg_mutex);
159508a1cb0fSAndrzej Pietrasiewicz 	return ret;
159608a1cb0fSAndrzej Pietrasiewicz }
159708a1cb0fSAndrzej Pietrasiewicz 
tcm_usbg_tpg_nexus_store(struct config_item * item,const char * page,size_t count)159808a1cb0fSAndrzej Pietrasiewicz static ssize_t tcm_usbg_tpg_nexus_store(struct config_item *item,
159908a1cb0fSAndrzej Pietrasiewicz 		const char *page, size_t count)
160008a1cb0fSAndrzej Pietrasiewicz {
160108a1cb0fSAndrzej Pietrasiewicz 	struct se_portal_group *se_tpg = to_tpg(item);
160208a1cb0fSAndrzej Pietrasiewicz 	struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg);
160308a1cb0fSAndrzej Pietrasiewicz 	unsigned char i_port[USBG_NAMELEN], *ptr;
160408a1cb0fSAndrzej Pietrasiewicz 	int ret;
160508a1cb0fSAndrzej Pietrasiewicz 
160608a1cb0fSAndrzej Pietrasiewicz 	if (!strncmp(page, "NULL", 4)) {
160708a1cb0fSAndrzej Pietrasiewicz 		ret = tcm_usbg_drop_nexus(tpg);
160808a1cb0fSAndrzej Pietrasiewicz 		return (!ret) ? count : ret;
160908a1cb0fSAndrzej Pietrasiewicz 	}
161008a1cb0fSAndrzej Pietrasiewicz 	if (strlen(page) >= USBG_NAMELEN) {
161108a1cb0fSAndrzej Pietrasiewicz 
161225b88550SMike Christie #define NEXUS_STORE_MSG "Emulated NAA Sas Address: %s, exceeds max: %d\n"
161308a1cb0fSAndrzej Pietrasiewicz 		pr_err(NEXUS_STORE_MSG, page, USBG_NAMELEN);
161408a1cb0fSAndrzej Pietrasiewicz #undef NEXUS_STORE_MSG
161508a1cb0fSAndrzej Pietrasiewicz 		return -EINVAL;
161608a1cb0fSAndrzej Pietrasiewicz 	}
161708a1cb0fSAndrzej Pietrasiewicz 	snprintf(i_port, USBG_NAMELEN, "%s", page);
161808a1cb0fSAndrzej Pietrasiewicz 
161908a1cb0fSAndrzej Pietrasiewicz 	ptr = strstr(i_port, "naa.");
162008a1cb0fSAndrzej Pietrasiewicz 	if (!ptr) {
162108a1cb0fSAndrzej Pietrasiewicz 		pr_err("Missing 'naa.' prefix\n");
162208a1cb0fSAndrzej Pietrasiewicz 		return -EINVAL;
162308a1cb0fSAndrzej Pietrasiewicz 	}
162408a1cb0fSAndrzej Pietrasiewicz 
162508a1cb0fSAndrzej Pietrasiewicz 	if (i_port[strlen(i_port) - 1] == '\n')
162608a1cb0fSAndrzej Pietrasiewicz 		i_port[strlen(i_port) - 1] = '\0';
162708a1cb0fSAndrzej Pietrasiewicz 
162808a1cb0fSAndrzej Pietrasiewicz 	ret = tcm_usbg_make_nexus(tpg, &i_port[0]);
162908a1cb0fSAndrzej Pietrasiewicz 	if (ret < 0)
163008a1cb0fSAndrzej Pietrasiewicz 		return ret;
163108a1cb0fSAndrzej Pietrasiewicz 	return count;
163208a1cb0fSAndrzej Pietrasiewicz }
163308a1cb0fSAndrzej Pietrasiewicz 
163408a1cb0fSAndrzej Pietrasiewicz CONFIGFS_ATTR(tcm_usbg_tpg_, nexus);
163508a1cb0fSAndrzej Pietrasiewicz 
163608a1cb0fSAndrzej Pietrasiewicz static struct configfs_attribute *usbg_base_attrs[] = {
163708a1cb0fSAndrzej Pietrasiewicz 	&tcm_usbg_tpg_attr_nexus,
163808a1cb0fSAndrzej Pietrasiewicz 	NULL,
163908a1cb0fSAndrzej Pietrasiewicz };
164008a1cb0fSAndrzej Pietrasiewicz 
usbg_port_link(struct se_portal_group * se_tpg,struct se_lun * lun)164108a1cb0fSAndrzej Pietrasiewicz static int usbg_port_link(struct se_portal_group *se_tpg, struct se_lun *lun)
164208a1cb0fSAndrzej Pietrasiewicz {
164308a1cb0fSAndrzej Pietrasiewicz 	struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg);
164408a1cb0fSAndrzej Pietrasiewicz 
164508a1cb0fSAndrzej Pietrasiewicz 	atomic_inc(&tpg->tpg_port_count);
164608a1cb0fSAndrzej Pietrasiewicz 	smp_mb__after_atomic();
164708a1cb0fSAndrzej Pietrasiewicz 	return 0;
164808a1cb0fSAndrzej Pietrasiewicz }
164908a1cb0fSAndrzej Pietrasiewicz 
usbg_port_unlink(struct se_portal_group * se_tpg,struct se_lun * se_lun)165008a1cb0fSAndrzej Pietrasiewicz static void usbg_port_unlink(struct se_portal_group *se_tpg,
165108a1cb0fSAndrzej Pietrasiewicz 		struct se_lun *se_lun)
165208a1cb0fSAndrzej Pietrasiewicz {
165308a1cb0fSAndrzej Pietrasiewicz 	struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg);
165408a1cb0fSAndrzej Pietrasiewicz 
165508a1cb0fSAndrzej Pietrasiewicz 	atomic_dec(&tpg->tpg_port_count);
165608a1cb0fSAndrzej Pietrasiewicz 	smp_mb__after_atomic();
165708a1cb0fSAndrzej Pietrasiewicz }
165808a1cb0fSAndrzej Pietrasiewicz 
usbg_check_stop_free(struct se_cmd * se_cmd)165908a1cb0fSAndrzej Pietrasiewicz static int usbg_check_stop_free(struct se_cmd *se_cmd)
166008a1cb0fSAndrzej Pietrasiewicz {
166108a1cb0fSAndrzej Pietrasiewicz 	return target_put_sess_cmd(se_cmd);
166208a1cb0fSAndrzej Pietrasiewicz }
166308a1cb0fSAndrzej Pietrasiewicz 
166408a1cb0fSAndrzej Pietrasiewicz static const struct target_core_fabric_ops usbg_ops = {
166508a1cb0fSAndrzej Pietrasiewicz 	.module				= THIS_MODULE,
166608a1cb0fSAndrzej Pietrasiewicz 	.fabric_name			= "usb_gadget",
166708a1cb0fSAndrzej Pietrasiewicz 	.tpg_get_wwn			= usbg_get_fabric_wwn,
166808a1cb0fSAndrzej Pietrasiewicz 	.tpg_get_tag			= usbg_get_tag,
166908a1cb0fSAndrzej Pietrasiewicz 	.tpg_check_demo_mode		= usbg_check_true,
167008a1cb0fSAndrzej Pietrasiewicz 	.release_cmd			= usbg_release_cmd,
167108a1cb0fSAndrzej Pietrasiewicz 	.sess_get_initiator_sid		= NULL,
167208a1cb0fSAndrzej Pietrasiewicz 	.write_pending			= usbg_send_write_request,
167308a1cb0fSAndrzej Pietrasiewicz 	.queue_data_in			= usbg_send_read_response,
167408a1cb0fSAndrzej Pietrasiewicz 	.queue_status			= usbg_send_status_response,
167508a1cb0fSAndrzej Pietrasiewicz 	.queue_tm_rsp			= usbg_queue_tm_rsp,
167608a1cb0fSAndrzej Pietrasiewicz 	.aborted_task			= usbg_aborted_task,
167708a1cb0fSAndrzej Pietrasiewicz 	.check_stop_free		= usbg_check_stop_free,
167808a1cb0fSAndrzej Pietrasiewicz 
167908a1cb0fSAndrzej Pietrasiewicz 	.fabric_make_wwn		= usbg_make_tport,
168008a1cb0fSAndrzej Pietrasiewicz 	.fabric_drop_wwn		= usbg_drop_tport,
168108a1cb0fSAndrzej Pietrasiewicz 	.fabric_make_tpg		= usbg_make_tpg,
168208a1cb0fSAndrzej Pietrasiewicz 	.fabric_enable_tpg		= usbg_enable_tpg,
168308a1cb0fSAndrzej Pietrasiewicz 	.fabric_drop_tpg		= usbg_drop_tpg,
168408a1cb0fSAndrzej Pietrasiewicz 	.fabric_post_link		= usbg_port_link,
1685cff834c1SNicholas Bellinger 	.fabric_pre_unlink		= usbg_port_unlink,
168608a1cb0fSAndrzej Pietrasiewicz 	.fabric_init_nodeacl		= usbg_init_nodeacl,
168708a1cb0fSAndrzej Pietrasiewicz 
168808a1cb0fSAndrzej Pietrasiewicz 	.tfc_wwn_attrs			= usbg_wwn_attrs,
168908a1cb0fSAndrzej Pietrasiewicz 	.tfc_tpg_base_attrs		= usbg_base_attrs,
169030c7ca93SDavid Disseldorp };
169108a1cb0fSAndrzej Pietrasiewicz 
169208a1cb0fSAndrzej Pietrasiewicz /* Start gadget.c code */
169308a1cb0fSAndrzej Pietrasiewicz 
169408a1cb0fSAndrzej Pietrasiewicz static struct usb_interface_descriptor bot_intf_desc = {
169508a1cb0fSAndrzej Pietrasiewicz 	.bLength =              sizeof(bot_intf_desc),
169608a1cb0fSAndrzej Pietrasiewicz 	.bDescriptorType =      USB_DT_INTERFACE,
169708a1cb0fSAndrzej Pietrasiewicz 	.bNumEndpoints =        2,
169808a1cb0fSAndrzej Pietrasiewicz 	.bAlternateSetting =	USB_G_ALT_INT_BBB,
169908a1cb0fSAndrzej Pietrasiewicz 	.bInterfaceClass =      USB_CLASS_MASS_STORAGE,
170008a1cb0fSAndrzej Pietrasiewicz 	.bInterfaceSubClass =   USB_SC_SCSI,
170108a1cb0fSAndrzej Pietrasiewicz 	.bInterfaceProtocol =   USB_PR_BULK,
170208a1cb0fSAndrzej Pietrasiewicz };
170308a1cb0fSAndrzej Pietrasiewicz 
170408a1cb0fSAndrzej Pietrasiewicz static struct usb_interface_descriptor uasp_intf_desc = {
170508a1cb0fSAndrzej Pietrasiewicz 	.bLength =		sizeof(uasp_intf_desc),
170608a1cb0fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_INTERFACE,
170708a1cb0fSAndrzej Pietrasiewicz 	.bNumEndpoints =	4,
170808a1cb0fSAndrzej Pietrasiewicz 	.bAlternateSetting =	USB_G_ALT_INT_UAS,
170908a1cb0fSAndrzej Pietrasiewicz 	.bInterfaceClass =	USB_CLASS_MASS_STORAGE,
171008a1cb0fSAndrzej Pietrasiewicz 	.bInterfaceSubClass =	USB_SC_SCSI,
171108a1cb0fSAndrzej Pietrasiewicz 	.bInterfaceProtocol =	USB_PR_UAS,
171208a1cb0fSAndrzej Pietrasiewicz };
17135384ee08SDmitry Bogdanov 
171408a1cb0fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor uasp_bi_desc = {
171508a1cb0fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
171608a1cb0fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
171708a1cb0fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_IN,
171808a1cb0fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
171908a1cb0fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(512),
172008a1cb0fSAndrzej Pietrasiewicz };
172108a1cb0fSAndrzej Pietrasiewicz 
172208a1cb0fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor uasp_fs_bi_desc = {
172308a1cb0fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
172408a1cb0fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
172508a1cb0fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_IN,
172608a1cb0fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
172708a1cb0fSAndrzej Pietrasiewicz };
172808a1cb0fSAndrzej Pietrasiewicz 
172908a1cb0fSAndrzej Pietrasiewicz static struct usb_pipe_usage_descriptor uasp_bi_pipe_desc = {
173008a1cb0fSAndrzej Pietrasiewicz 	.bLength =		sizeof(uasp_bi_pipe_desc),
173108a1cb0fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_PIPE_USAGE,
173208a1cb0fSAndrzej Pietrasiewicz 	.bPipeID =		DATA_IN_PIPE_ID,
173308a1cb0fSAndrzej Pietrasiewicz };
173408a1cb0fSAndrzej Pietrasiewicz 
173508a1cb0fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor uasp_ss_bi_desc = {
173608a1cb0fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
173708a1cb0fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
173808a1cb0fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_IN,
173908a1cb0fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
174008a1cb0fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(1024),
174108a1cb0fSAndrzej Pietrasiewicz };
174208a1cb0fSAndrzej Pietrasiewicz 
174308a1cb0fSAndrzej Pietrasiewicz static struct usb_ss_ep_comp_descriptor uasp_bi_ep_comp_desc = {
174408a1cb0fSAndrzej Pietrasiewicz 	.bLength =		sizeof(uasp_bi_ep_comp_desc),
174508a1cb0fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
174608a1cb0fSAndrzej Pietrasiewicz 	.bMaxBurst =		0,
174708a1cb0fSAndrzej Pietrasiewicz 	.bmAttributes =		UASP_SS_EP_COMP_LOG_STREAMS,
174808a1cb0fSAndrzej Pietrasiewicz 	.wBytesPerInterval =	0,
174908a1cb0fSAndrzej Pietrasiewicz };
175008a1cb0fSAndrzej Pietrasiewicz 
175108a1cb0fSAndrzej Pietrasiewicz static struct usb_ss_ep_comp_descriptor bot_bi_ep_comp_desc = {
175208a1cb0fSAndrzej Pietrasiewicz 	.bLength =		sizeof(bot_bi_ep_comp_desc),
175308a1cb0fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
175408a1cb0fSAndrzej Pietrasiewicz 	.bMaxBurst =		0,
175508a1cb0fSAndrzej Pietrasiewicz };
175608a1cb0fSAndrzej Pietrasiewicz 
175708a1cb0fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor uasp_bo_desc = {
175808a1cb0fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
175908a1cb0fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
176008a1cb0fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_OUT,
176108a1cb0fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
176208a1cb0fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(512),
176308a1cb0fSAndrzej Pietrasiewicz };
176408a1cb0fSAndrzej Pietrasiewicz 
176508a1cb0fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor uasp_fs_bo_desc = {
176608a1cb0fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
176708a1cb0fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
176808a1cb0fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_OUT,
176908a1cb0fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
177008a1cb0fSAndrzej Pietrasiewicz };
177108a1cb0fSAndrzej Pietrasiewicz 
177208a1cb0fSAndrzej Pietrasiewicz static struct usb_pipe_usage_descriptor uasp_bo_pipe_desc = {
177308a1cb0fSAndrzej Pietrasiewicz 	.bLength =		sizeof(uasp_bo_pipe_desc),
177408a1cb0fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_PIPE_USAGE,
177508a1cb0fSAndrzej Pietrasiewicz 	.bPipeID =		DATA_OUT_PIPE_ID,
177608a1cb0fSAndrzej Pietrasiewicz };
177708a1cb0fSAndrzej Pietrasiewicz 
177808a1cb0fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor uasp_ss_bo_desc = {
177908a1cb0fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
178008a1cb0fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
178108a1cb0fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_OUT,
178208a1cb0fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
178308a1cb0fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(0x400),
178408a1cb0fSAndrzej Pietrasiewicz };
178508a1cb0fSAndrzej Pietrasiewicz 
178608a1cb0fSAndrzej Pietrasiewicz static struct usb_ss_ep_comp_descriptor uasp_bo_ep_comp_desc = {
178708a1cb0fSAndrzej Pietrasiewicz 	.bLength =		sizeof(uasp_bo_ep_comp_desc),
178808a1cb0fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
178908a1cb0fSAndrzej Pietrasiewicz 	.bmAttributes =		UASP_SS_EP_COMP_LOG_STREAMS,
179008a1cb0fSAndrzej Pietrasiewicz };
179108a1cb0fSAndrzej Pietrasiewicz 
179208a1cb0fSAndrzej Pietrasiewicz static struct usb_ss_ep_comp_descriptor bot_bo_ep_comp_desc = {
179308a1cb0fSAndrzej Pietrasiewicz 	.bLength =		sizeof(bot_bo_ep_comp_desc),
179408a1cb0fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
179508a1cb0fSAndrzej Pietrasiewicz };
179608a1cb0fSAndrzej Pietrasiewicz 
179708a1cb0fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor uasp_status_desc = {
179808a1cb0fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
179908a1cb0fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
180008a1cb0fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_IN,
180108a1cb0fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
180208a1cb0fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(512),
180308a1cb0fSAndrzej Pietrasiewicz };
180408a1cb0fSAndrzej Pietrasiewicz 
180508a1cb0fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor uasp_fs_status_desc = {
180608a1cb0fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
180708a1cb0fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
180808a1cb0fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_IN,
180908a1cb0fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
181008a1cb0fSAndrzej Pietrasiewicz };
181108a1cb0fSAndrzej Pietrasiewicz 
181208a1cb0fSAndrzej Pietrasiewicz static struct usb_pipe_usage_descriptor uasp_status_pipe_desc = {
181308a1cb0fSAndrzej Pietrasiewicz 	.bLength =		sizeof(uasp_status_pipe_desc),
181408a1cb0fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_PIPE_USAGE,
181508a1cb0fSAndrzej Pietrasiewicz 	.bPipeID =		STATUS_PIPE_ID,
181608a1cb0fSAndrzej Pietrasiewicz };
181708a1cb0fSAndrzej Pietrasiewicz 
181808a1cb0fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor uasp_ss_status_desc = {
181908a1cb0fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
182008a1cb0fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
182108a1cb0fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_IN,
182208a1cb0fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
182308a1cb0fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(1024),
182408a1cb0fSAndrzej Pietrasiewicz };
182508a1cb0fSAndrzej Pietrasiewicz 
182608a1cb0fSAndrzej Pietrasiewicz static struct usb_ss_ep_comp_descriptor uasp_status_in_ep_comp_desc = {
182708a1cb0fSAndrzej Pietrasiewicz 	.bLength =		sizeof(uasp_status_in_ep_comp_desc),
182808a1cb0fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
182908a1cb0fSAndrzej Pietrasiewicz 	.bmAttributes =		UASP_SS_EP_COMP_LOG_STREAMS,
183008a1cb0fSAndrzej Pietrasiewicz };
183108a1cb0fSAndrzej Pietrasiewicz 
183208a1cb0fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor uasp_cmd_desc = {
183308a1cb0fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
183408a1cb0fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
183508a1cb0fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_OUT,
183608a1cb0fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
183708a1cb0fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(512),
183808a1cb0fSAndrzej Pietrasiewicz };
183908a1cb0fSAndrzej Pietrasiewicz 
184008a1cb0fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor uasp_fs_cmd_desc = {
184108a1cb0fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
184208a1cb0fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
184308a1cb0fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_OUT,
184408a1cb0fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
184508a1cb0fSAndrzej Pietrasiewicz };
184608a1cb0fSAndrzej Pietrasiewicz 
184708a1cb0fSAndrzej Pietrasiewicz static struct usb_pipe_usage_descriptor uasp_cmd_pipe_desc = {
184808a1cb0fSAndrzej Pietrasiewicz 	.bLength =		sizeof(uasp_cmd_pipe_desc),
184908a1cb0fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_PIPE_USAGE,
185008a1cb0fSAndrzej Pietrasiewicz 	.bPipeID =		CMD_PIPE_ID,
185108a1cb0fSAndrzej Pietrasiewicz };
185208a1cb0fSAndrzej Pietrasiewicz 
185308a1cb0fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor uasp_ss_cmd_desc = {
185408a1cb0fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
185508a1cb0fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
185608a1cb0fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_OUT,
185708a1cb0fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
185808a1cb0fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(1024),
185908a1cb0fSAndrzej Pietrasiewicz };
186008a1cb0fSAndrzej Pietrasiewicz 
186108a1cb0fSAndrzej Pietrasiewicz static struct usb_ss_ep_comp_descriptor uasp_cmd_comp_desc = {
186208a1cb0fSAndrzej Pietrasiewicz 	.bLength =		sizeof(uasp_cmd_comp_desc),
186308a1cb0fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
186408a1cb0fSAndrzej Pietrasiewicz };
186508a1cb0fSAndrzej Pietrasiewicz 
186608a1cb0fSAndrzej Pietrasiewicz static struct usb_descriptor_header *uasp_fs_function_desc[] = {
186708a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &bot_intf_desc,
186808a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_fs_bi_desc,
186908a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_fs_bo_desc,
187008a1cb0fSAndrzej Pietrasiewicz 
187108a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_intf_desc,
187208a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_fs_bi_desc,
187308a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_bi_pipe_desc,
187408a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_fs_bo_desc,
187508a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_bo_pipe_desc,
187608a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_fs_status_desc,
187708a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_status_pipe_desc,
187808a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_fs_cmd_desc,
187908a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_cmd_pipe_desc,
188008a1cb0fSAndrzej Pietrasiewicz 	NULL,
188108a1cb0fSAndrzej Pietrasiewicz };
188208a1cb0fSAndrzej Pietrasiewicz 
188308a1cb0fSAndrzej Pietrasiewicz static struct usb_descriptor_header *uasp_hs_function_desc[] = {
188408a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &bot_intf_desc,
188508a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_bi_desc,
188608a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_bo_desc,
188708a1cb0fSAndrzej Pietrasiewicz 
188808a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_intf_desc,
188908a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_bi_desc,
189008a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_bi_pipe_desc,
189108a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_bo_desc,
189208a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_bo_pipe_desc,
189308a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_status_desc,
189408a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_status_pipe_desc,
189508a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_cmd_desc,
189608a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_cmd_pipe_desc,
189708a1cb0fSAndrzej Pietrasiewicz 	NULL,
189808a1cb0fSAndrzej Pietrasiewicz };
189908a1cb0fSAndrzej Pietrasiewicz 
190008a1cb0fSAndrzej Pietrasiewicz static struct usb_descriptor_header *uasp_ss_function_desc[] = {
190108a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &bot_intf_desc,
190208a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_ss_bi_desc,
190308a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &bot_bi_ep_comp_desc,
190408a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_ss_bo_desc,
190508a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &bot_bo_ep_comp_desc,
190608a1cb0fSAndrzej Pietrasiewicz 
190708a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_intf_desc,
190808a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_ss_bi_desc,
190908a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_bi_ep_comp_desc,
191008a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_bi_pipe_desc,
191108a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_ss_bo_desc,
191208a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_bo_ep_comp_desc,
191308a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_bo_pipe_desc,
191408a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_ss_status_desc,
191508a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_status_in_ep_comp_desc,
191608a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_status_pipe_desc,
191708a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_ss_cmd_desc,
191808a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_cmd_comp_desc,
191908a1cb0fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &uasp_cmd_pipe_desc,
192008a1cb0fSAndrzej Pietrasiewicz 	NULL,
192108a1cb0fSAndrzej Pietrasiewicz };
192208a1cb0fSAndrzej Pietrasiewicz 
192308a1cb0fSAndrzej Pietrasiewicz static struct usb_string	tcm_us_strings[] = {
192408a1cb0fSAndrzej Pietrasiewicz 	[USB_G_STR_INT_UAS].s		= "USB Attached SCSI",
192508a1cb0fSAndrzej Pietrasiewicz 	[USB_G_STR_INT_BBB].s		= "Bulk Only Transport",
192608a1cb0fSAndrzej Pietrasiewicz 	{ },
192708a1cb0fSAndrzej Pietrasiewicz };
192808a1cb0fSAndrzej Pietrasiewicz 
192908a1cb0fSAndrzej Pietrasiewicz static struct usb_gadget_strings tcm_stringtab = {
193008a1cb0fSAndrzej Pietrasiewicz 	.language = 0x0409,
193108a1cb0fSAndrzej Pietrasiewicz 	.strings = tcm_us_strings,
193208a1cb0fSAndrzej Pietrasiewicz };
193308a1cb0fSAndrzej Pietrasiewicz 
193408a1cb0fSAndrzej Pietrasiewicz static struct usb_gadget_strings *tcm_strings[] = {
193508a1cb0fSAndrzej Pietrasiewicz 	&tcm_stringtab,
193608a1cb0fSAndrzej Pietrasiewicz 	NULL,
193708a1cb0fSAndrzej Pietrasiewicz };
193808a1cb0fSAndrzej Pietrasiewicz 
tcm_bind(struct usb_configuration * c,struct usb_function * f)193908a1cb0fSAndrzej Pietrasiewicz static int tcm_bind(struct usb_configuration *c, struct usb_function *f)
194008a1cb0fSAndrzej Pietrasiewicz {
194108a1cb0fSAndrzej Pietrasiewicz 	struct f_uas		*fu = to_f_uas(f);
194208a1cb0fSAndrzej Pietrasiewicz 	struct usb_string	*us;
194308a1cb0fSAndrzej Pietrasiewicz 	struct usb_gadget	*gadget = c->cdev->gadget;
194408a1cb0fSAndrzej Pietrasiewicz 	struct usb_ep		*ep;
194508a1cb0fSAndrzej Pietrasiewicz 	struct f_tcm_opts	*opts;
194608a1cb0fSAndrzej Pietrasiewicz 	int			iface;
194708a1cb0fSAndrzej Pietrasiewicz 	int			ret;
194808a1cb0fSAndrzej Pietrasiewicz 
194908a1cb0fSAndrzej Pietrasiewicz 	opts = container_of(f->fi, struct f_tcm_opts, func_inst);
195008a1cb0fSAndrzej Pietrasiewicz 
195108a1cb0fSAndrzej Pietrasiewicz 	mutex_lock(&opts->dep_lock);
195208a1cb0fSAndrzej Pietrasiewicz 	if (!opts->can_attach) {
195308a1cb0fSAndrzej Pietrasiewicz 		mutex_unlock(&opts->dep_lock);
195408a1cb0fSAndrzej Pietrasiewicz 		return -ENODEV;
195508a1cb0fSAndrzej Pietrasiewicz 	}
195608a1cb0fSAndrzej Pietrasiewicz 	mutex_unlock(&opts->dep_lock);
195708a1cb0fSAndrzej Pietrasiewicz 	us = usb_gstrings_attach(c->cdev, tcm_strings,
195808a1cb0fSAndrzej Pietrasiewicz 		ARRAY_SIZE(tcm_us_strings));
195908a1cb0fSAndrzej Pietrasiewicz 	if (IS_ERR(us))
196008a1cb0fSAndrzej Pietrasiewicz 		return PTR_ERR(us);
196108a1cb0fSAndrzej Pietrasiewicz 	bot_intf_desc.iInterface = us[USB_G_STR_INT_BBB].id;
196208a1cb0fSAndrzej Pietrasiewicz 	uasp_intf_desc.iInterface = us[USB_G_STR_INT_UAS].id;
196308a1cb0fSAndrzej Pietrasiewicz 
196408a1cb0fSAndrzej Pietrasiewicz 	iface = usb_interface_id(c, f);
196508a1cb0fSAndrzej Pietrasiewicz 	if (iface < 0)
196608a1cb0fSAndrzej Pietrasiewicz 		return iface;
196708a1cb0fSAndrzej Pietrasiewicz 
196808a1cb0fSAndrzej Pietrasiewicz 	bot_intf_desc.bInterfaceNumber = iface;
196908a1cb0fSAndrzej Pietrasiewicz 	uasp_intf_desc.bInterfaceNumber = iface;
197008a1cb0fSAndrzej Pietrasiewicz 	fu->iface = iface;
197108a1cb0fSAndrzej Pietrasiewicz 	ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_bi_desc,
197208a1cb0fSAndrzej Pietrasiewicz 			&uasp_bi_ep_comp_desc);
19739beab5d4SAndrzej Pietrasiewicz 	if (!ep)
197408a1cb0fSAndrzej Pietrasiewicz 		goto ep_fail;
197508a1cb0fSAndrzej Pietrasiewicz 
1976dc8c46a5SAndrzej Pietrasiewicz 	fu->ep_in = ep;
197708a1cb0fSAndrzej Pietrasiewicz 
197808a1cb0fSAndrzej Pietrasiewicz 	ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_bo_desc,
197908a1cb0fSAndrzej Pietrasiewicz 			&uasp_bo_ep_comp_desc);
1980dc8c46a5SAndrzej Pietrasiewicz 	if (!ep)
1981dc8c46a5SAndrzej Pietrasiewicz 		goto ep_fail;
1982dc8c46a5SAndrzej Pietrasiewicz 	fu->ep_out = ep;
1983dc8c46a5SAndrzej Pietrasiewicz 
1984dc8c46a5SAndrzej Pietrasiewicz 	ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_status_desc,
1985dc8c46a5SAndrzej Pietrasiewicz 			&uasp_status_in_ep_comp_desc);
1986dc8c46a5SAndrzej Pietrasiewicz 	if (!ep)
1987dc8c46a5SAndrzej Pietrasiewicz 		goto ep_fail;
19889beab5d4SAndrzej Pietrasiewicz 	fu->ep_status = ep;
19899beab5d4SAndrzej Pietrasiewicz 
19909beab5d4SAndrzej Pietrasiewicz 	ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_cmd_desc,
19919beab5d4SAndrzej Pietrasiewicz 			&uasp_cmd_comp_desc);
19929beab5d4SAndrzej Pietrasiewicz 	if (!ep)
19939beab5d4SAndrzej Pietrasiewicz 		goto ep_fail;
1994dc8c46a5SAndrzej Pietrasiewicz 	fu->ep_cmd = ep;
199508a1cb0fSAndrzej Pietrasiewicz 
199608a1cb0fSAndrzej Pietrasiewicz 	/* Assume endpoint addresses are the same for both speeds */
199708a1cb0fSAndrzej Pietrasiewicz 	uasp_bi_desc.bEndpointAddress =	uasp_ss_bi_desc.bEndpointAddress;
199808a1cb0fSAndrzej Pietrasiewicz 	uasp_bo_desc.bEndpointAddress = uasp_ss_bo_desc.bEndpointAddress;
199908a1cb0fSAndrzej Pietrasiewicz 	uasp_status_desc.bEndpointAddress =
200008a1cb0fSAndrzej Pietrasiewicz 		uasp_ss_status_desc.bEndpointAddress;
200108a1cb0fSAndrzej Pietrasiewicz 	uasp_cmd_desc.bEndpointAddress = uasp_ss_cmd_desc.bEndpointAddress;
200208a1cb0fSAndrzej Pietrasiewicz 
200308a1cb0fSAndrzej Pietrasiewicz 	uasp_fs_bi_desc.bEndpointAddress = uasp_ss_bi_desc.bEndpointAddress;
200408a1cb0fSAndrzej Pietrasiewicz 	uasp_fs_bo_desc.bEndpointAddress = uasp_ss_bo_desc.bEndpointAddress;
200508a1cb0fSAndrzej Pietrasiewicz 	uasp_fs_status_desc.bEndpointAddress =
200608a1cb0fSAndrzej Pietrasiewicz 		uasp_ss_status_desc.bEndpointAddress;
200708a1cb0fSAndrzej Pietrasiewicz 	uasp_fs_cmd_desc.bEndpointAddress = uasp_ss_cmd_desc.bEndpointAddress;
200808a1cb0fSAndrzej Pietrasiewicz 
200908a1cb0fSAndrzej Pietrasiewicz 	ret = usb_assign_descriptors(f, uasp_fs_function_desc,
201008a1cb0fSAndrzej Pietrasiewicz 			uasp_hs_function_desc, uasp_ss_function_desc,
201108a1cb0fSAndrzej Pietrasiewicz 			uasp_ss_function_desc);
201208a1cb0fSAndrzej Pietrasiewicz 	if (ret)
201308a1cb0fSAndrzej Pietrasiewicz 		goto ep_fail;
201408a1cb0fSAndrzej Pietrasiewicz 
201508a1cb0fSAndrzej Pietrasiewicz 	return 0;
201608a1cb0fSAndrzej Pietrasiewicz ep_fail:
201708a1cb0fSAndrzej Pietrasiewicz 	pr_err("Can't claim all required eps\n");
201808a1cb0fSAndrzej Pietrasiewicz 
201908a1cb0fSAndrzej Pietrasiewicz 	return -ENOTSUPP;
202008a1cb0fSAndrzej Pietrasiewicz }
202108a1cb0fSAndrzej Pietrasiewicz 
202208a1cb0fSAndrzej Pietrasiewicz struct guas_setup_wq {
202308a1cb0fSAndrzej Pietrasiewicz 	struct work_struct work;
202408a1cb0fSAndrzej Pietrasiewicz 	struct f_uas *fu;
202508a1cb0fSAndrzej Pietrasiewicz 	unsigned int alt;
202608a1cb0fSAndrzej Pietrasiewicz };
202708a1cb0fSAndrzej Pietrasiewicz 
tcm_delayed_set_alt(struct work_struct * wq)202808a1cb0fSAndrzej Pietrasiewicz static void tcm_delayed_set_alt(struct work_struct *wq)
202908a1cb0fSAndrzej Pietrasiewicz {
203008a1cb0fSAndrzej Pietrasiewicz 	struct guas_setup_wq *work = container_of(wq, struct guas_setup_wq,
203108a1cb0fSAndrzej Pietrasiewicz 			work);
203208a1cb0fSAndrzej Pietrasiewicz 	struct f_uas *fu = work->fu;
203308a1cb0fSAndrzej Pietrasiewicz 	int alt = work->alt;
203408a1cb0fSAndrzej Pietrasiewicz 
203508a1cb0fSAndrzej Pietrasiewicz 	kfree(work);
203608a1cb0fSAndrzej Pietrasiewicz 
203708a1cb0fSAndrzej Pietrasiewicz 	if (fu->flags & USBG_IS_BOT)
203808a1cb0fSAndrzej Pietrasiewicz 		bot_cleanup_old_alt(fu);
203908a1cb0fSAndrzej Pietrasiewicz 	if (fu->flags & USBG_IS_UAS)
204008a1cb0fSAndrzej Pietrasiewicz 		uasp_cleanup_old_alt(fu);
204190c4d057SMaciej Żenczykowski 
204290c4d057SMaciej Żenczykowski 	if (alt == USB_G_ALT_INT_BBB)
204308a1cb0fSAndrzej Pietrasiewicz 		bot_set_alt(fu);
204408a1cb0fSAndrzej Pietrasiewicz 	else if (alt == USB_G_ALT_INT_UAS)
204508a1cb0fSAndrzej Pietrasiewicz 		uasp_set_alt(fu);
204608a1cb0fSAndrzej Pietrasiewicz 	usb_composite_setup_continue(fu->function.config->cdev);
204708a1cb0fSAndrzej Pietrasiewicz }
204808a1cb0fSAndrzej Pietrasiewicz 
tcm_get_alt(struct usb_function * f,unsigned intf)204908a1cb0fSAndrzej Pietrasiewicz static int tcm_get_alt(struct usb_function *f, unsigned intf)
205008a1cb0fSAndrzej Pietrasiewicz {
205108a1cb0fSAndrzej Pietrasiewicz 	if (intf == bot_intf_desc.bInterfaceNumber)
205208a1cb0fSAndrzej Pietrasiewicz 		return USB_G_ALT_INT_BBB;
205308a1cb0fSAndrzej Pietrasiewicz 	if (intf == uasp_intf_desc.bInterfaceNumber)
205408a1cb0fSAndrzej Pietrasiewicz 		return USB_G_ALT_INT_UAS;
205508a1cb0fSAndrzej Pietrasiewicz 
205608a1cb0fSAndrzej Pietrasiewicz 	return -EOPNOTSUPP;
205708a1cb0fSAndrzej Pietrasiewicz }
205808a1cb0fSAndrzej Pietrasiewicz 
tcm_set_alt(struct usb_function * f,unsigned intf,unsigned alt)205908a1cb0fSAndrzej Pietrasiewicz static int tcm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
206008a1cb0fSAndrzej Pietrasiewicz {
206108a1cb0fSAndrzej Pietrasiewicz 	struct f_uas *fu = to_f_uas(f);
206208a1cb0fSAndrzej Pietrasiewicz 
206308a1cb0fSAndrzej Pietrasiewicz 	if ((alt == USB_G_ALT_INT_BBB) || (alt == USB_G_ALT_INT_UAS)) {
206408a1cb0fSAndrzej Pietrasiewicz 		struct guas_setup_wq *work;
206508a1cb0fSAndrzej Pietrasiewicz 
206608a1cb0fSAndrzej Pietrasiewicz 		work = kmalloc(sizeof(*work), GFP_ATOMIC);
206708a1cb0fSAndrzej Pietrasiewicz 		if (!work)
206808a1cb0fSAndrzej Pietrasiewicz 			return -ENOMEM;
206908a1cb0fSAndrzej Pietrasiewicz 		INIT_WORK(&work->work, tcm_delayed_set_alt);
207008a1cb0fSAndrzej Pietrasiewicz 		work->fu = fu;
207108a1cb0fSAndrzej Pietrasiewicz 		work->alt = alt;
207208a1cb0fSAndrzej Pietrasiewicz 		schedule_work(&work->work);
207308a1cb0fSAndrzej Pietrasiewicz 		return USB_GADGET_DELAYED_STATUS;
207408a1cb0fSAndrzej Pietrasiewicz 	}
207508a1cb0fSAndrzej Pietrasiewicz 	return -EOPNOTSUPP;
207608a1cb0fSAndrzej Pietrasiewicz }
207708a1cb0fSAndrzej Pietrasiewicz 
tcm_disable(struct usb_function * f)207808a1cb0fSAndrzej Pietrasiewicz static void tcm_disable(struct usb_function *f)
207908a1cb0fSAndrzej Pietrasiewicz {
20800b8b1a1fSJayshri Pawar 	struct f_uas *fu = to_f_uas(f);
20810b8b1a1fSJayshri Pawar 
20820b8b1a1fSJayshri Pawar 	if (fu->flags & USBG_IS_UAS)
20830b8b1a1fSJayshri Pawar 		uasp_cleanup_old_alt(fu);
20840b8b1a1fSJayshri Pawar 	else if (fu->flags & USBG_IS_BOT)
20850b8b1a1fSJayshri Pawar 		bot_cleanup_old_alt(fu);
20860b8b1a1fSJayshri Pawar 	fu->flags = 0;
20870b8b1a1fSJayshri Pawar }
20880b8b1a1fSJayshri Pawar 
tcm_setup(struct usb_function * f,const struct usb_ctrlrequest * ctrl)20890b8b1a1fSJayshri Pawar static int tcm_setup(struct usb_function *f,
209008a1cb0fSAndrzej Pietrasiewicz 		const struct usb_ctrlrequest *ctrl)
209108a1cb0fSAndrzej Pietrasiewicz {
209208a1cb0fSAndrzej Pietrasiewicz 	struct f_uas *fu = to_f_uas(f);
209308a1cb0fSAndrzej Pietrasiewicz 
209408a1cb0fSAndrzej Pietrasiewicz 	if (!(fu->flags & USBG_IS_BOT))
209508a1cb0fSAndrzej Pietrasiewicz 		return -EOPNOTSUPP;
209608a1cb0fSAndrzej Pietrasiewicz 
209708a1cb0fSAndrzej Pietrasiewicz 	return usbg_bot_setup(f, ctrl);
209808a1cb0fSAndrzej Pietrasiewicz }
209908a1cb0fSAndrzej Pietrasiewicz 
to_f_tcm_opts(struct config_item * item)210008a1cb0fSAndrzej Pietrasiewicz static inline struct f_tcm_opts *to_f_tcm_opts(struct config_item *item)
210108a1cb0fSAndrzej Pietrasiewicz {
210208a1cb0fSAndrzej Pietrasiewicz 	return container_of(to_config_group(item), struct f_tcm_opts,
210308a1cb0fSAndrzej Pietrasiewicz 		func_inst.group);
210408a1cb0fSAndrzej Pietrasiewicz }
210508a1cb0fSAndrzej Pietrasiewicz 
tcm_attr_release(struct config_item * item)210608a1cb0fSAndrzej Pietrasiewicz static void tcm_attr_release(struct config_item *item)
210708a1cb0fSAndrzej Pietrasiewicz {
210808a1cb0fSAndrzej Pietrasiewicz 	struct f_tcm_opts *opts = to_f_tcm_opts(item);
210908a1cb0fSAndrzej Pietrasiewicz 
211008a1cb0fSAndrzej Pietrasiewicz 	usb_put_function_instance(&opts->func_inst);
211108a1cb0fSAndrzej Pietrasiewicz }
211208a1cb0fSAndrzej Pietrasiewicz 
211308a1cb0fSAndrzej Pietrasiewicz static struct configfs_item_operations tcm_item_ops = {
211408a1cb0fSAndrzej Pietrasiewicz 	.release		= tcm_attr_release,
211508a1cb0fSAndrzej Pietrasiewicz };
211608a1cb0fSAndrzej Pietrasiewicz 
211708a1cb0fSAndrzej Pietrasiewicz static const struct config_item_type tcm_func_type = {
211808a1cb0fSAndrzej Pietrasiewicz 	.ct_item_ops	= &tcm_item_ops,
211908a1cb0fSAndrzej Pietrasiewicz 	.ct_owner	= THIS_MODULE,
212008a1cb0fSAndrzej Pietrasiewicz };
212108a1cb0fSAndrzej Pietrasiewicz 
tcm_free_inst(struct usb_function_instance * f)212208a1cb0fSAndrzej Pietrasiewicz static void tcm_free_inst(struct usb_function_instance *f)
212308a1cb0fSAndrzej Pietrasiewicz {
212408a1cb0fSAndrzej Pietrasiewicz 	struct f_tcm_opts *opts;
212508a1cb0fSAndrzej Pietrasiewicz 	unsigned i;
212608a1cb0fSAndrzej Pietrasiewicz 
212708a1cb0fSAndrzej Pietrasiewicz 	opts = container_of(f, struct f_tcm_opts, func_inst);
212808a1cb0fSAndrzej Pietrasiewicz 
212908a1cb0fSAndrzej Pietrasiewicz 	mutex_lock(&tpg_instances_lock);
213008a1cb0fSAndrzej Pietrasiewicz 	for (i = 0; i < TPG_INSTANCES; ++i)
21314bb8548dSAndrzej Pietrasiewicz 		if (tpg_instances[i].func_inst == f)
21324bb8548dSAndrzej Pietrasiewicz 			break;
21334bb8548dSAndrzej Pietrasiewicz 	if (i < TPG_INSTANCES)
21344bb8548dSAndrzej Pietrasiewicz 		tpg_instances[i].func_inst = NULL;
21354bb8548dSAndrzej Pietrasiewicz 	mutex_unlock(&tpg_instances_lock);
21364bb8548dSAndrzej Pietrasiewicz 
21374bb8548dSAndrzej Pietrasiewicz 	kfree(opts);
21384bb8548dSAndrzej Pietrasiewicz }
21394bb8548dSAndrzej Pietrasiewicz 
tcm_register_callback(struct usb_function_instance * f)21404bb8548dSAndrzej Pietrasiewicz static int tcm_register_callback(struct usb_function_instance *f)
21414bb8548dSAndrzej Pietrasiewicz {
21424bb8548dSAndrzej Pietrasiewicz 	struct f_tcm_opts *opts = container_of(f, struct f_tcm_opts, func_inst);
21434bb8548dSAndrzej Pietrasiewicz 
21444bb8548dSAndrzej Pietrasiewicz 	mutex_lock(&opts->dep_lock);
21454bb8548dSAndrzej Pietrasiewicz 	opts->can_attach = true;
21464bb8548dSAndrzej Pietrasiewicz 	mutex_unlock(&opts->dep_lock);
21474bb8548dSAndrzej Pietrasiewicz 
214897363902SBhumika Goyal 	return 0;
21494bb8548dSAndrzej Pietrasiewicz }
21504bb8548dSAndrzej Pietrasiewicz 
tcm_unregister_callback(struct usb_function_instance * f)21514bb8548dSAndrzej Pietrasiewicz static void tcm_unregister_callback(struct usb_function_instance *f)
21524bb8548dSAndrzej Pietrasiewicz {
2153dc8c46a5SAndrzej Pietrasiewicz 	struct f_tcm_opts *opts = container_of(f, struct f_tcm_opts, func_inst);
2154dc8c46a5SAndrzej Pietrasiewicz 
2155dc8c46a5SAndrzej Pietrasiewicz 	mutex_lock(&opts->dep_lock);
2156dc8c46a5SAndrzej Pietrasiewicz 	unregister_gadget_item(opts->
2157dc8c46a5SAndrzej Pietrasiewicz 		func_inst.group.cg_item.ci_parent->ci_parent);
2158dc8c46a5SAndrzej Pietrasiewicz 	opts->can_attach = false;
2159dc8c46a5SAndrzej Pietrasiewicz 	mutex_unlock(&opts->dep_lock);
2160dc8c46a5SAndrzej Pietrasiewicz }
2161dc8c46a5SAndrzej Pietrasiewicz 
usbg_attach(struct usbg_tpg * tpg)2162dc8c46a5SAndrzej Pietrasiewicz static int usbg_attach(struct usbg_tpg *tpg)
2163dc8c46a5SAndrzej Pietrasiewicz {
2164dc8c46a5SAndrzej Pietrasiewicz 	struct usb_function_instance *f = tpg->fi;
2165dc8c46a5SAndrzej Pietrasiewicz 	struct f_tcm_opts *opts = container_of(f, struct f_tcm_opts, func_inst);
2166dc8c46a5SAndrzej Pietrasiewicz 
2167dc8c46a5SAndrzej Pietrasiewicz 	if (opts->tcm_register_callback)
2168dc8c46a5SAndrzej Pietrasiewicz 		return opts->tcm_register_callback(f);
2169dc8c46a5SAndrzej Pietrasiewicz 
2170dc8c46a5SAndrzej Pietrasiewicz 	return 0;
21714bb8548dSAndrzej Pietrasiewicz }
21724bb8548dSAndrzej Pietrasiewicz 
usbg_detach(struct usbg_tpg * tpg)21734bb8548dSAndrzej Pietrasiewicz static void usbg_detach(struct usbg_tpg *tpg)
21744bb8548dSAndrzej Pietrasiewicz {
21754bb8548dSAndrzej Pietrasiewicz 	struct usb_function_instance *f = tpg->fi;
21764bb8548dSAndrzej Pietrasiewicz 	struct f_tcm_opts *opts = container_of(f, struct f_tcm_opts, func_inst);
21774bb8548dSAndrzej Pietrasiewicz 
21784bb8548dSAndrzej Pietrasiewicz 	if (opts->tcm_unregister_callback)
21794bb8548dSAndrzej Pietrasiewicz 		opts->tcm_unregister_callback(f);
21804bb8548dSAndrzej Pietrasiewicz }
21814bb8548dSAndrzej Pietrasiewicz 
tcm_set_name(struct usb_function_instance * f,const char * name)21824bb8548dSAndrzej Pietrasiewicz static int tcm_set_name(struct usb_function_instance *f, const char *name)
21834bb8548dSAndrzej Pietrasiewicz {
21844bb8548dSAndrzej Pietrasiewicz 	struct f_tcm_opts *opts = container_of(f, struct f_tcm_opts, func_inst);
21854bb8548dSAndrzej Pietrasiewicz 
21864bb8548dSAndrzej Pietrasiewicz 	pr_debug("tcm: Activating %s\n", name);
21874bb8548dSAndrzej Pietrasiewicz 
21884bb8548dSAndrzej Pietrasiewicz 	mutex_lock(&opts->dep_lock);
21894bb8548dSAndrzej Pietrasiewicz 	opts->ready = true;
21904bb8548dSAndrzej Pietrasiewicz 	mutex_unlock(&opts->dep_lock);
21914bb8548dSAndrzej Pietrasiewicz 
21924bb8548dSAndrzej Pietrasiewicz 	return 0;
2193dc8c46a5SAndrzej Pietrasiewicz }
2194dc8c46a5SAndrzej Pietrasiewicz 
tcm_alloc_inst(void)2195dc8c46a5SAndrzej Pietrasiewicz static struct usb_function_instance *tcm_alloc_inst(void)
2196dc8c46a5SAndrzej Pietrasiewicz {
2197dc8c46a5SAndrzej Pietrasiewicz 	struct f_tcm_opts *opts;
2198dc8c46a5SAndrzej Pietrasiewicz 	int i;
2199dc8c46a5SAndrzej Pietrasiewicz 
2200dc8c46a5SAndrzej Pietrasiewicz 
2201dc8c46a5SAndrzej Pietrasiewicz 	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
2202dc8c46a5SAndrzej Pietrasiewicz 	if (!opts)
2203dc8c46a5SAndrzej Pietrasiewicz 		return ERR_PTR(-ENOMEM);
2204dc8c46a5SAndrzej Pietrasiewicz 
2205dc8c46a5SAndrzej Pietrasiewicz 	mutex_lock(&tpg_instances_lock);
2206dc8c46a5SAndrzej Pietrasiewicz 	for (i = 0; i < TPG_INSTANCES; ++i)
2207dc8c46a5SAndrzej Pietrasiewicz 		if (!tpg_instances[i].func_inst)
2208dc8c46a5SAndrzej Pietrasiewicz 			break;
2209dc8c46a5SAndrzej Pietrasiewicz 
2210dc8c46a5SAndrzej Pietrasiewicz 	if (i == TPG_INSTANCES) {
2211dc8c46a5SAndrzej Pietrasiewicz 		mutex_unlock(&tpg_instances_lock);
2212dc8c46a5SAndrzej Pietrasiewicz 		kfree(opts);
2213dc8c46a5SAndrzej Pietrasiewicz 		return ERR_PTR(-EBUSY);
2214dc8c46a5SAndrzej Pietrasiewicz 	}
2215dc8c46a5SAndrzej Pietrasiewicz 	tpg_instances[i].func_inst = &opts->func_inst;
2216dc8c46a5SAndrzej Pietrasiewicz 	mutex_unlock(&tpg_instances_lock);
2217dc8c46a5SAndrzej Pietrasiewicz 
2218dc8c46a5SAndrzej Pietrasiewicz 	mutex_init(&opts->dep_lock);
2219dc8c46a5SAndrzej Pietrasiewicz 	opts->func_inst.set_inst_name = tcm_set_name;
2220dc8c46a5SAndrzej Pietrasiewicz 	opts->func_inst.free_func_inst = tcm_free_inst;
2221dc8c46a5SAndrzej Pietrasiewicz 	opts->tcm_register_callback = tcm_register_callback;
2222dc8c46a5SAndrzej Pietrasiewicz 	opts->tcm_unregister_callback = tcm_unregister_callback;
2223dc8c46a5SAndrzej Pietrasiewicz 
2224dc8c46a5SAndrzej Pietrasiewicz 	config_group_init_type_name(&opts->func_inst.group, "",
2225dc8c46a5SAndrzej Pietrasiewicz 			&tcm_func_type);
2226dc8c46a5SAndrzej Pietrasiewicz 
2227dc8c46a5SAndrzej Pietrasiewicz 	return &opts->func_inst;
2228dc8c46a5SAndrzej Pietrasiewicz }
2229dc8c46a5SAndrzej Pietrasiewicz 
tcm_free(struct usb_function * f)2230dc8c46a5SAndrzej Pietrasiewicz static void tcm_free(struct usb_function *f)
2231dc8c46a5SAndrzej Pietrasiewicz {
2232dc8c46a5SAndrzej Pietrasiewicz 	struct f_uas *tcm = to_f_uas(f);
2233dc8c46a5SAndrzej Pietrasiewicz 
2234dc8c46a5SAndrzej Pietrasiewicz 	kfree(tcm);
2235dc8c46a5SAndrzej Pietrasiewicz }
2236dc8c46a5SAndrzej Pietrasiewicz 
tcm_unbind(struct usb_configuration * c,struct usb_function * f)2237dc8c46a5SAndrzej Pietrasiewicz static void tcm_unbind(struct usb_configuration *c, struct usb_function *f)
2238dc8c46a5SAndrzej Pietrasiewicz {
2239dc8c46a5SAndrzej Pietrasiewicz 	usb_free_all_descriptors(f);
2240dc8c46a5SAndrzej Pietrasiewicz }
2241dc8c46a5SAndrzej Pietrasiewicz 
tcm_alloc(struct usb_function_instance * fi)2242dc8c46a5SAndrzej Pietrasiewicz static struct usb_function *tcm_alloc(struct usb_function_instance *fi)
2243dc8c46a5SAndrzej Pietrasiewicz {
2244dc8c46a5SAndrzej Pietrasiewicz 	struct f_uas *fu;
2245dc8c46a5SAndrzej Pietrasiewicz 	unsigned i;
2246dc8c46a5SAndrzej Pietrasiewicz 
2247dc8c46a5SAndrzej Pietrasiewicz 	mutex_lock(&tpg_instances_lock);
2248dc8c46a5SAndrzej Pietrasiewicz 	for (i = 0; i < TPG_INSTANCES; ++i)
2249dc8c46a5SAndrzej Pietrasiewicz 		if (tpg_instances[i].func_inst == fi)
2250dc8c46a5SAndrzej Pietrasiewicz 			break;
2251dc8c46a5SAndrzej Pietrasiewicz 	if (i == TPG_INSTANCES) {
22524bb8548dSAndrzej Pietrasiewicz 		mutex_unlock(&tpg_instances_lock);
22534bb8548dSAndrzej Pietrasiewicz 		return ERR_PTR(-ENODEV);
22544bb8548dSAndrzej Pietrasiewicz 	}
22554bb8548dSAndrzej Pietrasiewicz 
22564bb8548dSAndrzej Pietrasiewicz 	fu = kzalloc(sizeof(*fu), GFP_KERNEL);
2257dc8c46a5SAndrzej Pietrasiewicz 	if (!fu) {
2258dc8c46a5SAndrzej Pietrasiewicz 		mutex_unlock(&tpg_instances_lock);
2259dc8c46a5SAndrzej Pietrasiewicz 		return ERR_PTR(-ENOMEM);
2260dc8c46a5SAndrzej Pietrasiewicz 	}
2261dc8c46a5SAndrzej Pietrasiewicz 
2262dc8c46a5SAndrzej Pietrasiewicz 	fu->function.name = "Target Function";
2263dc8c46a5SAndrzej Pietrasiewicz 	fu->function.bind = tcm_bind;
2264dc8c46a5SAndrzej Pietrasiewicz 	fu->function.unbind = tcm_unbind;
2265dc8c46a5SAndrzej Pietrasiewicz 	fu->function.set_alt = tcm_set_alt;
2266dc8c46a5SAndrzej Pietrasiewicz 	fu->function.get_alt = tcm_get_alt;
2267dc8c46a5SAndrzej Pietrasiewicz 	fu->function.setup = tcm_setup;
2268dc8c46a5SAndrzej Pietrasiewicz 	fu->function.disable = tcm_disable;
2269dc8c46a5SAndrzej Pietrasiewicz 	fu->function.free_func = tcm_free;
2270dc8c46a5SAndrzej Pietrasiewicz 	fu->tpg = tpg_instances[i].tpg;
2271dc8c46a5SAndrzej Pietrasiewicz 	mutex_unlock(&tpg_instances_lock);
2272dc8c46a5SAndrzej Pietrasiewicz 
2273dc8c46a5SAndrzej Pietrasiewicz 	return &fu->function;
2274dc8c46a5SAndrzej Pietrasiewicz }
2275dc8c46a5SAndrzej Pietrasiewicz 
2276dc8c46a5SAndrzej Pietrasiewicz DECLARE_USB_FUNCTION(tcm, tcm_alloc_inst, tcm_alloc);
2277dc8c46a5SAndrzej Pietrasiewicz 
tcm_init(void)2278dc8c46a5SAndrzej Pietrasiewicz static int __init tcm_init(void)
2279dc8c46a5SAndrzej Pietrasiewicz {
2280dc8c46a5SAndrzej Pietrasiewicz 	int ret;
2281dc8c46a5SAndrzej Pietrasiewicz 
2282dc8c46a5SAndrzej Pietrasiewicz 	ret = usb_function_register(&tcmusb_func);
2283dc8c46a5SAndrzej Pietrasiewicz 	if (ret)
2284dc8c46a5SAndrzej Pietrasiewicz 		return ret;
2285dc8c46a5SAndrzej Pietrasiewicz 
2286dc8c46a5SAndrzej Pietrasiewicz 	ret = target_register_template(&usbg_ops);
2287dc8c46a5SAndrzej Pietrasiewicz 	if (ret)
2288dc8c46a5SAndrzej Pietrasiewicz 		usb_function_unregister(&tcmusb_func);
2289dc8c46a5SAndrzej Pietrasiewicz 
2290dc8c46a5SAndrzej Pietrasiewicz 	return ret;
2291dc8c46a5SAndrzej Pietrasiewicz }
2292dc8c46a5SAndrzej Pietrasiewicz module_init(tcm_init);
2293dc8c46a5SAndrzej Pietrasiewicz 
tcm_exit(void)2294dc8c46a5SAndrzej Pietrasiewicz static void __exit tcm_exit(void)
2295dc8c46a5SAndrzej Pietrasiewicz {
2296dc8c46a5SAndrzej Pietrasiewicz 	target_unregister_template(&usbg_ops);
22970b8b1a1fSJayshri Pawar 	usb_function_unregister(&tcmusb_func);
2298dc8c46a5SAndrzej Pietrasiewicz }
2299dc8c46a5SAndrzej Pietrasiewicz module_exit(tcm_exit);
2300dc8c46a5SAndrzej Pietrasiewicz 
2301dc8c46a5SAndrzej Pietrasiewicz MODULE_LICENSE("GPL");
2302dc8c46a5SAndrzej Pietrasiewicz MODULE_AUTHOR("Sebastian Andrzej Siewior");
2303dc8c46a5SAndrzej Pietrasiewicz