blob: 7ceb5249725ee94c565ad84c94b7c98832c5be15 [file] [log] [blame]
/*
* Copyright 2017, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the BSD 2-Clause license. Note that NO WARRANTY is provided.
* See "LICENSE_BSD2.txt" for details.
*
* @TAG(DATA61_BSD)
*/
#include "otgusbtty.h"
#include <usb/usb.h>
#include "../services.h"
#include <stdio.h>
#include <string.h>
struct otg_usbtty {
usb_otg_t otg;
ps_dma_man_t* dman;
void* desc;
uintptr_t pdesc;
};
struct free_token {
void* vaddr;
size_t size;
ps_dma_man_t* dman;
};
static struct device_desc otg_usbtty_device_desc = {
.bLength = 0x12,
.bDescriptorType = 0x1,
.bcdUSB = 0x110,
.bDeviceClass = 0x0,
.bDeviceSubClass = 0x0,
.bDeviceProtocol = 0x0,
.bMaxPacketSize0 = 0x40,
.idVendor = 0x067b,
.idProduct = 0x2303,
.bcdDevice = 0x300,
.iManufacturer = 0x0 /* 0x1 */,
.iProduct = 0x0 /* 0x2 */,
.iSerialNumber = 0x0,
.bNumConfigurations = 0x1
};
static struct config_desc otg_usbtty_config_desc = {
.bLength = 0x9,
.bDescriptorType = 0x2,
.wTotalLength = 0x27,
.bNumInterfaces = 0x1,
.bConfigurationValue = 0x1,
.iConfigurationIndex = 0x0,
.bmAttributes = 0x80,
.bMaxPower = 0x32
};
static struct iface_desc otg_usbtty_iface_desc = {
.bLength = 0x9,
.bDescriptorType = 0x4,
.bInterfaceNumber = 0x0,
.bAlternateSetting = 0x0,
.bNumEndpoints = 0x3,
.bInterfaceClass = 0xff,
.bInterfaceSubClass = 0x0,
.bInterfaceProtocol = 0x0,
.iInterface = 0x0
};
static struct endpoint_desc otg_usbtty_ep1_desc = {
.bLength = 0x7,
.bDescriptorType = 0x5,
.bEndpointAddress = 0x81,
.bmAttributes = 0x3,
.wMaxPacketSize = 0xa,
.bInterval = 0x1
};
static struct endpoint_desc otg_usbtty_ep2_desc = {
.bLength = 0x7,
.bDescriptorType = 0x5,
.bEndpointAddress = 0x2,
.bmAttributes = 0x2,
.wMaxPacketSize = 0x40,
.bInterval = 0x0
};
static struct endpoint_desc otg_usbtty_ep3_desc = {
.bLength = 0x7,
.bDescriptorType = 0x5,
.bEndpointAddress = 0x83,
.bmAttributes = 0x2,
.wMaxPacketSize = 0x40,
.bInterval = 0x0
};
static void
freebuf_cb(usb_otg_t otg, void* token,
enum usb_xact_status stat)
{
struct free_token* t;
if (stat != XACTSTAT_SUCCESS) {
ZF_LOGF("Transaction failed\n");
}
t = (struct free_token*)token;
ps_dma_free_pinned(t->dman, t->vaddr, t->size);
usb_free(t);
}
static void
send_desc(otg_usbtty_t tty, enum DescriptorType type, int index,
int maxlen)
{
struct anon_desc* d = NULL;
/* Not handling index yet... */
if (index != 0) {
ZF_LOGF("Index not implemented\n");
}
/* Find the descriptor */
switch (type) {
case DEVICE:
d = (struct anon_desc*)&otg_usbtty_device_desc;
printf("device descriptor read/");
if (maxlen >= d->bLength) {
printf("all\n");
} else {
printf("%d\n", maxlen);
}
break;
case CONFIGURATION:
printf("config\n");
break;
case STRING:
printf("string\n");
break;
case INTERFACE:
printf("interface\n");
break;
case ENDPOINT:
printf("endpoint\n");
break;
case DEVICE_QUALIFIER:
printf("device qualifier\n");
break;
case OTHER_SPEED_CONFIGURATION:
printf("other speed\n");
break;
case INTERFACE_POWER:
printf("interface power\n");
break;
case HID:
printf("hid\n");
break;
case HUB:
printf("Hub\n");
break;
default:
printf("Unhandled descriptor request\n");
}
/* Send the descriptor */
if (d != NULL) {
struct free_token* t;
uintptr_t pbuf;
int err;
t = usb_malloc(sizeof(*t));
if (!t) {
ZF_LOGF("Out of memory\n");
}
t->dman = tty->dman;
/* limit size to prevent babble */
t->size = d->bLength;
if (maxlen < t->size) {
t->size = maxlen;
}
/* Copy in */
t->vaddr = ps_dma_alloc_pinned(tty->dman, t->size, 32, 0, PS_MEM_NORMAL, &pbuf);
if (t->vaddr == NULL) {
ZF_LOGF("Out of DMA memory\n");
}
memcpy(t->vaddr, d, t->size);
/* Send the packet */
err = otg_prime(tty->otg, 0, PID_IN, t->vaddr, pbuf, t->size, freebuf_cb, t);
if (err) {
ps_dma_free_pinned(tty->dman, t->vaddr, t->size);
ZF_LOGF("OTG device error\n");
}
/* Status phase */
err = otg_prime(tty->otg, 0, PID_OUT, NULL, 0, 0, freebuf_cb, t);
if (err) {
ps_dma_free_pinned(tty->dman, t->vaddr, t->size);
ZF_LOGF("OTG device error\n");
}
}
}
static void
usbtty_setup_cb(usb_otg_t otg, void* token, struct usbreq* req)
{
otg_usbtty_t tty = (otg_usbtty_t)token;
(void)otg_usbtty_config_desc;
(void)otg_usbtty_iface_desc;
(void)otg_usbtty_ep1_desc;
(void)otg_usbtty_ep2_desc;
(void)otg_usbtty_ep3_desc;
switch (req->bRequest) {
case GET_DESCRIPTOR:
send_desc(tty, req->wValue >> 8, req->wValue & 0xff,
req->wLength);
break;
case GET_CONFIGURATION:
printf("get conf\n");
break;
case GET_STATUS:
printf("get stat\n");
break;
case CLR_FEATURE:
printf("Clear feat\n");
break;
case SET_FEATURE:
printf("Set feature\n");
break;
case SET_ADDRESS:
printf("Set address\n");
break;
case SET_DESCRIPTOR:
printf("Set descriptor\n");
break;
case SET_CONFIGURATION:
printf("Set config\n");
break;
case GET_INTERFACE:
printf("Get interface\n");
break;
case SET_INTERFACE:
printf("Set interface\n");
break;
default:
printf("Unhandled request %d\n", req->bRequest);
}
}
int
otg_usbtty_init(usb_otg_t otg, ps_dma_man_t* dman,
otg_usbtty_t* usbtty)
{
otg_usbtty_t tty;
int err;
if (!dman || !usbtty || !otg) {
ZF_LOGF("Invalid arguments\n");
}
/* Allocate memory */
tty = usb_malloc(sizeof(*tty));
if (tty == NULL) {
return -1;
}
tty->dman = dman;
tty->otg = otg;
/* Initialise the control endpoint */
err = otg_ep0_setup(otg, usbtty_setup_cb, tty);
if (err) {
usb_free(tty);
return -1;
}
*usbtty = tty;
return 0;
}