fuzev2: prevent button light flickering when accessing µSD
[kugel-rb.git] / firmware / usbstack / usb_serial.c
blobe7159099fe247d53f74a4190578951318bc5d463
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007 by Christian Gmeiner
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "string.h"
22 #include "system.h"
23 #include "usb_core.h"
24 #include "usb_drv.h"
25 #include "kernel.h"
26 #include "usb_serial.h"
27 #include "usb_class_driver.h"
28 /*#define LOGF_ENABLE*/
29 #include "logf.h"
31 /* serial interface */
32 static struct usb_interface_descriptor __attribute__((aligned(2)))
33 interface_descriptor =
35 .bLength = sizeof(struct usb_interface_descriptor),
36 .bDescriptorType = USB_DT_INTERFACE,
37 .bInterfaceNumber = 0,
38 .bAlternateSetting = 0,
39 .bNumEndpoints = 2,
40 .bInterfaceClass = USB_CLASS_CDC_DATA,
41 .bInterfaceSubClass = 0,
42 .bInterfaceProtocol = 0,
43 .iInterface = 0
47 static struct usb_endpoint_descriptor __attribute__((aligned(2)))
48 endpoint_descriptor =
50 .bLength = sizeof(struct usb_endpoint_descriptor),
51 .bDescriptorType = USB_DT_ENDPOINT,
52 .bEndpointAddress = 0,
53 .bmAttributes = USB_ENDPOINT_XFER_BULK,
54 .wMaxPacketSize = 0,
55 .bInterval = 0
58 #define BUFFER_SIZE 512
59 static unsigned char send_buffer[BUFFER_SIZE]
60 USB_DEVBSS_ATTR __attribute__((aligned(32)));
61 static unsigned char receive_buffer[32]
62 USB_DEVBSS_ATTR __attribute__((aligned(32)));
64 static void sendout(void);
66 static int buffer_start;
67 /* The number of bytes to transfer that haven't been given to the USB stack yet */
68 static int buffer_length;
69 /* The number of bytes to transfer that have been given to the USB stack */
70 static int buffer_transitlength;
71 static bool active = false;
73 static int ep_in, ep_out;
74 static int usb_interface;
76 int usb_serial_request_endpoints(struct usb_class_driver *drv)
78 ep_in = usb_core_request_endpoint(USB_ENDPOINT_XFER_BULK, USB_DIR_IN, drv);
79 if (ep_in < 0)
80 return -1;
82 ep_out = usb_core_request_endpoint(USB_ENDPOINT_XFER_BULK, USB_DIR_OUT,
83 drv);
84 if (ep_out < 0) {
85 usb_core_release_endpoint(ep_in);
86 return -1;
89 return 0;
92 int usb_serial_set_first_interface(int interface)
94 usb_interface = interface;
95 return interface + 1;
98 int usb_serial_get_config_descriptor(unsigned char *dest, int max_packet_size)
100 unsigned char *orig_dest = dest;
102 interface_descriptor.bInterfaceNumber = usb_interface;
103 PACK_DATA(dest, interface_descriptor);
105 endpoint_descriptor.wMaxPacketSize = max_packet_size;
107 endpoint_descriptor.bEndpointAddress = ep_in;
108 PACK_DATA(dest, endpoint_descriptor);
110 endpoint_descriptor.bEndpointAddress = ep_out;
111 PACK_DATA(dest, endpoint_descriptor);
113 return (dest - orig_dest);
116 /* called by usb_core_control_request() */
117 bool usb_serial_control_request(struct usb_ctrlrequest* req, unsigned char* dest)
119 bool handled = false;
121 (void)dest;
122 switch (req->bRequest) {
123 default:
124 logf("serial: unhandeld req %d", req->bRequest);
126 return handled;
129 void usb_serial_init_connection(void)
131 /* prime rx endpoint */
132 usb_drv_recv(ep_out, receive_buffer, sizeof receive_buffer);
134 /* we come here too after a bus reset, so reset some data */
135 buffer_transitlength = 0;
136 if(buffer_length>0)
138 sendout();
140 active=true;
143 /* called by usb_code_init() */
144 void usb_serial_init(void)
146 logf("serial: init");
147 buffer_start = 0;
148 buffer_length = 0;
149 buffer_transitlength = 0;
152 void usb_serial_disconnect(void)
154 active = false;
157 static void sendout(void)
159 buffer_transitlength = MIN(buffer_length,BUFFER_SIZE-buffer_start);
160 /* For unknown reasons packets larger than 96 bytes are not sent. We play
161 * safe and limit to 32. TODO: find the real bug */
162 buffer_transitlength = MIN(buffer_transitlength,32);
163 if(buffer_transitlength > 0)
165 buffer_length -= buffer_transitlength;
166 usb_drv_send_nonblocking(ep_in, &send_buffer[buffer_start],
167 buffer_transitlength);
171 void usb_serial_send(const unsigned char *data,int length)
173 int freestart, available_end_space, i;
175 if (!active||length<=0)
176 return;
178 i=buffer_start+buffer_length+buffer_transitlength;
179 freestart=i%BUFFER_SIZE;
180 available_end_space=BUFFER_SIZE-i;
182 if (0>=available_end_space)
184 /* current buffer wraps, so new data can't wrap */
185 int available_space = BUFFER_SIZE -
186 (buffer_length + buffer_transitlength);
188 length = MIN(length,available_space);
189 memcpy(&send_buffer[freestart],data,length);
190 buffer_length+=length;
192 else
194 /* current buffer doesn't wrap, so new data might */
195 int first_chunk = MIN(length,available_end_space);
197 memcpy(&send_buffer[freestart],data,first_chunk);
198 length-=first_chunk;
199 buffer_length+=first_chunk;
200 if(length>0)
202 /* wrap */
203 memcpy(&send_buffer[0],&data[first_chunk],MIN(length,buffer_start));
204 buffer_length+=MIN(length,buffer_start);
208 if (buffer_transitlength==0)
209 sendout();
210 /* else do nothing. The transfer completion handler will pick it up */
213 /* called by usb_core_transfer_complete() */
214 void usb_serial_transfer_complete(int ep,int dir, int status, int length)
216 (void)ep;
217 (void)length;
219 switch (dir) {
220 case USB_DIR_OUT:
221 logf("serial: %s", receive_buffer);
222 /* Data received. TODO : Do something with it ? */
224 /* Get the next bit */
225 usb_drv_recv(ep_out, receive_buffer, sizeof receive_buffer);
226 break;
228 case USB_DIR_IN:
229 /* Data sent out. Update circular buffer */
230 if(status == 0)
232 /* TODO: Handle (length != buffer_transitlength) */
234 buffer_start=(buffer_start+buffer_transitlength)%BUFFER_SIZE;
235 buffer_transitlength = 0;
238 if(buffer_length>0)
239 sendout();
240 break;