/* * Copyright (c) 2012, Philippe Retornaz * Copyright (c) 2012, EPFL STI IMT LSRO1 -- Mobots group * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * \addtogroup cc2538-usb * @{ * * \file * Platform process which implements a UART-like functionality over the * cc2538's USB hardware. * * This has been ported over from platform/cc2530dk * * \author * - Philippe Retornaz (EPFL) - Original code * - George Oikonomou - * Turned this to a 'serial over USB' platform process, ported for cc2538 */ #include "contiki.h" #include "sys/process.h" #include "net/linkaddr.h" #include "usb-api.h" #include "usb.h" #include "usb-arch.h" #include "cdc-acm/cdc-acm.h" #include "ieee-addr.h" #include /*---------------------------------------------------------------------------*/ #define DEBUG 0 #if DEBUG #include #define PRINTF(...) printf(__VA_ARGS__) #else #define PRINTF(...) #endif /*---------------------------------------------------------------------------*/ struct lang_id { uint8_t size; uint8_t type; uint16_t langid; }; static const struct lang_id lang_id = { sizeof(lang_id), 3, 0x0409 }; /*---------------------------------------------------------------------------*/ struct serial_nr { uint8_t size; uint8_t type; uint16_t string[16]; }; static struct serial_nr serial_nr = { sizeof(serial_nr), 3, { 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A' } }; /*---------------------------------------------------------------------------*/ struct manufacturer { uint8_t size; uint8_t type; uint16_t string[17]; }; static const struct manufacturer manufacturer = { sizeof(manufacturer), 3, { 'T', 'e', 'x', 'a', 's', ' ', 'I', 'n', 's', 't', 'r', 'u', 'm', 'e', 'n', 't', 's' } }; /*---------------------------------------------------------------------------*/ struct product { uint8_t size; uint8_t type; uint16_t string[21]; }; static const struct product product = { sizeof(product), 3, { 'c', 'c', '2', '5', '3', '8', ' ', 'S', 'y', 's', 't', 'e', 'm', '-', 'o', 'n', '-', 'C', 'h', 'i', 'p' } }; /*---------------------------------------------------------------------------*/ #define EPIN 0x82 #define EPOUT 0x03 #define RX_BUFFER_SIZE USB_EP3_SIZE #define TX_BUFFER_SIZE (USB_EP2_SIZE - 1) typedef struct _USBBuffer usb_buffer; static usb_buffer data_rx_urb; static usb_buffer data_tx_urb; static uint8_t usb_rx_data[RX_BUFFER_SIZE]; static uint8_t enabled = 0; #define SLIP_END 0300 static uint8_t usb_tx_data[TX_BUFFER_SIZE]; static uint8_t buffered_data = 0; /* Callback to the input handler */ static int (* input_handler)(unsigned char c); /*---------------------------------------------------------------------------*/ uint8_t * usb_class_get_string_descriptor(uint16_t lang, uint8_t string) { switch (string) { case 0: return (uint8_t *)&lang_id; case 1: return (uint8_t *)&manufacturer; case 2: return (uint8_t *)&product; case 3: return (uint8_t *)&serial_nr; default: return NULL; } } /*---------------------------------------------------------------------------*/ static void set_serial_number(void) { uint8_t i; uint8_t ieee[8]; uint8_t lown, highn; uint8_t c; ieee_addr_cpy_to(ieee, 8); for(i = 0; i < 8; i++) { lown = ieee[i] & 0x0F; highn = ieee[i] >> 4; c = lown > 9 ? 'A' + lown - 0xA : lown + '0'; serial_nr.string[i * 2 + 1] = c; c = highn > 9 ? 'A' + highn - 0xA : highn + '0'; serial_nr.string[i * 2] = c; } } /*---------------------------------------------------------------------------*/ static void queue_rx_urb(void) { data_rx_urb.flags = USB_BUFFER_PACKET_END; data_rx_urb.flags |= USB_BUFFER_NOTIFY; data_rx_urb.data = usb_rx_data; data_rx_urb.left = RX_BUFFER_SIZE; data_rx_urb.next = NULL; usb_submit_recv_buffer(EPOUT, &data_rx_urb); } /*---------------------------------------------------------------------------*/ static void do_work(void) { unsigned int events; events = usb_get_global_events(); if(events & USB_EVENT_CONFIG) { /* Configure EPs. Don't enable them yet, until the CDC line is up */ usb_setup_bulk_endpoint(EPIN); usb_setup_bulk_endpoint(EPOUT); queue_rx_urb(); } if(events & USB_EVENT_RESET) { enabled = 0; } events = usb_cdc_acm_get_events(); if(events & USB_CDC_ACM_LINE_STATE) { uint8_t line_state = usb_cdc_acm_get_line_state(); PRINTF("CDC-ACM event 0x%04x, Line State = %u\n", events, line_state); if(line_state & USB_CDC_ACM_DTE) { enabled = 1; } else { enabled = 0; } } if(!enabled) { return; } events = usb_get_ep_events(EPOUT); if((events & USB_EP_EVENT_NOTIFICATION) && !(data_rx_urb.flags & USB_BUFFER_SUBMITTED)) { if(!(data_rx_urb.flags & USB_BUFFER_FAILED)) { if(input_handler) { uint8_t len; uint8_t i; len = RX_BUFFER_SIZE - data_rx_urb.left; for(i = 0; i < len; i++) { input_handler(usb_rx_data[i]); } } } queue_rx_urb(); } } /*---------------------------------------------------------------------------*/ void usb_serial_flush() { if(buffered_data == 0) { return; } data_tx_urb.flags = USB_BUFFER_SHORT_END; data_tx_urb.flags |= USB_BUFFER_NOTIFY; data_tx_urb.next = NULL; data_tx_urb.data = usb_tx_data; data_tx_urb.left = buffered_data; buffered_data = 0; usb_submit_xmit_buffer(EPIN, &data_tx_urb); } /*---------------------------------------------------------------------------*/ void usb_serial_writeb(uint8_t b) { if(!enabled) { buffered_data = 0; return; } usb_tx_data[buffered_data] = b; buffered_data++; if(buffered_data == TX_BUFFER_SIZE) { usb_serial_flush(); } } /*---------------------------------------------------------------------------*/ PROCESS(usb_serial_process, "USB-Serial process"); /*---------------------------------------------------------------------------*/ PROCESS_THREAD(usb_serial_process, ev, data) { PROCESS_BEGIN(); set_serial_number(); usb_setup(); usb_cdc_acm_setup(); usb_set_global_event_process(&usb_serial_process); usb_cdc_acm_set_event_process(&usb_serial_process); usb_set_ep_event_process(EPIN, &usb_serial_process); usb_set_ep_event_process(EPOUT, &usb_serial_process); while(1) { PROCESS_WAIT_EVENT(); if(ev == PROCESS_EVENT_EXIT) { break; } if(ev == PROCESS_EVENT_POLL) { do_work(); } } PROCESS_END(); } /*---------------------------------------------------------------------------*/ void usb_serial_set_input(int (* input)(unsigned char c)) { input_handler = input; } /*---------------------------------------------------------------------------*/ void usb_serial_init() { process_start(&usb_serial_process, NULL); } /*---------------------------------------------------------------------------*/ /** @} */