2011-02-14 04:46:36 +02:00
|
|
|
/*
|
2011-02-14 17:48:42 +02:00
|
|
|
* fw/usb/usb.c - USB hardware setup and standard device requests
|
2011-02-14 04:46:36 +02:00
|
|
|
*
|
2013-03-30 00:14:11 +02:00
|
|
|
* Written 2008-2011, 2013 by Werner Almesberger
|
|
|
|
* Copyright 2008-2011, 2013 Werner Almesberger
|
2011-02-14 04:46:36 +02:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Known issues:
|
|
|
|
* - no suspend/resume
|
2011-02-14 17:54:29 +02:00
|
|
|
* - should support EP clearing and stalling
|
2011-02-14 04:46:36 +02:00
|
|
|
*/
|
|
|
|
|
2013-03-30 00:14:11 +02:00
|
|
|
#include <stdbool.h>
|
2011-02-14 04:46:36 +02:00
|
|
|
#include <stdint.h>
|
|
|
|
|
|
|
|
#include "usb.h"
|
|
|
|
#include "../board.h"
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef NULL
|
|
|
|
#define NULL 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if 1
|
|
|
|
extern void panic(void);
|
|
|
|
#define BUG_ON(cond) do { if (cond) panic(); } while (0)
|
|
|
|
#else
|
|
|
|
#define BUG_ON(cond)
|
|
|
|
#endif
|
|
|
|
|
2013-03-30 00:14:11 +02:00
|
|
|
bool (*user_setup)(const struct setup_request *setup);
|
2011-06-11 03:50:42 +03:00
|
|
|
void (*user_set_interface)(int nth);
|
2013-03-30 00:14:11 +02:00
|
|
|
bool (*user_get_descriptor)(uint8_t type, uint8_t index,
|
2011-03-08 23:56:52 +02:00
|
|
|
const uint8_t **reply, uint8_t *size);
|
2011-02-14 04:46:36 +02:00
|
|
|
void (*user_reset)(void);
|
|
|
|
|
|
|
|
|
|
|
|
void usb_io(struct ep_descr *ep, enum ep_state state, uint8_t *buf,
|
|
|
|
uint8_t size, void (*callback)(void *user), void *user)
|
|
|
|
{
|
|
|
|
BUG_ON(ep->state);
|
|
|
|
ep->state = state;
|
|
|
|
ep->buf = buf;
|
|
|
|
ep->end = buf+size;
|
|
|
|
ep->callback = callback;
|
|
|
|
ep->user = user;
|
2011-06-11 20:17:44 +03:00
|
|
|
usb_ep_change(ep);
|
2011-02-14 04:46:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-03-30 00:14:11 +02:00
|
|
|
static bool get_descriptor(uint8_t type, uint8_t index, uint16_t length)
|
2011-02-14 04:46:36 +02:00
|
|
|
{
|
|
|
|
const uint8_t *reply;
|
|
|
|
uint8_t size;
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case USB_DT_DEVICE:
|
|
|
|
reply = device_descriptor;
|
|
|
|
size = reply[0];
|
|
|
|
break;
|
|
|
|
case USB_DT_CONFIG:
|
|
|
|
if (index)
|
|
|
|
return 0;
|
|
|
|
reply = config_descriptor;
|
|
|
|
size = reply[2];
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (!user_get_descriptor)
|
|
|
|
return 0;
|
|
|
|
if (!user_get_descriptor(type, index, &reply, &size))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (length < size)
|
|
|
|
size = length;
|
|
|
|
usb_send(&eps[0], reply, size, NULL, NULL);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-03-30 00:14:11 +02:00
|
|
|
bool handle_setup(const struct setup_request *setup)
|
2011-02-14 04:46:36 +02:00
|
|
|
{
|
2011-02-14 17:01:11 +02:00
|
|
|
switch (setup->bmRequestType | setup->bRequest << 8) {
|
2011-02-14 04:46:36 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Device request
|
|
|
|
*
|
|
|
|
* See http://www.beyondlogic.org/usbnutshell/usb6.htm
|
|
|
|
*/
|
|
|
|
|
|
|
|
case FROM_DEVICE(GET_STATUS):
|
2011-02-14 17:01:11 +02:00
|
|
|
if (setup->wLength != 2)
|
2011-02-14 04:46:36 +02:00
|
|
|
return 0;
|
|
|
|
usb_send(&eps[0], "\000", 2, NULL, NULL);
|
|
|
|
break;
|
|
|
|
case TO_DEVICE(CLEAR_FEATURE):
|
|
|
|
break;
|
|
|
|
case TO_DEVICE(SET_FEATURE):
|
|
|
|
return 0;
|
|
|
|
case TO_DEVICE(SET_ADDRESS):
|
2011-02-14 17:01:11 +02:00
|
|
|
set_addr(setup->wValue);
|
2011-02-14 04:46:36 +02:00
|
|
|
break;
|
|
|
|
case FROM_DEVICE(GET_DESCRIPTOR):
|
2011-02-14 17:01:11 +02:00
|
|
|
if (!get_descriptor(setup->wValue >> 8, setup->wValue,
|
|
|
|
setup->wLength))
|
2011-02-14 04:46:36 +02:00
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case TO_DEVICE(SET_DESCRIPTOR):
|
|
|
|
return 0;
|
|
|
|
case FROM_DEVICE(GET_CONFIGURATION):
|
|
|
|
usb_send(&eps[0], "", 1, NULL, NULL);
|
|
|
|
break;
|
|
|
|
case TO_DEVICE(SET_CONFIGURATION):
|
2011-02-14 17:01:11 +02:00
|
|
|
if (setup->wValue != config_descriptor[5])
|
2011-02-14 04:46:36 +02:00
|
|
|
return 0;
|
2013-04-01 21:54:44 +03:00
|
|
|
usb_enable_bus_reset();
|
2011-02-14 04:46:36 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Interface request
|
|
|
|
*/
|
|
|
|
|
|
|
|
case FROM_INTERFACE(GET_STATUS):
|
|
|
|
return 0;
|
|
|
|
case TO_INTERFACE(CLEAR_FEATURE):
|
|
|
|
return 0;
|
|
|
|
case TO_INTERFACE(SET_FEATURE):
|
|
|
|
return 0;
|
|
|
|
case FROM_INTERFACE(GET_INTERFACE):
|
|
|
|
return 0;
|
|
|
|
case TO_INTERFACE(SET_INTERFACE):
|
|
|
|
{
|
|
|
|
const uint8_t *interface_descriptor =
|
|
|
|
config_descriptor+9;
|
2011-06-10 23:12:57 +03:00
|
|
|
const uint8_t *p;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
for (p = interface_descriptor;
|
|
|
|
p != config_descriptor+config_descriptor[2];
|
|
|
|
p += p[0]) {
|
|
|
|
if (p[2] == setup->wIndex &&
|
|
|
|
p[3] == setup->wValue) {
|
2011-06-11 03:50:42 +03:00
|
|
|
if (user_set_interface)
|
|
|
|
user_set_interface(i);
|
2011-06-10 23:12:57 +03:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
return 0;
|
2011-02-14 04:46:36 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Endpoint request
|
|
|
|
*/
|
|
|
|
|
|
|
|
case FROM_ENDPOINT(GET_STATUS):
|
|
|
|
return 0;
|
|
|
|
case TO_ENDPOINT(CLEAR_FEATURE):
|
|
|
|
return 0;
|
|
|
|
case TO_ENDPOINT(SET_FEATURE):
|
|
|
|
return 0;
|
|
|
|
case FROM_ENDPOINT(SYNCH_FRAME):
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
default:
|
2011-06-10 23:12:57 +03:00
|
|
|
if (user_setup)
|
|
|
|
return user_setup(setup);
|
|
|
|
return 0;
|
2011-02-14 04:46:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|