| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356 |
- /* This file is the part of the Lightweight USB device Stack for STM32 microcontrollers
- *
- * Copyright ©2016 Dmitry Filimonchuk <dmitrystu[at]gmail[dot]com>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #include <stdint.h>
- #include <stdbool.h>
- #include "usb.h"
- #define _MIN(a, b) ((a) < (b)) ? (a) : (b)
- static void usbd_process_ep0 (usbd_device *dev, uint8_t event, uint8_t ep);
- /** \brief Resets USB device state
- * \param dev pointer to usb device
- * \return none
- */
- static void usbd_process_reset(usbd_device *dev) {
- dev->status.device_state = usbd_state_default;
- dev->status.control_state = usbd_ctl_idle;
- dev->status.device_cfg = 0;
- dev->driver->ep_config(0, USB_EPTYPE_CONTROL, dev->status.ep0size);
- dev->endpoint[0] = usbd_process_ep0;
- dev->driver->setaddr(0);
- }
- /** \brief Callback that sets USB device address
- * \param dev pointer to usb device
- * \param req pointer to usb control request data
- * \return none
- */
- static void usbd_set_address (usbd_device *dev, usbd_ctlreq *req) {
- dev->driver->setaddr(req->wValue);
- dev->status.device_state = (req->wValue) ? usbd_state_addressed : usbd_state_default;
- }
- /** \brief Control transfer completion callback processing
- * \param dev pointer to the usb device
- * \return none
- */
- static void usbd_process_callback (usbd_device *dev) {
- if (dev->complete_callback) {
- dev->complete_callback(dev, dev->status.data_buf);
- dev->complete_callback = 0;
- }
- }
- /** \brief SET_CONFIG request processing
- * \param dev usbd_device
- * \param config config number from request
- * \return usbd_ack if success
- */
- static usbd_respond usbd_configure(usbd_device *dev, uint8_t config) {
- if (dev->config_callback) {
- if (dev->config_callback(dev, config) == usbd_ack) {
- dev->status.device_cfg = config;
- dev->status.device_state = (config) ? usbd_state_configured : usbd_state_addressed;
- return usbd_ack;
- }
- }
- return usbd_fail;
- }
- /** \brief Standard control request processing for device
- * \param dev pointer to usb device
- * \param req pointer to control request
- * \return TRUE if request is handled
- */
- static usbd_respond usbd_process_devrq (usbd_device *dev, usbd_ctlreq *req) {
- switch (req->bRequest) {
- case USB_STD_CLEAR_FEATURE:
- /* not yet supported */
- break;
- case USB_STD_GET_CONFIG:
- req->data[0] = dev->status.device_cfg;
- return usbd_ack;
- case USB_STD_GET_DESCRIPTOR:
- if (req->wValue == ((USB_DTYPE_STRING << 8) | INTSERIALNO_DESCRIPTOR )) {
- dev->status.data_count = dev->driver->get_serialno_desc(req->data);
- return usbd_ack;
- } else {
- if (dev->descriptor_callback) {
- return dev->descriptor_callback(req, &(dev->status.data_ptr), &(dev->status.data_count));
- }
- }
- break;
- case USB_STD_GET_STATUS:
- req->data[0] = 0;
- req->data[1] = 0;
- return usbd_ack;
- case USB_STD_SET_ADDRESS:
- if (usbd_getinfo(dev) & USBD_HW_ADDRFST) {
- usbd_set_address(dev, req);
- } else {
- dev->complete_callback = usbd_set_address;
- }
- return usbd_ack;
- case USB_STD_SET_CONFIG:
- return usbd_configure(dev, req->wValue);
- case USB_STD_SET_DESCRIPTOR:
- /* should be externally handled */
- break;
- case USB_STD_SET_FEATURE:
- /* not yet supported */
- break;
- default:
- break;
- }
- return usbd_fail;
- }
- /** \brief Standard control request processing for interface
- * \param dev pointer to usb device
- * \param req pointer to control request
- * \return TRUE if request is handled
- */
- static usbd_respond usbd_process_intrq(usbd_device *dev, usbd_ctlreq *req) {
- (void)dev;
- switch (req->bRequest) {
- case USB_STD_GET_STATUS:
- req->data[0] = 0;
- req->data[1] = 0;
- return usbd_ack;
- default:
- break;
- }
- return usbd_fail;
- }
- /** \brief Standard control request processing for endpoint
- * \param dev pointer to usb device
- * \param req pointer to control request
- * \return TRUE if request is handled
- */
- static usbd_respond usbd_process_eptrq(usbd_device *dev, usbd_ctlreq *req) {
- switch (req->bRequest) {
- case USB_STD_SET_FEATURE:
- dev->driver->ep_setstall(req->wIndex, 1);
- return usbd_ack;
- case USB_STD_CLEAR_FEATURE:
- dev->driver->ep_setstall(req->wIndex, 0);
- return usbd_ack;
- case USB_STD_GET_STATUS:
- req->data[0] = dev->driver->ep_isstalled(req->wIndex) ? 1 : 0;
- req->data[1] = 0;
- return usbd_ack;
- default:
- break;
- }
- return usbd_fail;
- }
- /** \brief Processing control request
- * \param dev pointer to usb device
- * \param req pointer to usb control request
- * \return TRUE if request is handled
- */
- static usbd_respond usbd_process_request(usbd_device *dev, usbd_ctlreq *req) {
- /* processing control request by callback */
- if (dev->control_callback) {
- usbd_respond r = dev->control_callback(dev, req, &(dev->complete_callback));
- if (r != usbd_fail) return r;
- }
- /* continuing standard USB requests */
- switch (req->bmRequestType & (USB_REQ_TYPE | USB_REQ_RECIPIENT)) {
- case USB_REQ_STANDARD | USB_REQ_DEVICE:
- return usbd_process_devrq(dev, req);
- case USB_REQ_STANDARD | USB_REQ_INTERFACE:
- return usbd_process_intrq(dev, req);
- case USB_REQ_STANDARD | USB_REQ_ENDPOINT:
- return usbd_process_eptrq(dev, req);
- default:
- break;
- }
- return usbd_fail;
- }
- /** \brief Control endpoint stall (STALL PID)
- * \param dev pointer to usb device
- * \param ep endpoint number
- */
- static void usbd_stall_pid(usbd_device *dev, uint8_t ep) {
- dev->driver->ep_setstall(ep & 0x7F, 1);
- dev->driver->ep_setstall(ep | 0x80, 1);
- dev->status.control_state = usbd_ctl_idle;
- }
- /** \brief Control endpoint TX event processing
- * \param dev pointer to usb device
- * \param ep endpoint number
- */
- static void usbd_process_eptx(usbd_device *dev, uint8_t ep) {
- int32_t _t;
- switch (dev->status.control_state) {
- case usbd_ctl_ztxdata:
- case usbd_ctl_txdata:
- _t = _MIN(dev->status.data_count, dev->status.ep0size);
- dev->driver->ep_write(ep, dev->status.data_ptr, _t);
- dev->status.data_ptr += _t;
- dev->status.data_count -= _t;
- /* if all data is not sent */
- if (0 != dev->status.data_count) break;
- /* if last packet has a EP0 size and host awaiting for the more data ZLP should be sent*/
- /* if ZLP required, control state will be unchanged, therefore next TX event sends ZLP */
- if ( usbd_ctl_txdata == dev->status.control_state || _t != dev->status.ep0size ) {
- dev->status.control_state = usbd_ctl_lastdata; /* no ZLP required */
- }
- break;
- case usbd_ctl_lastdata:
- dev->status.control_state = usbd_ctl_statusout;
- break;
- case usbd_ctl_statusin:
- dev->status.control_state = usbd_ctl_idle;
- return usbd_process_callback(dev);
- default:
- /* unexpected TX completion */
- /* just skipping it */
- break;
- }
- }
- /** \brief Control endpoint RX event processing
- * \param dev pointer to usb device
- * \param ep endpoint number
- */
- static void usbd_process_eprx(usbd_device *dev, uint8_t ep) {
- uint16_t _t;
- usbd_ctlreq *const req = dev->status.data_buf;
- switch (dev->status.control_state) {
- case usbd_ctl_idle:
- /* read SETUP packet, send STALL_PID if incorrect packet length */
- if (0x08 != dev->driver->ep_read(ep, req, dev->status.data_maxsize)) {
- return usbd_stall_pid(dev, ep);
- }
- dev->status.data_ptr = req->data;
- dev->status.data_count = req->wLength;
- /* processing request with no payload data*/
- if ((req->bmRequestType & USB_REQ_DEVTOHOST) || (0 == req->wLength)) break;
- /* checking available memory for DATA OUT stage */
- if (req->wLength > dev->status.data_maxsize) {
- return usbd_stall_pid(dev, ep);
- }
- /* continue DATA OUT stage */
- dev->status.control_state = usbd_ctl_rxdata;
- return;
- case usbd_ctl_rxdata:
- /*receive DATA OUT packet(s) */
- _t = dev->driver->ep_read(ep, dev->status.data_ptr, dev->status.data_count);
- if (dev->status.data_count < _t) {
- /* if received packet is large than expected */
- /* Must be error. Let's drop this request */
- return usbd_stall_pid(dev, ep);
- } else if (dev->status.data_count != _t) {
- /* if all data payload was not received yet */
- dev->status.data_count -= _t;
- dev->status.data_ptr += _t;
- return;
- }
- break;
- case usbd_ctl_statusout:
- /* reading STATUS OUT data to buffer */
- dev->driver->ep_read(ep, dev->status.data_ptr, dev->status.data_maxsize);
- dev->status.control_state = usbd_ctl_idle;
- return usbd_process_callback(dev);
- default:
- /* unexpected RX packet */
- return usbd_stall_pid(dev, ep);
- }
- /* usb request received. let's handle it */
- dev->status.data_ptr = req->data;
- dev->status.data_count = /*req->wLength;*/dev->status.data_maxsize;
- switch (usbd_process_request(dev, req)) {
- case usbd_ack:
- if (req->bmRequestType & USB_REQ_DEVTOHOST) {
- /* return data from function */
- if (dev->status.data_count >= req->wLength) {
- dev->status.data_count = req->wLength;
- dev->status.control_state = usbd_ctl_txdata;
- } else {
- /* DATA IN packet smaller than requested */
- /* ZLP maybe wanted */
- dev->status.control_state = usbd_ctl_ztxdata;
- }
- return usbd_process_eptx(dev, ep | 0x80);
- } else {
- /* confirming by ZLP in STATUS_IN stage */
- dev->driver->ep_write(ep | 0x80, 0, 0);
- dev->status.control_state = usbd_ctl_statusin;
- }
- break;
- case usbd_nak:
- dev->status.control_state = usbd_ctl_statusin;
- break;
- default:
- return usbd_stall_pid(dev, ep);
- }
- }
- /** \brief Control endpoint 0 event processing callback
- * \param dev usb device
- * \param event endpoint event
- */
- static void usbd_process_ep0 (usbd_device *dev, uint8_t event, uint8_t ep) {
- switch (event) {
- case usbd_evt_epsetup:
- /* force switch to setup state */
- dev->status.control_state = usbd_ctl_idle;
- dev->complete_callback = 0;
- case usbd_evt_eprx:
- return usbd_process_eprx(dev, ep);
- case usbd_evt_eptx:
- return usbd_process_eptx(dev, ep);
- default:
- break;
- }
- }
- /** \brief General event processing callback
- * \param dev usb device
- * \param evt usb event
- * \param ep active endpoint
- */
- static void usbd_process_evt(usbd_device *dev, uint8_t evt, uint8_t ep) {
- switch (evt) {
- case usbd_evt_reset:
- usbd_process_reset(dev);
- break;
- case usbd_evt_eprx:
- case usbd_evt_eptx:
- case usbd_evt_epsetup:
- if (dev->endpoint[ep & 0x07]) dev->endpoint[ep & 0x07](dev, evt, ep);
- break;
- default:
- break;
- }
- if (dev->events[evt]) dev->events[evt](dev, evt, ep);
- }
- __attribute__((externally_visible)) void usbd_poll(usbd_device *dev) {
- return dev->driver->poll(dev, usbd_process_evt);
- }
|