USB: fixes for v5.7-rc6
The main part here are the important fixes for the raw-gadget before it becomes an ABI. We're adding support for stall/halt/wedge which is actually pretty important in many situations. There's also a NULL pointer deref fix. Apart from raw-gadget, I've included some recent sparse fixes to a few drivers. Signed-off-by: Felipe Balbi <balbi@kernel.org> -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEElLzh7wn96CXwjh2IzL64meEamQYFAl6+X7kRHGJhbGJpQGtl cm5lbC5vcmcACgkQzL64meEamQa54hAAnXRX60sEZkd9ErE5oXuHOKKsRG5va9Jc In9lVxXr5xV/jxN0q8lbsR9nX3F3WODO/po6EUcrmSlo4JBH6WJZKf6HEm06oIL3 EjAuxXHuw8aBeS2kUFSvLYKtFg59u8KaD5lm/6zJO9bjVFduMTwTaxpLlkKbnXzd v0EaYbyh7uyoqz+DgElu2VG16HMje0BQgY7oGQORTsNikNTqxCOR1wjzjJvNWbQh JUC06qXLsTTvHtaW+uraLG0IGvYFLKr7snTEbVjMThllNvGHo0mWJeo3RGZxgHGV XcKgQAGuuzhmu00IviKXCIfGyCmQG1IN8+vjg3aI5mwY5BYkaBzLZjwYGHwJ2s9o T98FGPPgXkT4YWYUCIKD9HnJ111zmYrdIzi/7vbIQyTm17BE3gzz3oqw64DRi17u zOqIgGclkXGEAXzMZPVy2Rbcbcplmqaga+VbXK9uy5qD1q1Avjr4wG2aJoXIias7 9kGOXzbxRUMS6ReFE1eDAGAUrkNkRY+XoT1mlJWgQn57/7Lk+2mhi5GwTN6pnuuL Cjgi5K1E7ZzO05THbyU3P8EvDl2u2BYq/8IaqqXyPkalZ7blB1LEMsJXfwWv8tNK E4QuWnocwiRWqYwUEo++Xi42Acl5DVSRmhfKSDk1mybn3uvJW40dqCu42o2iPM33 SGydnp1Ezr4= =0P11 -----END PGP SIGNATURE----- Merge tag 'fixes-for-v5.7-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-linus Felipe writes: USB: fixes for v5.7-rc6 The main part here are the important fixes for the raw-gadget before it becomes an ABI. We're adding support for stall/halt/wedge which is actually pretty important in many situations. There's also a NULL pointer deref fix. Apart from raw-gadget, I've included some recent sparse fixes to a few drivers. Signed-off-by: Felipe Balbi <balbi@kernel.org> * tag 'fixes-for-v5.7-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb: usb: cdns3: gadget: make a bunch of functions static usb: mtu3: constify struct debugfs_reg32 usb: gadget: udc: atmel: Make some symbols static usb: raw-gadget: fix null-ptr-deref when reenabling endpoints usb: raw-gadget: documentation updates usb: raw-gadget: support stalling/halting/wedging endpoints usb: raw-gadget: fix gadget endpoint selection usb: raw-gadget: improve uapi headers comments
This commit is contained in:
Коммит
86e1cf7d17
|
@ -27,9 +27,8 @@ differences are:
|
|||
3. Raw Gadget provides a way to select a UDC device/driver to bind to,
|
||||
while GadgetFS currently binds to the first available UDC.
|
||||
|
||||
4. Raw Gadget uses predictable endpoint names (handles) across different
|
||||
UDCs (as long as UDCs have enough endpoints of each required transfer
|
||||
type).
|
||||
4. Raw Gadget explicitly exposes information about endpoints addresses and
|
||||
capabilities allowing a user to write UDC-agnostic gadgets.
|
||||
|
||||
5. Raw Gadget has ioctl-based interface instead of a filesystem-based one.
|
||||
|
||||
|
@ -50,12 +49,36 @@ The typical usage of Raw Gadget looks like:
|
|||
Raw Gadget and react to those depending on what kind of USB device
|
||||
needs to be emulated.
|
||||
|
||||
Note, that some UDC drivers have fixed addresses assigned to endpoints, and
|
||||
therefore arbitrary endpoint addresses can't be used in the descriptors.
|
||||
Nevertheles, Raw Gadget provides a UDC-agnostic way to write USB gadgets.
|
||||
Once a USB_RAW_EVENT_CONNECT event is received via USB_RAW_IOCTL_EVENT_FETCH,
|
||||
the USB_RAW_IOCTL_EPS_INFO ioctl can be used to find out information about
|
||||
endpoints that the UDC driver has. Based on that information, the user must
|
||||
chose UDC endpoints that will be used for the gadget being emulated, and
|
||||
properly assign addresses in endpoint descriptors.
|
||||
|
||||
You can find usage examples (along with a test suite) here:
|
||||
|
||||
https://github.com/xairy/raw-gadget
|
||||
|
||||
Internal details
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Currently every endpoint read/write ioctl submits a USB request and waits until
|
||||
its completion. This is the desired mode for coverage-guided fuzzing (as we'd
|
||||
like all USB request processing happen during the lifetime of a syscall),
|
||||
and must be kept in the implementation. (This might be slow for real world
|
||||
applications, thus the O_NONBLOCK improvement suggestion below.)
|
||||
|
||||
Potential future improvements
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- Implement ioctl's for setting/clearing halt status on endpoints.
|
||||
|
||||
- Reporting more events (suspend, resume, etc.) through
|
||||
USB_RAW_IOCTL_EVENT_FETCH.
|
||||
- Report more events (suspend, resume, etc.) through USB_RAW_IOCTL_EVENT_FETCH.
|
||||
|
||||
- Support O_NONBLOCK I/O.
|
||||
|
||||
- Support USB 3 features (accept SS endpoint companion descriptor when
|
||||
enabling endpoints; allow providing stream_id for bulk transfers).
|
||||
|
||||
- Support ISO transfer features (expose frame_number for completed requests).
|
||||
|
|
|
@ -82,7 +82,7 @@ static int cdns3_ep_run_stream_transfer(struct cdns3_endpoint *priv_ep,
|
|||
* @ptr: address of device controller register to be read and changed
|
||||
* @mask: bits requested to clar
|
||||
*/
|
||||
void cdns3_clear_register_bit(void __iomem *ptr, u32 mask)
|
||||
static void cdns3_clear_register_bit(void __iomem *ptr, u32 mask)
|
||||
{
|
||||
mask = readl(ptr) & ~mask;
|
||||
writel(mask, ptr);
|
||||
|
@ -137,7 +137,7 @@ struct usb_request *cdns3_next_request(struct list_head *list)
|
|||
*
|
||||
* Returns buffer or NULL if no buffers in list
|
||||
*/
|
||||
struct cdns3_aligned_buf *cdns3_next_align_buf(struct list_head *list)
|
||||
static struct cdns3_aligned_buf *cdns3_next_align_buf(struct list_head *list)
|
||||
{
|
||||
return list_first_entry_or_null(list, struct cdns3_aligned_buf, list);
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ struct cdns3_aligned_buf *cdns3_next_align_buf(struct list_head *list)
|
|||
*
|
||||
* Returns request or NULL if no requests in list
|
||||
*/
|
||||
struct cdns3_request *cdns3_next_priv_request(struct list_head *list)
|
||||
static struct cdns3_request *cdns3_next_priv_request(struct list_head *list)
|
||||
{
|
||||
return list_first_entry_or_null(list, struct cdns3_request, list);
|
||||
}
|
||||
|
@ -190,7 +190,7 @@ dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep,
|
|||
return priv_ep->trb_pool_dma + offset;
|
||||
}
|
||||
|
||||
int cdns3_ring_size(struct cdns3_endpoint *priv_ep)
|
||||
static int cdns3_ring_size(struct cdns3_endpoint *priv_ep)
|
||||
{
|
||||
switch (priv_ep->type) {
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
|
@ -345,7 +345,7 @@ static void cdns3_ep_inc_deq(struct cdns3_endpoint *priv_ep)
|
|||
cdns3_ep_inc_trb(&priv_ep->dequeue, &priv_ep->ccs, priv_ep->num_trbs);
|
||||
}
|
||||
|
||||
void cdns3_move_deq_to_next_trb(struct cdns3_request *priv_req)
|
||||
static void cdns3_move_deq_to_next_trb(struct cdns3_request *priv_req)
|
||||
{
|
||||
struct cdns3_endpoint *priv_ep = priv_req->priv_ep;
|
||||
int current_trb = priv_req->start_trb;
|
||||
|
@ -511,7 +511,7 @@ static void cdns3_wa2_descmiss_copy_data(struct cdns3_endpoint *priv_ep,
|
|||
}
|
||||
}
|
||||
|
||||
struct usb_request *cdns3_wa2_gadget_giveback(struct cdns3_device *priv_dev,
|
||||
static struct usb_request *cdns3_wa2_gadget_giveback(struct cdns3_device *priv_dev,
|
||||
struct cdns3_endpoint *priv_ep,
|
||||
struct cdns3_request *priv_req)
|
||||
{
|
||||
|
@ -551,7 +551,7 @@ struct usb_request *cdns3_wa2_gadget_giveback(struct cdns3_device *priv_dev,
|
|||
return &priv_req->request;
|
||||
}
|
||||
|
||||
int cdns3_wa2_gadget_ep_queue(struct cdns3_device *priv_dev,
|
||||
static int cdns3_wa2_gadget_ep_queue(struct cdns3_device *priv_dev,
|
||||
struct cdns3_endpoint *priv_ep,
|
||||
struct cdns3_request *priv_req)
|
||||
{
|
||||
|
@ -836,7 +836,7 @@ void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
|
|||
cdns3_gadget_ep_free_request(&priv_ep->endpoint, request);
|
||||
}
|
||||
|
||||
void cdns3_wa1_restore_cycle_bit(struct cdns3_endpoint *priv_ep)
|
||||
static void cdns3_wa1_restore_cycle_bit(struct cdns3_endpoint *priv_ep)
|
||||
{
|
||||
/* Work around for stale data address in TRB*/
|
||||
if (priv_ep->wa1_set) {
|
||||
|
@ -1904,7 +1904,7 @@ static int cdns3_ep_onchip_buffer_reserve(struct cdns3_device *priv_dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
void cdns3_stream_ep_reconfig(struct cdns3_device *priv_dev,
|
||||
static void cdns3_stream_ep_reconfig(struct cdns3_device *priv_dev,
|
||||
struct cdns3_endpoint *priv_ep)
|
||||
{
|
||||
if (!priv_ep->use_streams || priv_dev->gadget.speed < USB_SPEED_SUPER)
|
||||
|
@ -1925,7 +1925,7 @@ void cdns3_stream_ep_reconfig(struct cdns3_device *priv_dev,
|
|||
EP_CFG_TDL_CHK | EP_CFG_SID_CHK);
|
||||
}
|
||||
|
||||
void cdns3_configure_dmult(struct cdns3_device *priv_dev,
|
||||
static void cdns3_configure_dmult(struct cdns3_device *priv_dev,
|
||||
struct cdns3_endpoint *priv_ep)
|
||||
{
|
||||
struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kref.h>
|
||||
|
@ -123,8 +124,6 @@ static void raw_event_queue_destroy(struct raw_event_queue *queue)
|
|||
|
||||
struct raw_dev;
|
||||
|
||||
#define USB_RAW_MAX_ENDPOINTS 32
|
||||
|
||||
enum ep_state {
|
||||
STATE_EP_DISABLED,
|
||||
STATE_EP_ENABLED,
|
||||
|
@ -134,6 +133,7 @@ struct raw_ep {
|
|||
struct raw_dev *dev;
|
||||
enum ep_state state;
|
||||
struct usb_ep *ep;
|
||||
u8 addr;
|
||||
struct usb_request *req;
|
||||
bool urb_queued;
|
||||
bool disabling;
|
||||
|
@ -168,7 +168,8 @@ struct raw_dev {
|
|||
bool ep0_out_pending;
|
||||
bool ep0_urb_queued;
|
||||
ssize_t ep0_status;
|
||||
struct raw_ep eps[USB_RAW_MAX_ENDPOINTS];
|
||||
struct raw_ep eps[USB_RAW_EPS_NUM_MAX];
|
||||
int eps_num;
|
||||
|
||||
struct completion ep0_done;
|
||||
struct raw_event_queue queue;
|
||||
|
@ -202,8 +203,8 @@ static void dev_free(struct kref *kref)
|
|||
usb_ep_free_request(dev->gadget->ep0, dev->req);
|
||||
}
|
||||
raw_event_queue_destroy(&dev->queue);
|
||||
for (i = 0; i < USB_RAW_MAX_ENDPOINTS; i++) {
|
||||
if (dev->eps[i].state != STATE_EP_ENABLED)
|
||||
for (i = 0; i < dev->eps_num; i++) {
|
||||
if (dev->eps[i].state == STATE_EP_DISABLED)
|
||||
continue;
|
||||
usb_ep_disable(dev->eps[i].ep);
|
||||
usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
|
||||
|
@ -249,12 +250,26 @@ static void gadget_ep0_complete(struct usb_ep *ep, struct usb_request *req)
|
|||
complete(&dev->ep0_done);
|
||||
}
|
||||
|
||||
static u8 get_ep_addr(const char *name)
|
||||
{
|
||||
/* If the endpoint has fixed function (named as e.g. "ep12out-bulk"),
|
||||
* parse the endpoint address from its name. We deliberately use
|
||||
* deprecated simple_strtoul() function here, as the number isn't
|
||||
* followed by '\0' nor '\n'.
|
||||
*/
|
||||
if (isdigit(name[2]))
|
||||
return simple_strtoul(&name[2], NULL, 10);
|
||||
/* Otherwise the endpoint is configurable (named as e.g. "ep-a"). */
|
||||
return USB_RAW_EP_ADDR_ANY;
|
||||
}
|
||||
|
||||
static int gadget_bind(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
int ret = 0;
|
||||
int ret = 0, i = 0;
|
||||
struct raw_dev *dev = container_of(driver, struct raw_dev, driver);
|
||||
struct usb_request *req;
|
||||
struct usb_ep *ep;
|
||||
unsigned long flags;
|
||||
|
||||
if (strcmp(gadget->name, dev->udc_name) != 0)
|
||||
|
@ -273,6 +288,13 @@ static int gadget_bind(struct usb_gadget *gadget,
|
|||
dev->req->context = dev;
|
||||
dev->req->complete = gadget_ep0_complete;
|
||||
dev->gadget = gadget;
|
||||
gadget_for_each_ep(ep, dev->gadget) {
|
||||
dev->eps[i].ep = ep;
|
||||
dev->eps[i].addr = get_ep_addr(ep->name);
|
||||
dev->eps[i].state = STATE_EP_DISABLED;
|
||||
i++;
|
||||
}
|
||||
dev->eps_num = i;
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
/* Matches kref_put() in gadget_unbind(). */
|
||||
|
@ -555,7 +577,7 @@ static void *raw_alloc_io_data(struct usb_raw_ep_io *io, void __user *ptr,
|
|||
|
||||
if (copy_from_user(io, ptr, sizeof(*io)))
|
||||
return ERR_PTR(-EFAULT);
|
||||
if (io->ep >= USB_RAW_MAX_ENDPOINTS)
|
||||
if (io->ep >= USB_RAW_EPS_NUM_MAX)
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (!usb_raw_io_flags_valid(io->flags))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
@ -682,32 +704,48 @@ free:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool check_ep_caps(struct usb_ep *ep,
|
||||
struct usb_endpoint_descriptor *desc)
|
||||
static int raw_ioctl_ep0_stall(struct raw_dev *dev, unsigned long value)
|
||||
{
|
||||
switch (usb_endpoint_type(desc)) {
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
if (!ep->caps.type_iso)
|
||||
return false;
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
if (!ep->caps.type_bulk)
|
||||
return false;
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
if (!ep->caps.type_int)
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
|
||||
if (value)
|
||||
return -EINVAL;
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
if (dev->state != STATE_DEV_RUNNING) {
|
||||
dev_dbg(dev->dev, "fail, device is not running\n");
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
if (!dev->gadget) {
|
||||
dev_dbg(dev->dev, "fail, gadget is not bound\n");
|
||||
ret = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
if (dev->ep0_urb_queued) {
|
||||
dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
|
||||
ret = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
if (!dev->ep0_in_pending && !dev->ep0_out_pending) {
|
||||
dev_dbg(&dev->gadget->dev, "fail, no request pending\n");
|
||||
ret = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (usb_endpoint_dir_in(desc) && !ep->caps.dir_in)
|
||||
return false;
|
||||
if (usb_endpoint_dir_out(desc) && !ep->caps.dir_out)
|
||||
return false;
|
||||
ret = usb_ep_set_halt(dev->gadget->ep0);
|
||||
if (ret < 0)
|
||||
dev_err(&dev->gadget->dev,
|
||||
"fail, usb_ep_set_halt returned %d\n", ret);
|
||||
|
||||
return true;
|
||||
if (dev->ep0_in_pending)
|
||||
dev->ep0_in_pending = false;
|
||||
else
|
||||
dev->ep0_out_pending = false;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
|
||||
|
@ -715,7 +753,7 @@ static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
|
|||
int ret = 0, i;
|
||||
unsigned long flags;
|
||||
struct usb_endpoint_descriptor *desc;
|
||||
struct usb_ep *ep = NULL;
|
||||
struct raw_ep *ep;
|
||||
|
||||
desc = memdup_user((void __user *)value, sizeof(*desc));
|
||||
if (IS_ERR(desc))
|
||||
|
@ -743,41 +781,32 @@ static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
|
|||
goto out_free;
|
||||
}
|
||||
|
||||
for (i = 0; i < USB_RAW_MAX_ENDPOINTS; i++) {
|
||||
if (dev->eps[i].state == STATE_EP_ENABLED)
|
||||
for (i = 0; i < dev->eps_num; i++) {
|
||||
ep = &dev->eps[i];
|
||||
if (ep->state != STATE_EP_DISABLED)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if (i == USB_RAW_MAX_ENDPOINTS) {
|
||||
dev_dbg(&dev->gadget->dev,
|
||||
"fail, no device endpoints available\n");
|
||||
ret = -EBUSY;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
gadget_for_each_ep(ep, dev->gadget) {
|
||||
if (ep->enabled)
|
||||
if (ep->addr != usb_endpoint_num(desc) &&
|
||||
ep->addr != USB_RAW_EP_ADDR_ANY)
|
||||
continue;
|
||||
if (!check_ep_caps(ep, desc))
|
||||
if (!usb_gadget_ep_match_desc(dev->gadget, ep->ep, desc, NULL))
|
||||
continue;
|
||||
ep->desc = desc;
|
||||
ret = usb_ep_enable(ep);
|
||||
ep->ep->desc = desc;
|
||||
ret = usb_ep_enable(ep->ep);
|
||||
if (ret < 0) {
|
||||
dev_err(&dev->gadget->dev,
|
||||
"fail, usb_ep_enable returned %d\n", ret);
|
||||
goto out_free;
|
||||
}
|
||||
dev->eps[i].req = usb_ep_alloc_request(ep, GFP_ATOMIC);
|
||||
if (!dev->eps[i].req) {
|
||||
ep->req = usb_ep_alloc_request(ep->ep, GFP_ATOMIC);
|
||||
if (!ep->req) {
|
||||
dev_err(&dev->gadget->dev,
|
||||
"fail, usb_ep_alloc_request failed\n");
|
||||
usb_ep_disable(ep);
|
||||
usb_ep_disable(ep->ep);
|
||||
ret = -ENOMEM;
|
||||
goto out_free;
|
||||
}
|
||||
dev->eps[i].ep = ep;
|
||||
dev->eps[i].state = STATE_EP_ENABLED;
|
||||
ep->driver_data = &dev->eps[i];
|
||||
ep->state = STATE_EP_ENABLED;
|
||||
ep->ep->driver_data = ep;
|
||||
ret = i;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
@ -796,10 +825,6 @@ static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
|
|||
{
|
||||
int ret = 0, i = value;
|
||||
unsigned long flags;
|
||||
const void *desc;
|
||||
|
||||
if (i < 0 || i >= USB_RAW_MAX_ENDPOINTS)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
if (dev->state != STATE_DEV_RUNNING) {
|
||||
|
@ -812,7 +837,12 @@ static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
|
|||
ret = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
if (dev->eps[i].state != STATE_EP_ENABLED) {
|
||||
if (i < 0 || i >= dev->eps_num) {
|
||||
dev_dbg(dev->dev, "fail, invalid endpoint\n");
|
||||
ret = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
if (dev->eps[i].state == STATE_EP_DISABLED) {
|
||||
dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
|
@ -836,10 +866,8 @@ static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
|
|||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
|
||||
desc = dev->eps[i].ep->desc;
|
||||
dev->eps[i].ep = NULL;
|
||||
kfree(dev->eps[i].ep->desc);
|
||||
dev->eps[i].state = STATE_EP_DISABLED;
|
||||
kfree(desc);
|
||||
dev->eps[i].disabling = false;
|
||||
|
||||
out_unlock:
|
||||
|
@ -847,6 +875,74 @@ out_unlock:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int raw_ioctl_ep_set_clear_halt_wedge(struct raw_dev *dev,
|
||||
unsigned long value, bool set, bool halt)
|
||||
{
|
||||
int ret = 0, i = value;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
if (dev->state != STATE_DEV_RUNNING) {
|
||||
dev_dbg(dev->dev, "fail, device is not running\n");
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
if (!dev->gadget) {
|
||||
dev_dbg(dev->dev, "fail, gadget is not bound\n");
|
||||
ret = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
if (i < 0 || i >= dev->eps_num) {
|
||||
dev_dbg(dev->dev, "fail, invalid endpoint\n");
|
||||
ret = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
if (dev->eps[i].state == STATE_EP_DISABLED) {
|
||||
dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
if (dev->eps[i].disabling) {
|
||||
dev_dbg(&dev->gadget->dev,
|
||||
"fail, disable is in progress\n");
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
if (dev->eps[i].urb_queued) {
|
||||
dev_dbg(&dev->gadget->dev,
|
||||
"fail, waiting for urb completion\n");
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
if (usb_endpoint_xfer_isoc(dev->eps[i].ep->desc)) {
|
||||
dev_dbg(&dev->gadget->dev,
|
||||
"fail, can't halt/wedge ISO endpoint\n");
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (set && halt) {
|
||||
ret = usb_ep_set_halt(dev->eps[i].ep);
|
||||
if (ret < 0)
|
||||
dev_err(&dev->gadget->dev,
|
||||
"fail, usb_ep_set_halt returned %d\n", ret);
|
||||
} else if (!set && halt) {
|
||||
ret = usb_ep_clear_halt(dev->eps[i].ep);
|
||||
if (ret < 0)
|
||||
dev_err(&dev->gadget->dev,
|
||||
"fail, usb_ep_clear_halt returned %d\n", ret);
|
||||
} else if (set && !halt) {
|
||||
ret = usb_ep_set_wedge(dev->eps[i].ep);
|
||||
if (ret < 0)
|
||||
dev_err(&dev->gadget->dev,
|
||||
"fail, usb_ep_set_wedge returned %d\n", ret);
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void gadget_ep_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct raw_ep *r_ep = (struct raw_ep *)ep->driver_data;
|
||||
|
@ -868,7 +964,7 @@ static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
|
|||
{
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
struct raw_ep *ep = &dev->eps[io->ep];
|
||||
struct raw_ep *ep;
|
||||
DECLARE_COMPLETION_ONSTACK(done);
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
|
@ -882,6 +978,12 @@ static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
|
|||
ret = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
if (io->ep >= dev->eps_num) {
|
||||
dev_dbg(&dev->gadget->dev, "fail, invalid endpoint\n");
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
ep = &dev->eps[io->ep];
|
||||
if (ep->state != STATE_EP_ENABLED) {
|
||||
dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
|
||||
ret = -EBUSY;
|
||||
|
@ -1027,6 +1129,71 @@ out_unlock:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void fill_ep_caps(struct usb_ep_caps *caps,
|
||||
struct usb_raw_ep_caps *raw_caps)
|
||||
{
|
||||
raw_caps->type_control = caps->type_control;
|
||||
raw_caps->type_iso = caps->type_iso;
|
||||
raw_caps->type_bulk = caps->type_bulk;
|
||||
raw_caps->type_int = caps->type_int;
|
||||
raw_caps->dir_in = caps->dir_in;
|
||||
raw_caps->dir_out = caps->dir_out;
|
||||
}
|
||||
|
||||
static void fill_ep_limits(struct usb_ep *ep, struct usb_raw_ep_limits *limits)
|
||||
{
|
||||
limits->maxpacket_limit = ep->maxpacket_limit;
|
||||
limits->max_streams = ep->max_streams;
|
||||
}
|
||||
|
||||
static int raw_ioctl_eps_info(struct raw_dev *dev, unsigned long value)
|
||||
{
|
||||
int ret = 0, i;
|
||||
unsigned long flags;
|
||||
struct usb_raw_eps_info *info;
|
||||
struct raw_ep *ep;
|
||||
|
||||
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
if (dev->state != STATE_DEV_RUNNING) {
|
||||
dev_dbg(dev->dev, "fail, device is not running\n");
|
||||
ret = -EINVAL;
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
goto out_free;
|
||||
}
|
||||
if (!dev->gadget) {
|
||||
dev_dbg(dev->dev, "fail, gadget is not bound\n");
|
||||
ret = -EBUSY;
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
memset(info, 0, sizeof(*info));
|
||||
for (i = 0; i < dev->eps_num; i++) {
|
||||
ep = &dev->eps[i];
|
||||
strscpy(&info->eps[i].name[0], ep->ep->name,
|
||||
USB_RAW_EP_NAME_MAX);
|
||||
info->eps[i].addr = ep->addr;
|
||||
fill_ep_caps(&ep->ep->caps, &info->eps[i].caps);
|
||||
fill_ep_limits(ep->ep, &info->eps[i].limits);
|
||||
}
|
||||
ret = dev->eps_num;
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
if (copy_to_user((void __user *)value, info, sizeof(*info)))
|
||||
ret = -EFAULT;
|
||||
|
||||
out_free:
|
||||
kfree(info);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long raw_ioctl(struct file *fd, unsigned int cmd, unsigned long value)
|
||||
{
|
||||
struct raw_dev *dev = fd->private_data;
|
||||
|
@ -1069,6 +1236,24 @@ static long raw_ioctl(struct file *fd, unsigned int cmd, unsigned long value)
|
|||
case USB_RAW_IOCTL_VBUS_DRAW:
|
||||
ret = raw_ioctl_vbus_draw(dev, value);
|
||||
break;
|
||||
case USB_RAW_IOCTL_EPS_INFO:
|
||||
ret = raw_ioctl_eps_info(dev, value);
|
||||
break;
|
||||
case USB_RAW_IOCTL_EP0_STALL:
|
||||
ret = raw_ioctl_ep0_stall(dev, value);
|
||||
break;
|
||||
case USB_RAW_IOCTL_EP_SET_HALT:
|
||||
ret = raw_ioctl_ep_set_clear_halt_wedge(
|
||||
dev, value, true, true);
|
||||
break;
|
||||
case USB_RAW_IOCTL_EP_CLEAR_HALT:
|
||||
ret = raw_ioctl_ep_set_clear_halt_wedge(
|
||||
dev, value, false, true);
|
||||
break;
|
||||
case USB_RAW_IOCTL_EP_SET_WEDGE:
|
||||
ret = raw_ioctl_ep_set_clear_halt_wedge(
|
||||
dev, value, true, false);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
|
|
@ -185,7 +185,7 @@ static int regs_dbg_release(struct inode *inode, struct file *file)
|
|||
return 0;
|
||||
}
|
||||
|
||||
const struct file_operations queue_dbg_fops = {
|
||||
static const struct file_operations queue_dbg_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = queue_dbg_open,
|
||||
.llseek = no_llseek,
|
||||
|
@ -193,7 +193,7 @@ const struct file_operations queue_dbg_fops = {
|
|||
.release = queue_dbg_release,
|
||||
};
|
||||
|
||||
const struct file_operations regs_dbg_fops = {
|
||||
static const struct file_operations regs_dbg_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = regs_dbg_open,
|
||||
.llseek = generic_file_llseek,
|
||||
|
|
|
@ -276,7 +276,7 @@ static const struct file_operations mtu3_ep_fops = {
|
|||
.release = single_release,
|
||||
};
|
||||
|
||||
static struct debugfs_reg32 mtu3_prb_regs[] = {
|
||||
static const struct debugfs_reg32 mtu3_prb_regs[] = {
|
||||
dump_prb_reg("enable", U3D_SSUSB_PRB_CTRL0),
|
||||
dump_prb_reg("byte-sell", U3D_SSUSB_PRB_CTRL1),
|
||||
dump_prb_reg("byte-selh", U3D_SSUSB_PRB_CTRL2),
|
||||
|
@ -349,7 +349,7 @@ static const struct file_operations mtu3_probe_fops = {
|
|||
static void mtu3_debugfs_create_prb_files(struct mtu3 *mtu)
|
||||
{
|
||||
struct ssusb_mtk *ssusb = mtu->ssusb;
|
||||
struct debugfs_reg32 *regs;
|
||||
const struct debugfs_reg32 *regs;
|
||||
struct dentry *dir_prb;
|
||||
int i;
|
||||
|
||||
|
|
|
@ -93,6 +93,64 @@ struct usb_raw_ep_io {
|
|||
__u8 data[0];
|
||||
};
|
||||
|
||||
/* Maximum number of non-control endpoints in struct usb_raw_eps_info. */
|
||||
#define USB_RAW_EPS_NUM_MAX 30
|
||||
|
||||
/* Maximum length of UDC endpoint name in struct usb_raw_ep_info. */
|
||||
#define USB_RAW_EP_NAME_MAX 16
|
||||
|
||||
/* Used as addr in struct usb_raw_ep_info if endpoint accepts any address. */
|
||||
#define USB_RAW_EP_ADDR_ANY 0xff
|
||||
|
||||
/*
|
||||
* struct usb_raw_ep_caps - exposes endpoint capabilities from struct usb_ep
|
||||
* (technically from its member struct usb_ep_caps).
|
||||
*/
|
||||
struct usb_raw_ep_caps {
|
||||
__u32 type_control : 1;
|
||||
__u32 type_iso : 1;
|
||||
__u32 type_bulk : 1;
|
||||
__u32 type_int : 1;
|
||||
__u32 dir_in : 1;
|
||||
__u32 dir_out : 1;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct usb_raw_ep_limits - exposes endpoint limits from struct usb_ep.
|
||||
* @maxpacket_limit: Maximum packet size value supported by this endpoint.
|
||||
* @max_streams: maximum number of streams supported by this endpoint
|
||||
* (actual number is 2^n).
|
||||
* @reserved: Empty, reserved for potential future extensions.
|
||||
*/
|
||||
struct usb_raw_ep_limits {
|
||||
__u16 maxpacket_limit;
|
||||
__u16 max_streams;
|
||||
__u32 reserved;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct usb_raw_ep_info - stores information about a gadget endpoint.
|
||||
* @name: Name of the endpoint as it is defined in the UDC driver.
|
||||
* @addr: Address of the endpoint that must be specified in the endpoint
|
||||
* descriptor passed to USB_RAW_IOCTL_EP_ENABLE ioctl.
|
||||
* @caps: Endpoint capabilities.
|
||||
* @limits: Endpoint limits.
|
||||
*/
|
||||
struct usb_raw_ep_info {
|
||||
__u8 name[USB_RAW_EP_NAME_MAX];
|
||||
__u32 addr;
|
||||
struct usb_raw_ep_caps caps;
|
||||
struct usb_raw_ep_limits limits;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct usb_raw_eps_info - argument for USB_RAW_IOCTL_EPS_INFO ioctl.
|
||||
* eps: Structures that store information about non-control endpoints.
|
||||
*/
|
||||
struct usb_raw_eps_info {
|
||||
struct usb_raw_ep_info eps[USB_RAW_EPS_NUM_MAX];
|
||||
};
|
||||
|
||||
/*
|
||||
* Initializes a Raw Gadget instance.
|
||||
* Accepts a pointer to the usb_raw_init struct as an argument.
|
||||
|
@ -115,37 +173,38 @@ struct usb_raw_ep_io {
|
|||
#define USB_RAW_IOCTL_EVENT_FETCH _IOR('U', 2, struct usb_raw_event)
|
||||
|
||||
/*
|
||||
* Queues an IN (OUT for READ) urb as a response to the last control request
|
||||
* received on endpoint 0, provided that was an IN (OUT for READ) request and
|
||||
* waits until the urb is completed. Copies received data to user for READ.
|
||||
* Queues an IN (OUT for READ) request as a response to the last setup request
|
||||
* received on endpoint 0 (provided that was an IN (OUT for READ) request), and
|
||||
* waits until the request is completed. Copies received data to user for READ.
|
||||
* Accepts a pointer to the usb_raw_ep_io struct as an argument.
|
||||
* Returns length of trasferred data on success or negative error code on
|
||||
* Returns length of transferred data on success or negative error code on
|
||||
* failure.
|
||||
*/
|
||||
#define USB_RAW_IOCTL_EP0_WRITE _IOW('U', 3, struct usb_raw_ep_io)
|
||||
#define USB_RAW_IOCTL_EP0_READ _IOWR('U', 4, struct usb_raw_ep_io)
|
||||
|
||||
/*
|
||||
* Finds an endpoint that supports the transfer type specified in the
|
||||
* descriptor and enables it.
|
||||
* Accepts a pointer to the usb_endpoint_descriptor struct as an argument.
|
||||
* Finds an endpoint that satisfies the parameters specified in the provided
|
||||
* descriptors (address, transfer type, etc.) and enables it.
|
||||
* Accepts a pointer to the usb_raw_ep_descs struct as an argument.
|
||||
* Returns enabled endpoint handle on success or negative error code on failure.
|
||||
*/
|
||||
#define USB_RAW_IOCTL_EP_ENABLE _IOW('U', 5, struct usb_endpoint_descriptor)
|
||||
|
||||
/* Disables specified endpoint.
|
||||
/*
|
||||
* Disables specified endpoint.
|
||||
* Accepts endpoint handle as an argument.
|
||||
* Returns 0 on success or negative error code on failure.
|
||||
*/
|
||||
#define USB_RAW_IOCTL_EP_DISABLE _IOW('U', 6, __u32)
|
||||
|
||||
/*
|
||||
* Queues an IN (OUT for READ) urb as a response to the last control request
|
||||
* received on endpoint usb_raw_ep_io.ep, provided that was an IN (OUT for READ)
|
||||
* request and waits until the urb is completed. Copies received data to user
|
||||
* for READ.
|
||||
* Queues an IN (OUT for READ) request as a response to the last setup request
|
||||
* received on endpoint usb_raw_ep_io.ep (provided that was an IN (OUT for READ)
|
||||
* request), and waits until the request is completed. Copies received data to
|
||||
* user for READ.
|
||||
* Accepts a pointer to the usb_raw_ep_io struct as an argument.
|
||||
* Returns length of trasferred data on success or negative error code on
|
||||
* Returns length of transferred data on success or negative error code on
|
||||
* failure.
|
||||
*/
|
||||
#define USB_RAW_IOCTL_EP_WRITE _IOW('U', 7, struct usb_raw_ep_io)
|
||||
|
@ -164,4 +223,27 @@ struct usb_raw_ep_io {
|
|||
*/
|
||||
#define USB_RAW_IOCTL_VBUS_DRAW _IOW('U', 10, __u32)
|
||||
|
||||
/*
|
||||
* Fills in the usb_raw_eps_info structure with information about non-control
|
||||
* endpoints available for the currently connected UDC.
|
||||
* Returns the number of available endpoints on success or negative error code
|
||||
* on failure.
|
||||
*/
|
||||
#define USB_RAW_IOCTL_EPS_INFO _IOR('U', 11, struct usb_raw_eps_info)
|
||||
|
||||
/*
|
||||
* Stalls a pending control request on endpoint 0.
|
||||
* Returns 0 on success or negative error code on failure.
|
||||
*/
|
||||
#define USB_RAW_IOCTL_EP0_STALL _IO('U', 12)
|
||||
|
||||
/*
|
||||
* Sets or clears halt or wedge status of the endpoint.
|
||||
* Accepts endpoint handle as an argument.
|
||||
* Returns 0 on success or negative error code on failure.
|
||||
*/
|
||||
#define USB_RAW_IOCTL_EP_SET_HALT _IOW('U', 13, __u32)
|
||||
#define USB_RAW_IOCTL_EP_CLEAR_HALT _IOW('U', 14, __u32)
|
||||
#define USB_RAW_IOCTL_EP_SET_WEDGE _IOW('U', 15, __u32)
|
||||
|
||||
#endif /* _UAPI__LINUX_USB_RAW_GADGET_H */
|
||||
|
|
Загрузка…
Ссылка в новой задаче