/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2007 by Christian Gmeiner * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "string.h" #include "system.h" #include "usb_core.h" #include "usb_drv.h" #include "kernel.h" #include "usb_serial.h" //#define LOGF_ENABLE #include "logf.h" #ifdef USB_SERIAL /* serial interface */ static struct usb_interface_descriptor __attribute__((aligned(2))) interface_descriptor = { .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 0, .bAlternateSetting = 0, .bNumEndpoints = 2, .bInterfaceClass = USB_CLASS_CDC_DATA, .bInterfaceSubClass = 0, .bInterfaceProtocol = 0, .iInterface = 0 }; static struct usb_endpoint_descriptor __attribute__((aligned(2))) endpoint_descriptor = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = 0, .bInterval = 0 }; #define BUFFER_SIZE 512 /* Max 16k because of controller limitations */ #if CONFIG_CPU == IMX31L static unsigned char send_buffer[BUFFER_SIZE] USBDEVBSS_ATTR __attribute__((aligned(32))); static unsigned char receive_buffer[32] USBDEVBSS_ATTR __attribute__((aligned(32))); #else static unsigned char send_buffer[BUFFER_SIZE] __attribute__((aligned(32))); static unsigned char receive_buffer[32] __attribute__((aligned(32))); #endif static bool busy_sending = false; static int buffer_start; static int buffer_length; static bool active = false; static int usb_endpoint; static int usb_interface; static struct mutex sendlock SHAREDBSS_ATTR; static void sendout(void) { if(buffer_start+buffer_length > BUFFER_SIZE) { /* Buffer wraps. Only send the first part */ usb_drv_send_nonblocking(usb_endpoint, &send_buffer[buffer_start], (BUFFER_SIZE - buffer_start)); } else { /* Send everything */ usb_drv_send_nonblocking(usb_endpoint, &send_buffer[buffer_start], buffer_length); } busy_sending=true; } int usb_serial_set_first_endpoint(int endpoint) { usb_endpoint = endpoint; return endpoint + 1; } int usb_serial_set_first_interface(int interface) { usb_interface = interface; return interface + 1; } int usb_serial_get_config_descriptor(unsigned char *dest,int max_packet_size) { unsigned char *orig_dest = dest; endpoint_descriptor.wMaxPacketSize=max_packet_size; interface_descriptor.bInterfaceNumber=usb_interface; memcpy(dest,&interface_descriptor,sizeof(struct usb_interface_descriptor)); dest+=sizeof(struct usb_interface_descriptor); endpoint_descriptor.bEndpointAddress = usb_endpoint | USB_DIR_IN; memcpy(dest,&endpoint_descriptor,sizeof(struct usb_endpoint_descriptor)); dest+=sizeof(struct usb_endpoint_descriptor); endpoint_descriptor.bEndpointAddress = usb_endpoint | USB_DIR_OUT; memcpy(dest,&endpoint_descriptor,sizeof(struct usb_endpoint_descriptor)); dest+=sizeof(struct usb_endpoint_descriptor); return (dest - orig_dest); } void usb_serial_init_connection(void) { /* prime rx endpoint */ usb_drv_recv(usb_endpoint, receive_buffer, sizeof receive_buffer); /* we come here too after a bus reset, so reset some data */ mutex_lock(&sendlock); busy_sending = false; if(buffer_length>0) { sendout(); } mutex_unlock(&sendlock); } /* called by usb_code_init() */ void usb_serial_init(void) { logf("serial: init"); busy_sending = false; buffer_start = 0; buffer_length = 0; active = true; mutex_init(&sendlock); } void usb_serial_disconnect(void) { active = false; } void usb_serial_send(unsigned char *data,int length) { if(!active) return; if(length<=0) return; mutex_lock(&sendlock); if(buffer_start+buffer_length > BUFFER_SIZE) { /* current buffer wraps, so new data can't */ int available_space = BUFFER_SIZE - buffer_length; length=MIN(length,available_space); memcpy(&send_buffer[(buffer_start+buffer_length)%BUFFER_SIZE], data,length); buffer_length+=length; } else { /* current buffer doesn't wrap, so new data might */ int available_end_space = (BUFFER_SIZE - (buffer_start+buffer_length)); int first_chunk = MIN(length,available_end_space); memcpy(&send_buffer[buffer_start + buffer_length],data,first_chunk); length-=first_chunk; buffer_length+=first_chunk; if(length>0) { /* wrap */ memcpy(&send_buffer[0],&data[first_chunk],MIN(length,buffer_start)); buffer_length+=MIN(length,buffer_start); } } if(busy_sending) { /* Do nothing. The transfer completion handler will pick it up */ } else { sendout(); } mutex_unlock(&sendlock); } /* called by usb_core_transfer_complete() */ void usb_serial_transfer_complete(int ep,bool in, int status, int length) { (void)ep; switch (in) { case false: logf("serial: %s", receive_buffer); /* Data received. TODO : Do something with it ? */ usb_drv_recv(usb_endpoint, receive_buffer, sizeof receive_buffer); break; case true: mutex_lock(&sendlock); /* Data sent out. Update circular buffer */ if(status == 0) { buffer_start = (buffer_start + length)%BUFFER_SIZE; buffer_length -= length; } busy_sending = false; if(buffer_length>0) { sendout(); } mutex_unlock(&sendlock); break; } } /* called by usb_core_control_request() */ bool usb_serial_control_request(struct usb_ctrlrequest* req) { bool handled = false; switch (req->bRequest) { default: logf("serial: unhandeld req %d", req->bRequest); } return handled; } #endif /*USB_SERIAL*/