1 //==========================================================================
5 // Example application for the USB serial layer in eCos.
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
12 // eCos is free software; you can redistribute it and/or modify it under
13 // the terms of the GNU General Public License as published by the Free
14 // Software Foundation; either version 2 or (at your option) any later version.
16 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
17 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 // You should have received a copy of the GNU General Public License along
22 // with eCos; if not, write to the Free Software Foundation, Inc.,
23 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 // As a special exception, if other files instantiate templates or use macros
26 // or inline functions from this file, or you compile this file and link it
27 // with other works to produce a work based on this file, this file does not
28 // by itself cause the resulting work to be covered by the GNU General Public
29 // License. However the source code for this file must still be made available
30 // in accordance with section (3) of the GNU General Public License.
32 // This exception does not invalidate any other reasons why a work based on
33 // this file might be covered by the GNU General Public License.
35 // -------------------------------------------
36 //####ECOSGPLCOPYRIGHTEND####
37 //===========================================================================
38 //#####DESCRIPTIONBEGIN####
40 // Author(s): Frank M. Pagliughi (fmp), SoRo Systems, Inc.
43 // Description: USB serial example application.
45 //####DESCRIPTIONEND####
46 //===========================================================================
48 #include <cyg/kernel/kapi.h>
49 #include <cyg/hal/hal_arch.h>
50 #include <cyg/infra/diag.h>
51 #include <pkgconf/kernel.h>
52 #include <cyg/io/serialio.h>
54 // Replace this with any other USB driver desired.
55 #include <cyg/io/usb/usbs_at91.h>
56 #include <cyg/io/usb/usbs_serial.h>
57 #include <pkgconf/io_usb_slave_serial.h>
65 // This application creates a USB-serial converter. To the host it will appear
66 // as a single serial USB port, such as /dev/ttyUSB0 for a Linux host.
67 // Any characters received from the USB host will be sent out the serial port
68 // and visa-verse. It creates a separate, dedicated thread for each direction.
70 // It uses the eCos USB-serial layer to enumerate with the USB host and monitor
71 // the connection, but then uses standard C I/O functions to perform the
74 // The USB serial module can be configured as a generic adapter or an an ACM
75 // communications class device. For the latter, the application handles the
76 // USB communications class requests which allows it to receive requests from
77 // the host to set serial parameters, like the baud rate. This actually turns
78 // this example into a more realistic USB-serial adapter that can be configured
79 // dynamically by the host.
81 // The eCos library must be configured with the packages for USB slave, USB
82 // serial, and File I/O. It also requires the proper serial port driver for the
85 // This example was tested with the AT91SAM7S-EK board, but should work with any
86 // board that has a USB slave and serial port, and the necessary drivers.
89 // Comment this line out to remove debug output.
92 #if defined(DEBUG_OUTPUT)
93 #define DBG diag_printf
95 #define DBG (1) ? (void)0 : diag_printf
98 // Set these to the USB devtab entries for the Tx and Rx Bulk endpoints
99 // selected in the configuration of the USB serial subsystem.
100 #define USB_TX_DEV "/dev/usbs1"
101 #define USB_RX_DEV "/dev/usbs2"
103 // Set this for any available serial port on the target.
104 #define SER_DEV "/dev/ser0"
106 // Buffer for incoming USB bulk data. The local USB driver can probably split
107 // packets, but just in case, making this the page size of the host might be
109 #define BUF_SIZE 4096
110 static char usb2ser_buf[BUF_SIZE];
113 cyg_thread thread[2];
115 // Space for two 4K stacks
116 #define THREAD_STACK_SIZE 4096
117 char stack[2][THREAD_STACK_SIZE];
119 // The handles for the threads
120 cyg_handle_t usb2ser_thread,
123 // --------------------------------------------------------------------------
124 // For an ACM serial device we can handle the USB class messages to deal with
125 // requests from the host like setting the serial parameters (baud rate,
128 #ifdef CYGDAT_IO_USB_SLAVE_CLASS_TYPE_ACM
130 static cyg_uint8 acm_buf[32];
131 static cyg_uint32 baud = 34800;
133 // --------------------------------------------------------------------------
134 // Handler for the completion of a SetLineCoding request from the host.
135 // The 'acm_buf' should contain the 7-byte request OUT packet from the
136 // host. This contains a request to change the serial parameters.
137 // In this example function, we will accept a few different baud rates.
138 // To keep the example relatively simple, though, we keep the other serial
139 // parameters to 1 stop bit, no parity, 8 data bits.
141 static usbs_control_return
142 acm_set_line_coding(usbs_control_endpoint* ep0, int n)
146 cyg_io_handle_t handle;
148 // Get the requested baud rate from the received ctrl OUT packet
149 req_baud = ((acm_buf[3] << 24) | (acm_buf[2] << 16) |
150 (acm_buf[1] << 8) | acm_buf[0]);
152 DBG("Set Baud: %u\n", (unsigned) baud);
154 // Look up the serial handle and attempt to set the baud rate.
155 if (cyg_io_lookup(SER_DEV, &handle) == 0) {
156 cyg_serial_info_t ser_info;
157 cyg_uint32 len = sizeof(ser_info);
160 case 9600 : ser_info.baud = CYGNUM_SERIAL_BAUD_9600; break;
161 case 38400 : ser_info.baud = CYGNUM_SERIAL_BAUD_38400; break;
162 case 115200 : ser_info.baud = CYGNUM_SERIAL_BAUD_115200; break;
164 DBG("Unsupported baud rate\n");
165 return USBS_CONTROL_RETURN_HANDLED;
167 ser_info.stop = CYGNUM_SERIAL_STOP_1;
168 ser_info.parity = CYGNUM_SERIAL_PARITY_NONE;
169 ser_info.word_length = CYGNUM_SERIAL_WORD_LENGTH_8;
172 err = cyg_io_set_config(handle, CYG_IO_SET_CONFIG_SERIAL_INFO,
177 DBG("Error setting serial params\n");
181 DBG("Error looking up serial device: %s\n", SER_DEV);
183 return USBS_CONTROL_RETURN_HANDLED;
186 // --------------------------------------------------------------------------
187 // Handler for the ACM class messages.
189 static usbs_control_return
190 acm_class_handler(usbs_control_endpoint* ep0, void* data)
192 usbs_control_return result = USBS_CONTROL_RETURN_UNKNOWN;
194 usb_devreq *req = (usb_devreq *) ep0->control_buffer;
196 static cyg_uint8 rsp_buf[32];
198 DBG("ACM Class Handler\n");
200 switch (req->request) {
202 case USBS_SERIAL_SET_LINE_CODING :
203 DBG("Set Line Coding\n");
204 memset(acm_buf, 0, 32);
205 ep0->buffer = acm_buf;
206 ep0->buffer_size = 7;
207 ep0->complete_fn = acm_set_line_coding;
208 result = USBS_CONTROL_RETURN_HANDLED;
211 case USBS_SERIAL_GET_LINE_CODING :
212 DBG("Get Line Coding\n");
213 rsp_buf[0] = baud & 0xFF;
214 rsp_buf[1] = (baud >> 8) & 0xFF;
215 rsp_buf[2] = (baud >> 16) & 0xFF;
216 rsp_buf[3] = (baud >> 24) & 0xFF;
217 rsp_buf[4] = 0; // One stop bit
218 rsp_buf[5] = 0; // No parity
219 rsp_buf[6] = 8; // 8 data bits
220 ep0->buffer = rsp_buf;
221 ep0->buffer_size = 7;
222 result = USBS_CONTROL_RETURN_HANDLED;
226 DBG("*** Unhandled ACM Request: 0x%02X ***\n",
227 (unsigned) req->request);
233 #endif // CYGDAT_IO_USB_SLAVE_CLASS_TYPE_ACM
235 // --------------------------------------------------------------------------
236 // Thread receives packets from the USB and sends them out the serial port
237 // It uses a buffered stdio input, an un-buffered low-level file output.
238 // This isn't terribly efficient, but rather an example of both methods.
240 void usb2ser_func(cyg_addrword_t data)
243 FILE *rxf = fopen(USB_RX_DEV, "r");
244 int txh = open(SER_DEV, O_WRONLY, 0);
246 DBG("Usb2Ser: Thread starting\n");
249 DBG("Error opening USB rx port\n");
254 DBG("Error opening serial tx port\n");
258 // Give the USB receiver an adequate buffer.
259 setvbuf(rxf, usb2ser_buf, _IOFBF, BUF_SIZE);
263 // ----- Wait for the host to configure -----
265 DBG("Usb2Ser: Waiting for USB configuration\n");
266 usbs_serial_wait_until_configured();
267 cyg_thread_delay((cyg_tick_count_t) 10);
269 // ----- While configured read data & send out serial port -----
271 DBG("Usb2Ser: USB configured\n");
272 while (usbs_serial_is_configured()) {
273 if ((c = getc(rxf)) < 0) {
274 DBG("*** USB Read Error: %d ***\n", c);
284 // --------------------------------------------------------------------------
285 // Thread receives packets from the serial port and sends them out the USB
286 // It uses a buffered stdio input, an un-buffered low-level file output.
287 // This isn't terribly efficient, but rather an example of both methods.
289 void ser2usb_func(cyg_addrword_t data)
292 FILE *rxf = fopen(SER_DEV, "r");
293 int txh = open(USB_TX_DEV, O_WRONLY, 0);
295 DBG("Ser2Usb: Thread starting\n");
298 DBG("Error opening serial rx port\n");
303 DBG("Error opening USB tx port\n");
309 // ----- Wait for the host to configure -----
311 DBG("Ser2Usb: Waiting for USB configuration\n");
312 usbs_serial_wait_until_configured();
313 cyg_thread_delay((cyg_tick_count_t) 10);
315 // ----- While configured read data & send out serial port -----
317 DBG("Ser2Usb: USB configured\n");
318 while (usbs_serial_is_configured()) {
319 if ((c = getc(rxf)) < 0) {
320 DBG("*** Console Read Error: %d ***\n", c);
330 // --------------------------------------------------------------------------
331 // Application Startup
332 // --------------------------------------------------------------------------
334 void cyg_user_start(void)
336 DBG("Entering cyg_user_start() function\n");
338 #ifdef CYGDAT_IO_USB_SLAVE_CLASS_TYPE_ACM
339 // Override the class handler to use ours.
340 usbs_serial_ep0->class_control_fn = acm_class_handler;
343 cyg_thread_create(4, usb2ser_func, (cyg_addrword_t) 0,
344 "Usb2Serial", (void *) stack[0], THREAD_STACK_SIZE,
345 &usb2ser_thread, &thread[0]);
347 cyg_thread_create(4, ser2usb_func, (cyg_addrword_t) 1,
348 "Serial2Usb", (void *) stack[1], THREAD_STACK_SIZE,
349 &ser2usb_thread, &thread[1]);
351 // Start USB subsystem
354 // Start the threads running.
355 cyg_thread_resume(usb2ser_thread);
356 cyg_thread_resume(ser2usb_thread);