1 /***************************************************************************
2 * Copyright (c) 2024 Microsoft Corporation
3 *
4 * This program and the accompanying materials are made available under the
5 * terms of the MIT License which is available at
6 * https://opensource.org/licenses/MIT.
7 *
8 * SPDX-License-Identifier: MIT
9 **************************************************************************/
10
11 /**************************************************************************/
12 /**************************************************************************/
13 /** */
14 /** USBX Component */
15 /** */
16 /** Device Printer Class */
17 /** */
18 /**************************************************************************/
19 /**************************************************************************/
20
21 #define UX_SOURCE_CODE
22
23
24 /* Include necessary system files. */
25
26 #include "ux_api.h"
27 #include "ux_device_class_printer.h"
28 #include "ux_device_stack.h"
29
30
31 #if defined(UX_DEVICE_STANDALONE)
32
33 /**************************************************************************/
34 /* */
35 /* FUNCTION RELEASE */
36 /* */
37 /* _ux_device_class_printer_write_run PORTABLE C */
38 /* 6.3.0 */
39 /* AUTHOR */
40 /* */
41 /* Yajun Xia, Microsoft Corporation */
42 /* */
43 /* DESCRIPTION */
44 /* */
45 /* This function writes to the Printer class. */
46 /* */
47 /* It's for standalone mode. */
48 /* */
49 /* INPUT */
50 /* */
51 /* printer Address of printer class */
52 /* instance */
53 /* buffer Pointer to data to write */
54 /* requested_length Length of bytes to write, */
55 /* set to 0 to issue ZLP */
56 /* actual_length Pointer to save number of */
57 /* bytes written */
58 /* */
59 /* OUTPUT */
60 /* */
61 /* State machine Status to check */
62 /* UX_STATE_NEXT Transfer done, to next state */
63 /* UX_STATE_EXIT Abnormal, to reset state */
64 /* (others) Keep running, waiting */
65 /* */
66 /* CALLS */
67 /* */
68 /* _ux_device_stack_transfer_run Run Transfer state machine */
69 /* _ux_utility_memory_copy Copy memory */
70 /* */
71 /* CALLED BY */
72 /* */
73 /* Application */
74 /* */
75 /* RELEASE HISTORY */
76 /* */
77 /* DATE NAME DESCRIPTION */
78 /* */
79 /* 10-31-2022 Yajun Xia Initial Version 6.2.0 */
80 /* 10-31-2023 Chaoqiong Xiao Modified comment(s), */
81 /* added a new mode to manage */
82 /* endpoint buffer in classes, */
83 /* resulting in version 6.3.0 */
84 /* */
85 /**************************************************************************/
_ux_device_class_printer_write_run(UX_DEVICE_CLASS_PRINTER * printer,UCHAR * buffer,ULONG requested_length,ULONG * actual_length)86 UINT _ux_device_class_printer_write_run(UX_DEVICE_CLASS_PRINTER *printer, UCHAR *buffer,
87 ULONG requested_length, ULONG *actual_length)
88 {
89
90 UX_SLAVE_ENDPOINT *endpoint;
91 UX_SLAVE_DEVICE *device;
92 UX_SLAVE_TRANSFER *transfer_request;
93 UINT status = 0;
94 #if (UX_DEVICE_ENDPOINT_BUFFER_OWNER != 1) || !defined(UX_DEVICE_CLASS_CDC_ACM_ZERO_COPY)
95 UINT zlp = UX_FALSE;
96 #endif
97
98 /* If trace is enabled, insert this event into the trace buffer. */
99 UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PRINTER_WRITE, printer, buffer, requested_length, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0)
100
101 /* Get the pointer to the device. */
102 device = &_ux_system_slave -> ux_system_slave_device;
103
104 /* As long as the device is in the CONFIGURED state. */
105 if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED)
106 {
107
108 /* Error trap. */
109 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONFIGURATION_HANDLE_UNKNOWN);
110
111 /* If trace is enabled, insert this event into the trace buffer. */
112 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONFIGURATION_HANDLE_UNKNOWN, device, 0, 0, UX_TRACE_ERRORS, 0, 0)
113
114 /* Cannot proceed with command, the interface is down. */
115 printer -> ux_device_class_printer_write_state = UX_STATE_RESET;
116 printer -> ux_device_class_printer_write_status = UX_CONFIGURATION_HANDLE_UNKNOWN;
117
118 return(UX_STATE_EXIT);
119 }
120
121 /* Locate the endpoints. */
122 endpoint = printer -> ux_device_class_printer_endpoint_in;
123
124 /* Check if it's available. */
125 if (endpoint == UX_NULL)
126 {
127 /* Error trap. */
128 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_ENDPOINT_HANDLE_UNKNOWN);
129
130 printer -> ux_device_class_printer_write_state = UX_STATE_RESET;
131 printer -> ux_device_class_printer_write_status = UX_ENDPOINT_HANDLE_UNKNOWN;
132
133 return(UX_STATE_EXIT);
134 }
135
136 /* We are writing to the IN endpoint. */
137 transfer_request = &endpoint -> ux_slave_endpoint_transfer_request;
138
139 #if (UX_DEVICE_ENDPOINT_BUFFER_OWNER == 1) && defined(UX_DEVICE_CLASS_PRINTER_ZERO_COPY)
140
141 /* Run the transfer state machine. */
142 if (printer -> ux_device_class_printer_write_state == UX_STATE_RESET)
143 {
144
145 /* Set the state to WRITE_WAIT. */
146 printer -> ux_device_class_printer_write_state = UX_DEVICE_CLASS_PRINTER_WRITE_WAIT;
147 printer -> ux_device_class_printer_write_status = UX_TRANSFER_NO_ANSWER;
148 transfer_request -> ux_slave_transfer_request_data_pointer = buffer;
149 UX_SLAVE_TRANSFER_STATE_RESET(transfer_request);
150 }
151
152 /* Issue the transfer request. */
153 #if defined(UX_DEVICE_CLASS_PRINTER_WRITE_AUTO_ZLP)
154 status = _ux_device_stack_transfer_run(transfer_request, requested_length, requested_length + 1);
155 #else
156 status = _ux_device_stack_transfer_run(transfer_request, requested_length, requested_length);
157 #endif
158
159 /* Error case. */
160 if (status < UX_STATE_NEXT)
161 {
162
163 printer -> ux_device_class_printer_write_state = UX_STATE_RESET;
164 printer -> ux_device_class_printer_write_status =
165 transfer_request -> ux_slave_transfer_request_completion_code;
166 return(UX_STATE_ERROR);
167 }
168
169 /* Success case. */
170 if (status == UX_STATE_NEXT)
171 {
172
173 /* Last transfer status. */
174 printer -> ux_device_class_printer_write_status =
175 transfer_request -> ux_slave_transfer_request_completion_code;
176
177 /* Update actual length. */
178 *actual_length = transfer_request -> ux_slave_transfer_request_actual_length;
179
180 /* It's done. */
181 printer -> ux_device_class_printer_write_state = UX_STATE_RESET;
182 }
183 return(status);
184
185 #else
186
187 /* Handle state cases. */
188 switch(printer -> ux_device_class_printer_write_state)
189 {
190 case UX_STATE_RESET:
191 printer -> ux_device_class_printer_write_state = UX_DEVICE_CLASS_PRINTER_WRITE_START;
192 printer -> ux_device_class_printer_write_status = UX_TRANSFER_NO_ANSWER;
193 printer -> ux_device_class_printer_write_buffer = buffer;
194 printer -> ux_device_class_printer_write_requested_length = requested_length;
195 printer -> ux_device_class_printer_write_actual_length = 0;
196 printer -> ux_device_class_printer_write_host_length = UX_DEVICE_CLASS_PRINTER_WRITE_BUFFER_SIZE;
197 if (requested_length == 0)
198 zlp = UX_TRUE;
199
200 /* Fall through. */
201 case UX_DEVICE_CLASS_PRINTER_WRITE_START:
202
203 /* Get remaining requested length. */
204 requested_length = printer -> ux_device_class_printer_write_requested_length -
205 printer -> ux_device_class_printer_write_actual_length;
206
207 /* There is no remaining, we are done. */
208 if (requested_length == 0 && !zlp)
209 {
210 *actual_length = printer -> ux_device_class_printer_write_actual_length;
211 printer -> ux_device_class_printer_write_state = UX_STATE_RESET;
212 printer -> ux_device_class_printer_write_status = UX_SUCCESS;
213 return(UX_STATE_NEXT);
214 }
215
216 /* Check if we have enough in the local buffer. */
217 if (requested_length > UX_DEVICE_CLASS_PRINTER_WRITE_BUFFER_SIZE)
218
219 /* We have too much to transfer. */
220 printer -> ux_device_class_printer_write_transfer_length =
221 UX_DEVICE_CLASS_PRINTER_WRITE_BUFFER_SIZE;
222
223 else
224 {
225
226 /* We can proceed with the demanded length. */
227 printer -> ux_device_class_printer_write_transfer_length = requested_length;
228
229 #if !defined(UX_DEVICE_CLASS_PRINTER_WRITE_AUTO_ZLP)
230
231 /* Assume expected length and transfer length match. */
232 printer -> ux_device_class_printer_write_host_length = requested_length;
233 #else
234
235 /* Assume expected more than transfer to let stack append ZLP if needed. */
236 printer -> ux_device_class_printer_write_host_length = UX_DEVICE_CLASS_PRINTER_WRITE_BUFFER_SIZE + 1;
237 #endif
238 }
239
240
241 /* On a out, we copy the buffer to the caller. Not very efficient but it makes the API
242 easier. */
243 _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer,
244 printer -> ux_device_class_printer_write_buffer,
245 printer -> ux_device_class_printer_write_transfer_length); /* Use case of memcpy is verified. */
246
247 /* Next state. */
248 printer -> ux_device_class_printer_write_state = UX_DEVICE_CLASS_PRINTER_WRITE_WAIT;
249 UX_SLAVE_TRANSFER_STATE_RESET(transfer_request);
250
251 /* Fall through. */
252 case UX_DEVICE_CLASS_PRINTER_WRITE_WAIT:
253
254 /* Send the request to the device controller. */
255 status = _ux_device_stack_transfer_run(transfer_request,
256 printer -> ux_device_class_printer_write_transfer_length,
257 printer -> ux_device_class_printer_write_host_length);
258
259 /* Error case. */
260 if (status < UX_STATE_NEXT)
261 {
262
263 printer -> ux_device_class_printer_write_state = UX_STATE_RESET;
264 printer -> ux_device_class_printer_write_status =
265 transfer_request -> ux_slave_transfer_request_completion_code;
266 return(UX_STATE_ERROR);
267 }
268
269 /* Success case. */
270 if (status == UX_STATE_NEXT)
271 {
272
273 /* Next buffer address. */
274 printer -> ux_device_class_printer_write_buffer +=
275 transfer_request -> ux_slave_transfer_request_actual_length;
276
277 /* Set the length actually received. */
278 printer -> ux_device_class_printer_write_actual_length +=
279 transfer_request -> ux_slave_transfer_request_actual_length;
280
281 /* Last transfer status. */
282 printer -> ux_device_class_printer_write_status =
283 transfer_request -> ux_slave_transfer_request_completion_code;
284
285 /* Update actual done length. */
286 *actual_length = printer -> ux_device_class_printer_write_actual_length;
287
288 /* Check ZLP case. */
289 if (printer -> ux_device_class_printer_write_requested_length == 0)
290 {
291 printer -> ux_device_class_printer_write_state = UX_STATE_RESET;
292 return(UX_STATE_NEXT);
293 }
294
295 /* Next state. */
296 printer -> ux_device_class_printer_write_state = UX_DEVICE_CLASS_PRINTER_WRITE_START;
297 }
298
299 /* Keep waiting. */
300 return(UX_STATE_WAIT);
301
302 default: /* Error. */
303 printer -> ux_device_class_printer_write_state = UX_STATE_RESET;
304 break;
305 }
306 #endif
307
308 /* Error case. */
309 return(UX_STATE_EXIT);
310 }
311
312 /**************************************************************************/
313 /* */
314 /* FUNCTION RELEASE */
315 /* */
316 /* _uxe_device_class_printer_write_run PORTABLE C */
317 /* 6.3.0 */
318 /* AUTHOR */
319 /* */
320 /* Yajun Xia, Microsoft Corporation */
321 /* */
322 /* DESCRIPTION */
323 /* */
324 /* This function checks errors in printer write function call. */
325 /* */
326 /* It's for standalone mode. */
327 /* */
328 /* INPUT */
329 /* */
330 /* printer Address of printer class */
331 /* instance */
332 /* buffer Pointer to data to write */
333 /* requested_length Length of bytes to write, */
334 /* set to 0 to issue ZLP */
335 /* actual_length Pointer to save number of */
336 /* bytes written */
337 /* */
338 /* OUTPUT */
339 /* */
340 /* State machine Status to check */
341 /* UX_STATE_NEXT Transfer done, to next state */
342 /* UX_STATE_EXIT Abnormal, to reset state */
343 /* UX_STATE_ERROR Error occurred */
344 /* (others) Keep running, waiting */
345 /* */
346 /* CALLS */
347 /* */
348 /* _ux_device_class_printer_write_run Printer class write process */
349 /* */
350 /* CALLED BY */
351 /* */
352 /* Application */
353 /* */
354 /* RELEASE HISTORY */
355 /* */
356 /* DATE NAME DESCRIPTION */
357 /* */
358 /* 10-31-2023 Yajun xia Initial Version 6.3.0 */
359 /* */
360 /**************************************************************************/
_uxe_device_class_printer_write_run(UX_DEVICE_CLASS_PRINTER * printer,UCHAR * buffer,ULONG requested_length,ULONG * actual_length)361 UINT _uxe_device_class_printer_write_run(UX_DEVICE_CLASS_PRINTER *printer, UCHAR *buffer,
362 ULONG requested_length, ULONG *actual_length)
363 {
364
365 /* Sanity check. */
366 if ((printer == UX_NULL) || ((buffer == UX_NULL) && (requested_length > 0)) || (actual_length == UX_NULL))
367 {
368 return (UX_STATE_ERROR);
369 }
370
371 return(_ux_device_class_printer_write_run(printer, buffer, requested_length, actual_length));
372 }
373
374 #endif
375