/* * This file is part of the libopenstm32 project. * * Copyright (C) 2010 Gareth McMullin * * 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 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include "usb_private.h" static struct usb_control_state { enum { IDLE, STALLED, DATA_IN, LAST_DATA_IN, STATUS_IN, DATA_OUT, LAST_DATA_OUT, STATUS_OUT } state; struct usb_setup_data req; uint8_t *ctrl_buf; uint16_t ctrl_len; void (*complete)(struct usb_setup_data *req); } control_state; /** Register application callback function for handling of usb control * request. */ int usbd_register_control_callback(uint8_t type, uint8_t type_mask, usbd_control_callback callback) { int i; for(i = 0; i < MAX_USER_CONTROL_CALLBACK; i++) { if(_usbd_device.user_control_callback[i].cb) continue; _usbd_device.user_control_callback[i].type = type; _usbd_device.user_control_callback[i].type_mask = type_mask; _usbd_device.user_control_callback[i].cb = callback; return 0; } return -1; } static void usb_control_send_chunk(void) { if(_usbd_device.desc->bMaxPacketSize0 < control_state.ctrl_len) { /* Data stage, normal transmission */ usbd_ep_write_packet(0, control_state.ctrl_buf, _usbd_device.desc->bMaxPacketSize0); control_state.state = DATA_IN; control_state.ctrl_buf += _usbd_device.desc->bMaxPacketSize0; control_state.ctrl_len -= _usbd_device.desc->bMaxPacketSize0; } else { /* Data stage, end of transmission */ usbd_ep_write_packet(0, control_state.ctrl_buf, control_state.ctrl_len); control_state.state = LAST_DATA_IN; control_state.ctrl_len = 0; control_state.ctrl_buf = NULL; } } static int usb_control_recv_chunk(void) { uint16_t packetsize = MIN(_usbd_device.desc->bMaxPacketSize0, control_state.req.wLength - control_state.ctrl_len); uint16_t size = usbd_ep_read_packet(0, control_state.ctrl_buf + control_state.ctrl_len, packetsize); if (size != packetsize) { usbd_ep_stall_set(0, 1); return -1; } control_state.ctrl_len += size; return packetsize; } static int usb_control_request_dispatch(struct usb_setup_data *req) { int result = 0; int i; struct user_control_callback *cb = _usbd_device.user_control_callback; /* Call user command hook function */ for(i = 0; i < MAX_USER_CONTROL_CALLBACK; i++) { if(cb[i].cb == NULL) break; if((req->bmRequestType & cb[i].type_mask) == cb[i].type) { result = cb[i].cb(req, &control_state.ctrl_buf, &control_state.ctrl_len, &control_state.complete); if(result) return result; } } /* Try standard request if not already handled */ return _usbd_standard_request(req, &control_state.ctrl_buf, &control_state.ctrl_len); } /* Handle commands and read requests. */ static void usb_control_setup_read(struct usb_setup_data *req) { control_state.ctrl_buf = _usbd_device.ctrl_buf; control_state.ctrl_len = req->wLength; if(usb_control_request_dispatch(req)) { if(control_state.ctrl_len) { /* Go to data out stage if handled */ usb_control_send_chunk(); } else { /* Go to status stage if handled */ usbd_ep_write_packet(0, NULL, 0); control_state.state = STATUS_IN; } } else { /* Stall endpoint on failure */ usbd_ep_stall_set(0, 1); } } static void usb_control_setup_write(struct usb_setup_data *req) { if(req->wLength > _usbd_device.ctrl_buf_len) { usbd_ep_stall_set(0, 1); return; } /* Buffer into which to write received data */ control_state.ctrl_buf = _usbd_device.ctrl_buf; control_state.ctrl_len = 0; /* Wait for DATA OUT Stage */ if(req->wLength > _usbd_device.desc->bMaxPacketSize0) control_state.state = DATA_OUT; else control_state.state = LAST_DATA_OUT; } void _usbd_control_setup(uint8_t ea) { struct usb_setup_data *req = &control_state.req; (void)ea; control_state.complete = NULL; if(usbd_ep_read_packet(0, req, 8) != 8) { usbd_ep_stall_set(0, 1); return; } if(req->wLength == 0) { usb_control_setup_read(req); } else if(req->bmRequestType & 0x80) { usb_control_setup_read(req); } else { usb_control_setup_write(req); } } void _usbd_control_out(uint8_t ea) { (void)ea; switch(control_state.state) { case DATA_OUT: if(usb_control_recv_chunk() < 0) break; if((control_state.req.wLength - control_state.ctrl_len) <= _usbd_device.desc->bMaxPacketSize0) control_state.state = LAST_DATA_OUT; break; case LAST_DATA_OUT: { int result = 0; if(usb_control_recv_chunk() < 0) break; /* We have now received the full data payload. * Invoke callback to process. */ if(usb_control_request_dispatch(&control_state.req)) { /* Got to status stage on success */ usbd_ep_write_packet(0, NULL, 0); control_state.state = STATUS_IN; } else { usbd_ep_stall_set(0, 1); } break; } case STATUS_OUT: { usbd_ep_read_packet(0, NULL, 0); control_state.state = IDLE; if (control_state.complete) control_state.complete(&control_state.req); control_state.complete = NULL; break; } default: usbd_ep_stall_set(0, 1); } } void _usbd_control_in(uint8_t ea) { (void)ea; switch(control_state.state) { case DATA_IN: usb_control_send_chunk(); break; case LAST_DATA_IN: control_state.state = STATUS_OUT; break; case STATUS_IN: { struct usb_setup_data *req = &control_state.req; if (control_state.complete) control_state.complete(&control_state.req); /* Exception: Handle SET ADDRESS function here... */ if((req->bmRequestType == 0) && (req->bRequest == USB_REQ_SET_ADDRESS)) _usbd_hw_set_address(req->wValue); control_state.state = IDLE; break; } default: usbd_ep_stall_set(0, 1); } }