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