]> Git Repo - J-linux.git/blobdiff - drivers/usb/core/message.c
Merge tag 'dma-mapping-5.10' of git://git.infradead.org/users/hch/dma-mapping
[J-linux.git] / drivers / usb / core / message.c
index 7d6bbbf4a916aa505b866b14b7951fb18a081671..19ebb542befcb5846f4de69d257c6f263d10637b 100644 (file)
@@ -162,6 +162,143 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
 }
 EXPORT_SYMBOL_GPL(usb_control_msg);
 
+/**
+ * usb_control_msg_send - Builds a control "send" message, sends it off and waits for completion
+ * @dev: pointer to the usb device to send the message to
+ * @endpoint: endpoint to send the message to
+ * @request: USB message request value
+ * @requesttype: USB message request type value
+ * @value: USB message value
+ * @index: USB message index value
+ * @driver_data: pointer to the data to send
+ * @size: length in bytes of the data to send
+ * @timeout: time in msecs to wait for the message to complete before timing
+ *     out (if 0 the wait is forever)
+ * @memflags: the flags for memory allocation for buffers
+ *
+ * Context: !in_interrupt ()
+ *
+ * This function sends a control message to a specified endpoint that is not
+ * expected to fill in a response (i.e. a "send message") and waits for the
+ * message to complete, or timeout.
+ *
+ * Do not use this function from within an interrupt context. If you need
+ * an asynchronous message, or need to send a message from within interrupt
+ * context, use usb_submit_urb(). If a thread in your driver uses this call,
+ * make sure your disconnect() method can wait for it to complete. Since you
+ * don't have a handle on the URB used, you can't cancel the request.
+ *
+ * The data pointer can be made to a reference on the stack, or anywhere else,
+ * as it will not be modified at all.  This does not have the restriction that
+ * usb_control_msg() has where the data pointer must be to dynamically allocated
+ * memory (i.e. memory that can be successfully DMAed to a device).
+ *
+ * Return: If successful, 0 is returned, Otherwise, a negative error number.
+ */
+int usb_control_msg_send(struct usb_device *dev, __u8 endpoint, __u8 request,
+                        __u8 requesttype, __u16 value, __u16 index,
+                        const void *driver_data, __u16 size, int timeout,
+                        gfp_t memflags)
+{
+       unsigned int pipe = usb_sndctrlpipe(dev, endpoint);
+       int ret;
+       u8 *data = NULL;
+
+       if (usb_pipe_type_check(dev, pipe))
+               return -EINVAL;
+
+       if (size) {
+               data = kmemdup(driver_data, size, memflags);
+               if (!data)
+                       return -ENOMEM;
+       }
+
+       ret = usb_control_msg(dev, pipe, request, requesttype, value, index,
+                             data, size, timeout);
+       kfree(data);
+
+       if (ret < 0)
+               return ret;
+       if (ret == size)
+               return 0;
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(usb_control_msg_send);
+
+/**
+ * usb_control_msg_recv - Builds a control "receive" message, sends it off and waits for completion
+ * @dev: pointer to the usb device to send the message to
+ * @endpoint: endpoint to send the message to
+ * @request: USB message request value
+ * @requesttype: USB message request type value
+ * @value: USB message value
+ * @index: USB message index value
+ * @driver_data: pointer to the data to be filled in by the message
+ * @size: length in bytes of the data to be received
+ * @timeout: time in msecs to wait for the message to complete before timing
+ *     out (if 0 the wait is forever)
+ * @memflags: the flags for memory allocation for buffers
+ *
+ * Context: !in_interrupt ()
+ *
+ * This function sends a control message to a specified endpoint that is
+ * expected to fill in a response (i.e. a "receive message") and waits for the
+ * message to complete, or timeout.
+ *
+ * Do not use this function from within an interrupt context. If you need
+ * an asynchronous message, or need to send a message from within interrupt
+ * context, use usb_submit_urb(). If a thread in your driver uses this call,
+ * make sure your disconnect() method can wait for it to complete. Since you
+ * don't have a handle on the URB used, you can't cancel the request.
+ *
+ * The data pointer can be made to a reference on the stack, or anywhere else
+ * that can be successfully written to.  This function does not have the
+ * restriction that usb_control_msg() has where the data pointer must be to
+ * dynamically allocated memory (i.e. memory that can be successfully DMAed to a
+ * device).
+ *
+ * The "whole" message must be properly received from the device in order for
+ * this function to be successful.  If a device returns less than the expected
+ * amount of data, then the function will fail.  Do not use this for messages
+ * where a variable amount of data might be returned.
+ *
+ * Return: If successful, 0 is returned, Otherwise, a negative error number.
+ */
+int usb_control_msg_recv(struct usb_device *dev, __u8 endpoint, __u8 request,
+                        __u8 requesttype, __u16 value, __u16 index,
+                        void *driver_data, __u16 size, int timeout,
+                        gfp_t memflags)
+{
+       unsigned int pipe = usb_rcvctrlpipe(dev, endpoint);
+       int ret;
+       u8 *data;
+
+       if (!size || !driver_data || usb_pipe_type_check(dev, pipe))
+               return -EINVAL;
+
+       data = kmalloc(size, memflags);
+       if (!data)
+               return -ENOMEM;
+
+       ret = usb_control_msg(dev, pipe, request, requesttype, value, index,
+                             data, size, timeout);
+
+       if (ret < 0)
+               goto exit;
+
+       if (ret == size) {
+               memcpy(driver_data, data, size);
+               ret = 0;
+       } else {
+               ret = -EINVAL;
+       }
+
+exit:
+       kfree(data);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(usb_control_msg_recv);
+
 /**
  * usb_interrupt_msg - Builds an interrupt urb, sends it off and waits for completion
  * @usb_dev: pointer to the usb device to send the message to
@@ -948,11 +1085,12 @@ int usb_set_isoch_delay(struct usb_device *dev)
        if (dev->speed < USB_SPEED_SUPER)
                return 0;
 
-       return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+       return usb_control_msg_send(dev, 0,
                        USB_REQ_SET_ISOCH_DELAY,
                        USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
                        dev->hub_delay, 0, NULL, 0,
-                       USB_CTRL_SET_TIMEOUT);
+                       USB_CTRL_SET_TIMEOUT,
+                       GFP_NOIO);
 }
 
 /**
@@ -1070,13 +1208,13 @@ int usb_clear_halt(struct usb_device *dev, int pipe)
         * (like some ibmcam model 1 units) seem to expect hosts to make
         * this request for iso endpoints, which can't halt!
         */
-       result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
-               USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT,
-               USB_ENDPOINT_HALT, endp, NULL, 0,
-               USB_CTRL_SET_TIMEOUT);
+       result = usb_control_msg_send(dev, 0,
+                                     USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT,
+                                     USB_ENDPOINT_HALT, endp, NULL, 0,
+                                     USB_CTRL_SET_TIMEOUT, GFP_NOIO);
 
        /* don't un-halt or force to DATA0 except on success */
-       if (result < 0)
+       if (result)
                return result;
 
        /* NOTE:  seems like Microsoft and Apple don't bother verifying
@@ -1438,9 +1576,11 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
        if (dev->quirks & USB_QUIRK_NO_SET_INTF)
                ret = -EPIPE;
        else
-               ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
-                                  USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE,
-                                  alternate, interface, NULL, 0, 5000);
+               ret = usb_control_msg_send(dev, 0,
+                                          USB_REQ_SET_INTERFACE,
+                                          USB_RECIP_INTERFACE, alternate,
+                                          interface, NULL, 0, 5000,
+                                          GFP_NOIO);
 
        /* 9.4.10 says devices don't need this and are free to STALL the
         * request if the interface only has one alternate setting.
@@ -1450,7 +1590,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
                        "manual set_interface for iface %d, alt %d\n",
                        interface, alternate);
                manual = 1;
-       } else if (ret < 0) {
+       } else if (ret) {
                /* Re-instate the old alt setting */
                usb_hcd_alloc_bandwidth(dev, NULL, alt, iface->cur_altsetting);
                usb_enable_lpm(dev);
@@ -1574,11 +1714,11 @@ int usb_reset_configuration(struct usb_device *dev)
                mutex_unlock(hcd->bandwidth_mutex);
                return retval;
        }
