]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/io/usb/serial/slave/v2_0/tests/usb2serial.c
Initial revision
[karo-tx-redboot.git] / packages / io / usb / serial / slave / v2_0 / tests / usb2serial.c
1 //==========================================================================
2 //
3 //      usb2serial.c
4 //
5 //      Example application for the USB serial layer in eCos.
6 //
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 //
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.
15 //
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
19 // for more details.
20 //
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.
24 //
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.
31 //
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.
34 //
35 // -------------------------------------------
36 //####ECOSGPLCOPYRIGHTEND####
37 //===========================================================================
38 //#####DESCRIPTIONBEGIN####
39 //
40 // Author(s):    Frank M. Pagliughi (fmp), SoRo Systems, Inc.
41 // Contributors: 
42 // Date:         2008-06-02
43 // Description:  USB serial example application.
44 //
45 //####DESCRIPTIONEND####
46 //===========================================================================
47
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>
53
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>
58
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <unistd.h>
62 #include <fcntl.h>
63 #include <sys/stat.h>
64
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.
69 // 
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 
72 // communications.
73 // 
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.
80 // 
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
83 // target platform.
84 // 
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.
87
88
89 // Comment this line out to remove debug output.
90 #define DEBUG_OUTPUT
91
92 #if defined(DEBUG_OUTPUT)
93 #define DBG diag_printf
94 #else
95 #define DBG (1) ? (void)0 : diag_printf
96 #endif
97
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"
102
103 // Set this for any available serial port on the target.
104 #define SER_DEV     "/dev/ser0"
105
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
108 // helpful.
109 #define BUF_SIZE 4096
110 static char usb2ser_buf[BUF_SIZE];
111
112 // The threads
113 cyg_thread thread[2];
114
115 // Space for two 4K stacks
116 #define THREAD_STACK_SIZE 4096
117 char stack[2][THREAD_STACK_SIZE];
118
119 // The handles for the threads
120 cyg_handle_t    usb2ser_thread, 
121                 ser2usb_thread;
122
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, 
126 // etc).
127
128 #ifdef CYGDAT_IO_USB_SLAVE_CLASS_TYPE_ACM
129
130 static cyg_uint8    acm_buf[32];
131 static cyg_uint32   baud = 34800;
132
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.
140
141 static usbs_control_return
142 acm_set_line_coding(usbs_control_endpoint* ep0, int n)
143 {
144   int err;
145   cyg_uint32 req_baud;
146   cyg_io_handle_t handle;
147   
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]);
151   
152   DBG("Set Baud: %u\n", (unsigned) baud);
153   
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);
158     
159     switch (baud) {
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;
163       default:
164         DBG("Unsupported baud rate\n");
165         return USBS_CONTROL_RETURN_HANDLED;
166     }
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;
170     ser_info.flags = 0;
171     
172     err = cyg_io_set_config(handle, CYG_IO_SET_CONFIG_SERIAL_INFO, 
173                             &ser_info, &len);
174     if (err == 0)
175       baud = req_baud;
176     else {
177       DBG("Error setting serial params\n");
178     }
179   }
180   else {
181     DBG("Error looking up serial device: %s\n", SER_DEV);
182   }
183   return USBS_CONTROL_RETURN_HANDLED;
184 }
185
186 // --------------------------------------------------------------------------
187 // Handler for the ACM class messages.
188 // 
189 static usbs_control_return 
190 acm_class_handler(usbs_control_endpoint* ep0, void* data)
191 {
192   usbs_control_return result = USBS_CONTROL_RETURN_UNKNOWN;
193   
194   usb_devreq  *req = (usb_devreq *) ep0->control_buffer;
195   
196   static cyg_uint8 rsp_buf[32];
197   
198   DBG("ACM Class Handler\n");
199   
200   switch (req->request) {
201     
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;
209       break;
210       
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;
223       break;
224       
225     default :
226       DBG("*** Unhandled ACM Request: 0x%02X ***\n",
227           (unsigned) req->request);
228   }
229   
230   return result;
231 }
232
233 #endif      // CYGDAT_IO_USB_SLAVE_CLASS_TYPE_ACM
234
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.
239
240 void usb2ser_func(cyg_addrword_t data)
241 {
242   int  c;
243   FILE *rxf = fopen(USB_RX_DEV, "r");
244   int   txh = open(SER_DEV, O_WRONLY, 0);
245   
246   DBG("Usb2Ser: Thread starting\n");
247   
248   if (!rxf) {
249     DBG("Error opening USB rx port\n");
250     return;
251   }
252   
253   if (txh < 0) {
254     DBG("Error opening serial tx port\n");
255     return;
256   }
257   
258   // Give the USB receiver an adequate buffer.
259   setvbuf(rxf, usb2ser_buf, _IOFBF, BUF_SIZE);
260   
261   while (1) {
262     
263     // ----- Wait for the host to configure -----
264     
265     DBG("Usb2Ser: Waiting for USB configuration\n");
266     usbs_serial_wait_until_configured();
267     cyg_thread_delay((cyg_tick_count_t) 10);
268     
269     // ----- While configured read data & send out serial port -----
270     
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);
275       }
276       else {
277         char ch = (char) c;
278         write(txh, &ch, 1);
279       }
280     }
281   }
282 }
283
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.
288
289 void ser2usb_func(cyg_addrword_t data)
290 {
291   int  c;
292   FILE *rxf = fopen(SER_DEV, "r");
293   int  txh = open(USB_TX_DEV, O_WRONLY, 0);
294   
295   DBG("Ser2Usb: Thread starting\n");
296   
297   if (!rxf) {
298     DBG("Error opening serial rx port\n");
299     return;
300   }
301   
302   if (txh < 0) {
303     DBG("Error opening USB tx port\n");
304     return;
305   }
306   
307   while (1) {
308     
309     // ----- Wait for the host to configure -----
310     
311     DBG("Ser2Usb: Waiting for USB configuration\n");
312     usbs_serial_wait_until_configured();
313     cyg_thread_delay((cyg_tick_count_t) 10);
314     
315     // ----- While configured read data & send out serial port -----
316     
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);
321       }
322       else {
323         char ch = (char) c;
324         write(txh, &ch, 1);
325       }
326     }
327   }
328 }
329
330 // --------------------------------------------------------------------------
331 //  Application Startup
332 // --------------------------------------------------------------------------
333
334 void cyg_user_start(void)
335 {
336   DBG("Entering cyg_user_start() function\n");
337   
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;
341 #endif
342   
343   cyg_thread_create(4, usb2ser_func, (cyg_addrword_t) 0,
344                     "Usb2Serial", (void *) stack[0], THREAD_STACK_SIZE,
345                     &usb2ser_thread, &thread[0]);
346   
347   cyg_thread_create(4, ser2usb_func, (cyg_addrword_t) 1,
348                     "Serial2Usb", (void *) stack[1], THREAD_STACK_SIZE,
349                     &ser2usb_thread, &thread[1]);
350   
351   // Start USB subsystem
352   usbs_serial_start();
353   
354   // Start the threads running.
355   cyg_thread_resume(usb2ser_thread);
356   cyg_thread_resume(ser2usb_thread);
357 }
358