1 /* libusb-win32, Generic Windows USB Library
2  * Copyright (c) 2002-2005 Stephan Meyer <ste_meyer@web.de>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18 
19 
20 #include "libusb_driver.h"
21 #include <stdlib.h>
22 
23 
set_configuration(libusb_device_t * dev,int configuration,int timeout)24 NTSTATUS set_configuration(libusb_device_t *dev,
25 						   int configuration,
26                            int timeout)
27 {
28     NTSTATUS status = STATUS_SUCCESS;
29     URB urb, *urb_ptr = NULL;
30     USB_CONFIGURATION_DESCRIPTOR *configuration_descriptor = NULL;
31     USB_INTERFACE_DESCRIPTOR *interface_descriptor = NULL;
32     USBD_INTERFACE_LIST_ENTRY *interfaces = NULL;
33     int i, j, interface_number, desc_size, config_index, ret;
34 
35 	// check if this config value is already set
36 	if ((configuration > 0) && dev->config.value == configuration)
37     {
38         return STATUS_SUCCESS;
39     }
40 
41 	// check if this config index is already set
42 	if ((configuration < 0) && dev->config.value && dev->config.index == (abs(configuration)-1))
43     {
44         return STATUS_SUCCESS;
45     }
46 
47     memset(&urb, 0, sizeof(URB));
48 
49     if (configuration == 0)
50     {
51         urb.UrbHeader.Function = URB_FUNCTION_SELECT_CONFIGURATION;
52         urb.UrbHeader.Length = sizeof(struct _URB_SELECT_CONFIGURATION);
53 
54         status = call_usbd(dev, &urb, IOCTL_INTERNAL_USB_SUBMIT_URB, timeout);
55 
56         if (!NT_SUCCESS(status) || !USBD_SUCCESS(urb.UrbHeader.Status))
57         {
58             USBERR("setting configuration %d failed: status: 0x%x, urb-status: 0x%x\n",
59                         configuration, status, urb.UrbHeader.Status);
60             return status;
61         }
62 
63         dev->config.handle =  urb.UrbSelectConfiguration.ConfigurationHandle;
64         dev->config.value = 0;
65 
66         clear_pipe_info(dev);
67 
68         return status;
69     }
70 
71 	if (configuration <= SET_CONFIG_ACTIVE_CONFIG)
72 	{
73 		// note: as of v1.2.4.0, the active/default configuration is
74 		// always the first configuration the device returns. (index 0)
75 		configuration=-1;
76 	}
77 
78 	USBMSG("setting configuration %s %d timeout=%d",
79 		(configuration < 0) ? "index" : "value",
80 		(configuration < 0) ? abs(configuration) - 1 : configuration,
81 		timeout);
82 
83 
84 	// If configuration is negative, it is retrieved by index.
85 	//
86     configuration_descriptor = get_config_descriptor(dev, configuration,
87                                &desc_size, &config_index);
88     if (!configuration_descriptor)
89     {
90         USBERR0("getting configuration descriptor failed");
91         return STATUS_INVALID_PARAMETER;
92     }
93 
94 	// if we passed an index in we can check here to see
95 	// if the device is already configured with this value
96 	if (dev->config.value == configuration_descriptor->bConfigurationValue)
97     {
98 		UpdateContextConfigDescriptor(
99 			dev,
100 			configuration_descriptor,
101 			desc_size,
102 			configuration_descriptor->bConfigurationValue,
103 			config_index);
104 
105 		status = STATUS_SUCCESS;
106 		goto SetConfigurationDone;
107     }
108 
109 	// MEMORY ALLOCATION BEGINS
110     interfaces =
111         ExAllocatePool(NonPagedPool,(configuration_descriptor->bNumInterfaces + 1)
112                        * sizeof(USBD_INTERFACE_LIST_ENTRY));
113 
114     if (!interfaces)
115     {
116         USBERR0("memory allocation failed\n");
117 		status = STATUS_NO_MEMORY;
118 		ExFreePool(configuration_descriptor);
119 		goto SetConfigurationDone;
120     }
121 
122     memset(interfaces, 0, (configuration_descriptor->bNumInterfaces + 1)
123            * sizeof(USBD_INTERFACE_LIST_ENTRY));
124 
125     interface_number = 0;
126 
127     for (i = 0; i < configuration_descriptor->bNumInterfaces; i++)
128     {
129         for (j = interface_number; j < LIBUSB_MAX_NUMBER_OF_INTERFACES; j++)
130         {
131             interface_descriptor =
132                 find_interface_desc(configuration_descriptor, desc_size, j, 0);
133             if (interface_descriptor)
134             {
135                 interface_number = ++j;
136                 break;
137             }
138         }
139 
140         if (!interface_descriptor)
141         {
142             USBERR("unable to find interface descriptor at index %d\n", i);
143 			status = STATUS_INVALID_PARAMETER;
144 			ExFreePool(configuration_descriptor);
145 			goto SetConfigurationDone;
146         }
147         else
148         {
149             USBMSG("found interface %d\n",
150                           interface_descriptor->bInterfaceNumber);
151             interfaces[i].InterfaceDescriptor = interface_descriptor;
152         }
153     }
154 
155 	urb_ptr = USBD_CreateConfigurationRequestEx(configuration_descriptor, interfaces);
156 	if (!urb_ptr)
157 	{
158 		USBERR0("memory allocation failed\n");
159 		status = STATUS_NO_MEMORY;
160 		ExFreePool(configuration_descriptor);
161 		goto SetConfigurationDone;
162 	}
163 
164 	for (i = 0; i < configuration_descriptor->bNumInterfaces; i++)
165 	{
166 		for (j = 0; j < (int)interfaces[i].Interface->NumberOfPipes; j++)
167 		{
168 			interfaces[i].Interface->Pipes[j].MaximumTransferSize = LIBUSB_MAX_READ_WRITE;
169 		}
170 	}
171 
172 	USBDBG("#%d %s passing configuration request to target-device.",
173 		dev->id, dev->device_id);
174 
175 	status = call_usbd(dev, urb_ptr, IOCTL_INTERNAL_USB_SUBMIT_URB, timeout);
176 
177 	if (!NT_SUCCESS(status) || !USBD_SUCCESS(urb_ptr->UrbHeader.Status))
178 	{
179 		USBERR("setting configuration %d failed: status: 0x%x, urb-status: 0x%x\n",
180 					configuration, status, urb_ptr->UrbHeader.Status);
181 		if (NT_SUCCESS(status)) status = urb_ptr->UrbHeader.Status;
182 
183 		ExFreePool(configuration_descriptor);
184 		goto SetConfigurationDone;
185 	}
186 
187 	dev->config.handle = urb_ptr->UrbSelectConfiguration.ConfigurationHandle;
188 
189     clear_pipe_info(dev);
190 
191     for (i = 0; i < configuration_descriptor->bNumInterfaces; i++)
192     {
193         update_pipe_info(dev, interfaces[i].Interface);
194     }
195 	UpdateContextConfigDescriptor(dev, configuration_descriptor, desc_size, configuration_descriptor->bConfigurationValue, config_index);
196 
197 SetConfigurationDone:
198     if (interfaces)
199 		ExFreePool(interfaces);
200 
201     if (urb_ptr)
202 		ExFreePool(urb_ptr);
203 
204     return status;
205 }
206