Create a branch for header work.
[reactos.git] / drivers / usb / nt4compat / usbdriver / cbi.c
1 /**
2 * cbi.c - USB driver stack project for Windows NT 4.0
3 *
4 * Copyright (c) 2002-2004 Zhiming mypublic99@yahoo.com
5 *
6 * This program/include file is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as published
8 * by the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program/include file is distributed in the hope that it will be
12 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program (in the main directory of the distribution, the file
18 * COPYING); if not, write to the Free Software Foundation,Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 #include "usbdriver.h"
23
24 VOID umss_cbi_send_adsc_complete(PURB purb, PVOID context);
25 VOID umss_cbi_transfer_data(PUMSS_DEVICE_EXTENSION pdev_ext);
26 VOID umss_cbi_get_status(PUMSS_DEVICE_EXTENSION pdev_ext);
27 VOID umss_cbi_transfer_data_complete(PURB purb, PVOID context);
28 VOID umss_cbi_get_status_complete(PURB purb, PVOID context);
29
30 NTSTATUS
31 umss_class_specific_request(IN PUMSS_DEVICE_EXTENSION pdev_ext,
32 IN UCHAR request,
33 IN UCHAR dir,
34 IN PVOID buffer,
35 IN ULONG buffer_length,
36 IN PURBCOMPLETION completion)
37 {
38 PURB purb;
39 NTSTATUS status;
40
41 UNREFERENCED_PARAMETER(dir);
42
43 purb = usb_alloc_mem(NonPagedPool, sizeof(URB));
44 if (!purb) return STATUS_NO_MEMORY;
45
46 // Build URB for the ADSC command
47 UsbBuildVendorRequest(purb,
48 pdev_ext->dev_handle | 0xffff,
49 buffer,
50 buffer_length,
51 0x21, request, 0, pdev_ext->pif_desc->bInterfaceNumber, completion, pdev_ext, 0);
52
53 status = usb_submit_urb(pdev_ext->dev_mgr, purb);
54 if (status != STATUS_PENDING)
55 {
56 usb_free_mem(purb);
57 purb = NULL;
58 return status;
59 }
60 dev_mgr_register_irp(pdev_ext->dev_mgr, pdev_ext->io_packet.pirp, purb);
61 return status;
62 }
63
64 NTSTATUS
65 umss_cbi_startio(IN PUMSS_DEVICE_EXTENSION pdev_ext, IN PIO_PACKET io_packet)
66 {
67 NTSTATUS status;
68
69 status = STATUS_NOT_SUPPORTED;
70 return status;
71
72 RtlCopyMemory(&pdev_ext->io_packet, io_packet, sizeof(pdev_ext->io_packet));
73
74 // Send the ADSC request to the device
75 // Calls UMSS_CbiSendADSCComplete when transfer completes
76 status = umss_class_specific_request(pdev_ext,
77 ACCEPT_DEVICE_SPECIFIC_COMMAND,
78 USB_DIR_OUT,
79 io_packet->cdb, io_packet->cdb_length, umss_cbi_send_adsc_complete);
80
81 return status;
82 }
83
84
85
86 VOID
87 umss_cbi_send_adsc_complete(PURB purb, PVOID context)
88 {
89 NTSTATUS status;
90 PUMSS_DEVICE_EXTENSION pdev_ext;
91 PIO_PACKET io_packet;
92
93 pdev_ext = (PUMSS_DEVICE_EXTENSION) context;
94 io_packet = &pdev_ext->io_packet;
95
96 status = purb->status;
97
98 dev_mgr_remove_irp(pdev_ext->dev_mgr, pdev_ext->io_packet.pirp);
99
100 if (!usb_success(status))
101 {
102 usb_dbg_print(DBGLVL_MINIMUM, ("umss_cbi_send_adsc_complete(): Command Block Failure!!!\n"));
103
104 // BUGBUG - Should reset device here?
105 // Device failed Command Block, complete with error
106 umss_complete_request(pdev_ext, STATUS_IO_DEVICE_ERROR);
107
108 }
109 else if (io_packet->data_length)
110 {
111
112 usb_dbg_print(DBGLVL_HIGH, ("umss_cbi_send_adsc_complete(): Queuing Data Transfer DPC\n"));
113 umss_cbi_transfer_data(pdev_ext);
114
115 }
116 else if (pdev_ext->pif_desc->bInterfaceProtocol == PROTOCOL_CBI)
117 {
118 // Device supports interrupt pipe, so get status
119 umss_cbi_get_status(pdev_ext);
120 }
121 else
122 {
123 // Device does not report status, so complete request
124 umss_complete_request(pdev_ext, STATUS_SUCCESS);
125 }
126
127 usb_free_mem(purb);
128 purb = NULL;
129 }
130
131
132
133 VOID
134 umss_cbi_reset_pipe(IN PVOID reference)
135 {
136 PUMSS_DEVICE_EXTENSION pdev_ext;
137 pdev_ext = (PUMSS_DEVICE_EXTENSION) reference;
138
139 // Reset the appropriate pipe, based on data direction
140 umss_reset_pipe(pdev_ext,
141 (pdev_ext->io_packet.flags & USB_DIR_IN) ?
142 usb_make_handle((pdev_ext->dev_handle >> 16), pdev_ext->if_idx, pdev_ext->in_endp_idx) :
143 usb_make_handle((pdev_ext->dev_handle >> 16), pdev_ext->if_idx, pdev_ext->out_endp_idx));
144
145 // Device stalled endpoint, so complete I/O operation with error.
146 // BUGBUG is this correct? Check spec...
147 umss_complete_request(pdev_ext, USB_STATUS_STALL_PID);
148 }
149
150 VOID
151 umss_cbi_transfer_data(PUMSS_DEVICE_EXTENSION pdev_ext)
152 {
153 PVOID buffer = NULL;
154 ULONG buffer_length;
155
156 // Get next data buffer element, if any.
157 buffer = umss_get_buffer(pdev_ext, &buffer_length);
158 if (NULL == buffer)
159 {
160 //Done with data phase, so move to status phase if (supported)
161
162 if (pdev_ext->pif_desc->bInterfaceProtocol == PROTOCOL_CBI)
163 {
164 // Device supports interrupt pipe, so get status
165 umss_cbi_get_status(pdev_ext);
166 }
167 else
168 {
169 // No interrupt pipe, so just complete the request
170 umss_complete_request(pdev_ext, STATUS_SUCCESS);
171 }
172 }
173 else
174 {
175 // Transfer next element of the data phase
176 umss_bulk_transfer(pdev_ext,
177 (UCHAR) ((pdev_ext->io_packet.flags & USB_DIR_IN) ? USB_DIR_IN : USB_DIR_OUT),
178 buffer, buffer_length, umss_cbi_transfer_data_complete);
179 }
180 }
181
182
183 VOID
184 umss_cbi_transfer_data_complete(PURB purb, PVOID context)
185 {
186 NTSTATUS status;
187 PUMSS_DEVICE_EXTENSION pdev_ext;
188
189 pdev_ext = (PUMSS_DEVICE_EXTENSION) context;
190 status = purb->status;
191
192 usb_free_mem(purb);
193 purb = NULL;
194
195 if (!usb_success(status))
196 {
197 // Device failed Data Transfer
198 // Check if we need to clear stalled pipe
199 if (usb_halted(status))
200 {
201 // Reset pipe can only be done at passive level, so we need
202 // to schedule a work item to do it.
203 if (!umss_schedule_workitem
204 ((PVOID) pdev_ext, umss_cbi_reset_pipe, pdev_ext->dev_mgr, pdev_ext->dev_handle))
205 {
206 usb_dbg_print(DBGLVL_MINIMUM,
207 ("umss_cbi_transfer_data_complete(): Failed to allocate work-item to reset pipe!\n"));
208 TRAP();
209 umss_complete_request(pdev_ext, STATUS_IO_DEVICE_ERROR);
210 }
211 }
212 else
213 {
214 umss_complete_request(pdev_ext, STATUS_IO_DEVICE_ERROR);
215 }
216 return;
217 }
218 // Transfer succeeded
219 // umss_cbi_transfer_data( pdev_ext );
220 umss_complete_request(pdev_ext, STATUS_SUCCESS);
221 return;
222 }
223
224
225 VOID
226 umss_cbi_get_status(PUMSS_DEVICE_EXTENSION pdev_ext)
227 {
228 PURB purb;
229 NTSTATUS status;
230
231 purb = usb_alloc_mem(NonPagedPool, sizeof(URB));
232 if (purb == NULL)
233 return;
234
235 // Build a URB for our interrupt transfer
236 UsbBuildInterruptOrBulkTransferRequest(purb,
237 usb_make_handle((pdev_ext->dev_handle >> 16), pdev_ext->if_idx,
238 pdev_ext->int_endp_idx), (PUCHAR) & pdev_ext->idb,
239 sizeof(INTERRUPT_DATA_BLOCK), umss_cbi_get_status_complete,
240 pdev_ext, 0);
241
242 // Call USB driver stack
243 status = usb_submit_urb(pdev_ext->dev_mgr, purb);
244 if (status != STATUS_PENDING)
245 {
246 usb_free_mem(purb);
247 purb = NULL;
248 return;
249 }
250 dev_mgr_register_irp(pdev_ext->dev_mgr, pdev_ext->io_packet.pirp, purb);
251 return;
252 }
253
254
255 VOID
256 umss_cbi_get_status_complete(PURB purb, PVOID context)
257 {
258 NTSTATUS status;
259 PUMSS_DEVICE_EXTENSION pdev_ext;
260 PINTERRUPT_DATA_BLOCK idb;
261
262 pdev_ext = (PUMSS_DEVICE_EXTENSION) context;
263
264 status = purb->status;
265 dev_mgr_remove_irp(pdev_ext->dev_mgr, pdev_ext->io_packet.pirp);
266
267 usb_free_mem(purb);
268 purb = NULL;
269
270 if (!usb_success(status))
271 {
272 // Device failed Data Transfer
273 // Check if we need to clear stalled pipe
274 if (usb_halted(status))
275 {
276 if (!umss_schedule_workitem
277 ((PVOID) pdev_ext, umss_cbi_reset_pipe, pdev_ext->dev_mgr, pdev_ext->dev_handle))
278 {
279 usb_dbg_print(DBGLVL_MINIMUM,
280 ("umss_cbi_get_status_complete(): Failed to allocate work-item to reset pipe!\n"));
281 TRAP();
282 umss_complete_request(pdev_ext, STATUS_IO_DEVICE_ERROR);
283 return;
284 }
285 }
286 umss_complete_request(pdev_ext, STATUS_IO_DEVICE_ERROR);
287 return;
288 }
289
290 // Interrupt transfer succeeded
291 idb = &(pdev_ext->idb);
292
293 // Check for an error in the status block
294 if ((0 != idb->bType) || (0 != (idb->bValue & 0x3)))
295 {
296 umss_complete_request(pdev_ext, STATUS_IO_DEVICE_ERROR);
297 }
298 else
299 {
300 umss_complete_request(pdev_ext, STATUS_SUCCESS);
301 }
302 }