From nobody Mon Apr 6 15:45:06 2026 Received: from metis.whiteo.stw.pengutronix.de (metis.whiteo.stw.pengutronix.de [185.203.201.7]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7FAFA3AE6F4 for ; Thu, 19 Mar 2026 09:36:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.203.201.7 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773912978; cv=none; b=l6Njxi7Ggw6qov5cznsZCaN4ElThfCxmJqC9W3whxFtIk5jV4CO/3wvA1w2FwxlfXBqBAALMe/ncig5aPL4YAhoiYwXlxHI5uGsCR+uK20jmQHzkbs8/tEL2OfwXfSrQFYng2QPt4Pgbk5niEE03lqdQDPrb8yyaRAzRwUqWQW0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773912978; c=relaxed/simple; bh=D48QkosbP3dN6C+gVRCxkYXxDKA7n5rlntVO57LmSiw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=JKEDK3A2R+fd8LraaFX1mLvUEbchFJiY4e5olmSN1cC44zO4VXv3ynnb/eXasbIovaZard24hifLVZfDXJvt7r9cM1f03f9QID62Fex+fU3qcucOMjT5hc0rp/JyYYNj5GEBRrBKp2FGAbRI/q1cGlFPtgUUO/w7SYPDV6JxueE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=pengutronix.de; spf=pass smtp.mailfrom=pengutronix.de; arc=none smtp.client-ip=185.203.201.7 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=pengutronix.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=pengutronix.de Received: from drehscheibe.grey.stw.pengutronix.de ([2a0a:edc0:0:c01:1d::a2]) by metis.whiteo.stw.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1w39nV-0003OF-OQ; Thu, 19 Mar 2026 10:36:09 +0100 Received: from dude04.red.stw.pengutronix.de ([2a0a:edc0:0:1101:1d::ac] helo=dude04) by drehscheibe.grey.stw.pengutronix.de with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1w39nV-0013Dg-0z; Thu, 19 Mar 2026 10:36:09 +0100 Received: from [::1] (helo=dude04.red.stw.pengutronix.de) by dude04 with esmtp (Exim 4.98.2) (envelope-from ) id 1w39nV-00000008yzO-0b49; Thu, 19 Mar 2026 10:36:09 +0100 From: Michael Grzeschik Date: Thu, 19 Mar 2026 10:36:04 +0100 Subject: [PATCH 07/11] net/9p/usbg: add extra interface for status change Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260319-9pfixes-v1-7-c977a7433185@pengutronix.de> References: <20260319-9pfixes-v1-0-c977a7433185@pengutronix.de> In-Reply-To: <20260319-9pfixes-v1-0-c977a7433185@pengutronix.de> To: Eric Van Hensbergen , Latchesar Ionkov , Dominique Martinet , Christian Schoenebeck , Greg Kroah-Hartman , Hyungjung Joo Cc: v9fs@lists.linux.dev, linux-kernel@vger.kernel.org, kernel@pengutronix.de, Michael Grzeschik X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=9584; i=m.grzeschik@pengutronix.de; h=from:subject:message-id; bh=D48QkosbP3dN6C+gVRCxkYXxDKA7n5rlntVO57LmSiw=; b=owEBbQKS/ZANAwAKAb9pWET5cfSrAcsmYgBpu8OHjv6o5kvErDrP+cdn31qoZGCFdANLpa7v9 pCDd14fZaCJAjMEAAEKAB0WIQQV2+2Fpbqd6fvv0Gi/aVhE+XH0qwUCabvDhwAKCRC/aVhE+XH0 q77wEACIGiYa06Ac1ocsjNtJEy61cRg3r0y3eKlwQRsXbpbm/vcOkgPpDayWuaTFNK2KNksdFHh E/O9wjTHbiljH3EO9wTzjxqRQ/vS2C8K/mxzgLUbgLOGVZTVhFXwzWi25i+UvlW07gX1ZT3t3j1 bXelE6qX69TcUnfVzAbuvQ6s/nLWziUeHRT2ZqJiEYlc15fqtZI7u/KlLnQLzB5TokY18Ct0Hor cKcZ1xq0IzvZGGDBM9rBvhQIcd6HpyjQRL3roIyPjLHTruOFVRXUOWcISShiQHwGtz5ATKLIlwE vyeeJOU094XXgdUTuHyZ8H5La8duQXiwn44HSrurzMAvwjyn45SBA4LA7+hcy8da7yQjs8ohgMJ t9A4J2vgN5wZfbHdf4TVAEf4WLgobAc4PwfGAvYFaL7ALStb7yrM4Og+7leywR9X6R02070YfeK KCfhQ6KY/DpBpXuLA9AN/2FzQsoR59KKPDUOdvjtK+4Vq7Sj78m9sKtJK1YnUIN1kZjueDvgkOw 8QFuKvp0u35yw+MOZRym5TxtS+xJ42eAmsBggp0OmJNrQQgn48ei/zDnCln1PJt7VscDzyHaWwg 8wXu+jyhx+sPHmr+6+3wZxuAnPNAg/WK6kMsaw4OY6IDrBNS0nNpVFde8AaQHRNXjb6rqiDjFSB bCtAjvKWxgNwNzQ== X-Developer-Key: i=m.grzeschik@pengutronix.de; a=openpgp; fpr=957BC452CE953D7EA60CF4FC0BE9E3157A1E2C64 X-SA-Exim-Connect-IP: 2a0a:edc0:0:c01:1d::a2 X-SA-Exim-Mail-From: m.grzeschik@pengutronix.de X-SA-Exim-Scanned: No (on metis.whiteo.stw.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-kernel@vger.kernel.org In the current implementation of the trans_usbg the status "connected", when all components are ready to transfer, is not well chosen. For now the 9p_usbg_create function looks if the in_ep is enabled, if not, it sets the 9p as disconnected. On the other side when the set_alt function calls ep_enable it checks if usbg_create was already called and client is set, if so it then sets the client as connected. With the Disconnected 9pfs layer a mount would always fail. However, after the set_alt to 0, the mount would still fail. Since it is unsure if there is actually something running to transfer data. For this constraint to overcome, the trans_usbg is working with an completion in the p9_usbg_request callback. This has many limitations, since from the usb perspective it is true that the wire connection is triggering the initial set_alt to 0 and therefor can tell the 9pfs protocol that the instance now is physically connected. This still does not tell anything about the connection and availability of the 9pfs transport that is providing the protocol over the usb. To solve this issue, this patch is adding an extra interface, that actually is containing the endpoints which will transfer the data in an separate alt mode. This is likewise to other standards as e.g. the ncm gadget. The 9p transport host, that will make the 9pfs protocol available over the two endpoints will then have to call set_alt(1) on this data interface. With this approach the gadget has the proper status change and knows when to expect to receive data via the endpoints. - don't allow set_alt when client is not ready Avoid letting the endpoints to be enabled, when the client is not yet prepared. Signed-off-by: Michael Grzeschik --- net/9p/trans_usbg.c | 119 ++++++++++++++++++++++++++++++++++++++++++++----= ---- 1 file changed, 101 insertions(+), 18 deletions(-) diff --git a/net/9p/trans_usbg.c b/net/9p/trans_usbg.c index c764e06ad4bf7ef7266160b06063a4a98226649c..5c954749ddba23d03eed85b9f9f= 94add9c5f9bf4 100644 --- a/net/9p/trans_usbg.c +++ b/net/9p/trans_usbg.c @@ -40,6 +40,7 @@ struct f_usb9pfs { struct p9_client *client; =20 + u8 ctrl_id, data_id; /* 9p request lock for en/dequeue */ spinlock_t lock; =20 @@ -85,6 +86,12 @@ struct f_usb9pfs_dev { struct list_head usb9pfs_instance; }; =20 +static inline struct f_usb9pfs_dev *usb9pfs_to_usb9pfs_dev(struct f_usb9pf= s *usb9pfs) +{ + return container_of(usb9pfs->function.fi, + struct f_usb9pfs_opts, func_inst)->dev; +} + static DEFINE_MUTEX(usb9pfs_lock); static struct list_head usbg_instance_list; =20 @@ -364,9 +371,11 @@ static int enable_endpoint(struct usb_composite_dev *c= dev, } =20 static int -enable_usb9pfs(struct usb_composite_dev *cdev, struct f_usb9pfs *usb9pfs) +enable_usb9pfs(struct f_usb9pfs *usb9pfs) { - struct p9_client *client; + struct usb_composite_dev *cdev =3D + usb9pfs->function.config->cdev; + int ret =3D 0; =20 ret =3D enable_endpoint(cdev, usb9pfs, usb9pfs->in_ep); @@ -381,10 +390,6 @@ enable_usb9pfs(struct usb_composite_dev *cdev, struct = f_usb9pfs *usb9pfs) if (ret) goto disable_out; =20 - client =3D usb9pfs->client; - if (client) - client->status =3D Connected; - dev_dbg(&cdev->gadget->dev, "%s enabled\n", usb9pfs->function.name); return 0; =20 @@ -431,17 +436,13 @@ static int p9_usbg_create(struct p9_client *client, s= truct fs_context *fc) return -EINVAL; =20 client->trans =3D (void *)usb9pfs; - if (!usb9pfs->in_req) - client->status =3D Disconnected; - else - client->status =3D Connected; + client->status =3D Connected; spin_lock_irq(&usb9pfs->lock); usb9pfs->client =3D client; spin_unlock_irq(&usb9pfs->lock); =20 client->trans_mod->maxsize =3D usb9pfs->buflen; =20 - complete(&usb9pfs->send); =20 return 0; } @@ -563,7 +564,7 @@ static struct usb_interface_descriptor usb9pfs_intf =3D= { .bLength =3D sizeof(usb9pfs_intf), .bDescriptorType =3D USB_DT_INTERFACE, =20 - .bNumEndpoints =3D 2, + .bNumEndpoints =3D 0, .bInterfaceClass =3D USB_CLASS_VENDOR_SPEC, .bInterfaceSubClass =3D USB_SUBCLASS_VENDOR_SPEC, .bInterfaceProtocol =3D USB_PROTOCOL_9PFS, @@ -571,6 +572,36 @@ static struct usb_interface_descriptor usb9pfs_intf = =3D { /* .iInterface =3D DYNAMIC */ }; =20 +/* the default data interface has no endpoints ... */ + +static struct usb_interface_descriptor usb9pfs_data_nop_intf =3D { + .bLength =3D sizeof(usb9pfs_data_nop_intf), + .bDescriptorType =3D USB_DT_INTERFACE, + + .bInterfaceNumber =3D 1, + .bAlternateSetting =3D 0, + .bNumEndpoints =3D 0, + .bInterfaceClass =3D USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass =3D USB_SUBCLASS_VENDOR_SPEC, + .bInterfaceProtocol =3D USB_PROTOCOL_9PFS, + /* .iInterface =3D DYNAMIC */ +}; + +/* ... but the "real" data interface has two bulk endpoints */ + +static struct usb_interface_descriptor usb9pfs_data_intf =3D { + .bLength =3D sizeof(usb9pfs_data_intf), + .bDescriptorType =3D USB_DT_INTERFACE, + + .bInterfaceNumber =3D 1, + .bAlternateSetting =3D 1, + .bNumEndpoints =3D 2, + .bInterfaceClass =3D USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass =3D USB_SUBCLASS_VENDOR_SPEC, + .bInterfaceProtocol =3D USB_PROTOCOL_9PFS, + /* .iInterface =3D DYNAMIC */ +}; + /* full speed support: */ =20 static struct usb_endpoint_descriptor fs_usb9pfs_source_desc =3D { @@ -591,6 +622,8 @@ static struct usb_endpoint_descriptor fs_usb9pfs_sink_d= esc =3D { =20 static struct usb_descriptor_header *fs_usb9pfs_descs[] =3D { (struct usb_descriptor_header *)&usb9pfs_intf, + (struct usb_descriptor_header *)&usb9pfs_data_nop_intf, + (struct usb_descriptor_header *)&usb9pfs_data_intf, (struct usb_descriptor_header *)&fs_usb9pfs_sink_desc, (struct usb_descriptor_header *)&fs_usb9pfs_source_desc, NULL, @@ -616,6 +649,8 @@ static struct usb_endpoint_descriptor hs_usb9pfs_sink_d= esc =3D { =20 static struct usb_descriptor_header *hs_usb9pfs_descs[] =3D { (struct usb_descriptor_header *)&usb9pfs_intf, + (struct usb_descriptor_header *)&usb9pfs_data_nop_intf, + (struct usb_descriptor_header *)&usb9pfs_data_intf, (struct usb_descriptor_header *)&hs_usb9pfs_source_desc, (struct usb_descriptor_header *)&hs_usb9pfs_sink_desc, NULL, @@ -657,6 +692,8 @@ static struct usb_ss_ep_comp_descriptor ss_usb9pfs_sink= _comp_desc =3D { =20 static struct usb_descriptor_header *ss_usb9pfs_descs[] =3D { (struct usb_descriptor_header *)&usb9pfs_intf, + (struct usb_descriptor_header *)&usb9pfs_data_nop_intf, + (struct usb_descriptor_header *)&usb9pfs_data_intf, (struct usb_descriptor_header *)&ss_usb9pfs_source_desc, (struct usb_descriptor_header *)&ss_usb9pfs_source_comp_desc, (struct usb_descriptor_header *)&ss_usb9pfs_sink_desc, @@ -666,7 +703,9 @@ static struct usb_descriptor_header *ss_usb9pfs_descs[]= =3D { =20 /* function-specific strings: */ static struct usb_string strings_usb9pfs[] =3D { - [0].s =3D "usb9pfs input to output", + [0].s =3D "usb9pfs control", + [1].s =3D "usb9pfs nop", + [2].s =3D "usb9pfs data", { } /* end of list */ }; =20 @@ -688,20 +727,34 @@ static int usb9pfs_func_bind(struct usb_configuration= *c, struct f_usb9pfs *usb9pfs =3D func_to_usb9pfs(f); struct f_usb9pfs_opts *opts; struct usb_composite_dev *cdev =3D c->cdev; + struct usb_string *us; int ret; int id; =20 + us =3D usb_gstrings_attach(cdev, usb9pfs_strings, + ARRAY_SIZE(strings_usb9pfs)); + if (IS_ERR(us)) + return PTR_ERR(us); + + usb9pfs_intf.iInterface =3D us[0].id; + usb9pfs_data_nop_intf.iInterface =3D us[1].id; + usb9pfs_data_intf.iInterface =3D us[2].id; + /* allocate interface ID(s) */ id =3D usb_interface_id(c, f); if (id < 0) return id; + + usb9pfs->ctrl_id =3D id; usb9pfs_intf.bInterfaceNumber =3D id; =20 - id =3D usb_string_id(cdev); + id =3D usb_interface_id(c, f); if (id < 0) return id; - strings_usb9pfs[0].id =3D id; - usb9pfs_intf.iInterface =3D id; + + usb9pfs->data_id =3D id; + usb9pfs_data_nop_intf.bInterfaceNumber =3D id; + usb9pfs_data_intf.bInterfaceNumber =3D id; =20 /* allocate endpoints */ usb9pfs->in_ep =3D usb_ep_autoconfig(cdev->gadget, @@ -775,9 +828,38 @@ static int usb9pfs_set_alt(struct usb_function *f, unsigned int intf, unsigned int alt) { struct f_usb9pfs *usb9pfs =3D func_to_usb9pfs(f); - struct usb_composite_dev *cdev =3D f->config->cdev; + int ret; + + if (!alt) { + if (intf =3D=3D usb9pfs->data_id && + usb9pfs->in_ep->enabled) + disable_usb9pfs(usb9pfs); + return 0; + } + + if (!usb9pfs->client || !usb9pfs_to_usb9pfs_dev(usb9pfs)->inuse) + return -EINVAL; + + ret =3D enable_usb9pfs(usb9pfs); + if (ret) + return ret; + + complete(&usb9pfs->send); + + return 0; +} + +/* + * Because the data interface supports multiple altsettings, + * this 9pfs function *MUST* implement a get_alt() method. + */ +static int usb9pfs_get_alt(struct usb_function *f, unsigned int intf) +{ + struct f_usb9pfs *usb9pfs =3D func_to_usb9pfs(f); =20 - return enable_usb9pfs(cdev, usb9pfs); + if (intf =3D=3D usb9pfs->ctrl_id) + return 0; + return usb9pfs->in_ep->enabled ? 1 : 0; } =20 static void usb9pfs_disable(struct usb_function *f) @@ -818,6 +900,7 @@ static struct usb_function *usb9pfs_alloc(struct usb_fu= nction_instance *fi) usb9pfs->function.bind =3D usb9pfs_func_bind; usb9pfs->function.unbind =3D usb9pfs_func_unbind; usb9pfs->function.set_alt =3D usb9pfs_set_alt; + usb9pfs->function.get_alt =3D usb9pfs_get_alt; usb9pfs->function.disable =3D usb9pfs_disable; usb9pfs->function.strings =3D usb9pfs_strings; =20 --=20 2.47.3