-       retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
-                       USB_REQ_SET_CONFIGURATION, 0,
-                       config->desc.bConfigurationValue, 0,
-                       NULL, 0, USB_CTRL_SET_TIMEOUT);
-       if (retval < 0) {
+       retval = usb_control_msg_send(dev, 0, USB_REQ_SET_CONFIGURATION, 0,
+                                     config->desc.bConfigurationValue, 0,
+                                     NULL, 0, USB_CTRL_SET_TIMEOUT,
+                                     GFP_NOIO);
+       if (retval) {
                usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL);
                usb_enable_lpm(dev);
                mutex_unlock(hcd->bandwidth_mutex);
@@ -1957,10 +2097,10 @@ free_interfaces:
        }
        kfree(new_interfaces);
 
-       ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
-                             USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
-                             NULL, 0, USB_CTRL_SET_TIMEOUT);
-       if (ret < 0 && cp) {
+       ret = usb_control_msg_send(dev, 0, USB_REQ_SET_CONFIGURATION, 0,
+                                  configuration, 0, NULL, 0,
+                                  USB_CTRL_SET_TIMEOUT, GFP_NOIO);
+       if (ret && cp) {
                /*
                 * All the old state is gone, so what else can we do?
                 * The device is probably useless now anyway.
This page took 0.035791 seconds and 4 git commands to generate.