Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd6f1e0ed9 | ||
|
|
aa88deb054 |
No files matched your search
@@ -407,7 +407,9 @@ static void ipod_touch_machine_init(MachineState *machine)
|
||||
dev = ipod_touch_init_usb_otg(s5l8900_get_irq(nms, S5L8720_USB_OTG_IRQ), s5l8720_usb_hwcfg);
|
||||
synopsys_usb_state *usb_otg = S5L8900USBOTG(dev);
|
||||
nms->usb_otg = usb_otg;
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
memory_region_add_subregion(sysmem, USBOTG_MEM_BASE, &nms->usb_otg->iomem);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
|
||||
// init two pl080 DMAC0 devices
|
||||
dev = qdev_new("pl080");
|
||||
@@ -533,6 +535,10 @@ static void ipod_touch_machine_init(MachineState *machine)
|
||||
qemu_register_reset(ipod_touch_cpu_reset, nms);
|
||||
|
||||
qemu_add_kbd_event_handler(ipod_touch_key_event, spi4_state->mt);
|
||||
|
||||
// toggle DFU
|
||||
gpio_set_on(nms->gpio_state->gpio_state, GPIO_FORCE_DFU);
|
||||
nms->usb_phys_state->is_cable_connected = true;
|
||||
}
|
||||
|
||||
static void ipod_touch_machine_class_init(ObjectClass *klass, void *data)
|
||||
|
||||
+175
-14
@@ -95,7 +95,7 @@ static void synopsys_usb_update_in_ep(synopsys_usb_state *_state, uint8_t _ep)
|
||||
synopsys_usb_update_ep(_state, eps);
|
||||
|
||||
if(eps->control & USB_EPCON_ENABLE)
|
||||
;//printf("USB: IN transfer queued on %d.\n", _ep);
|
||||
;printf("USB: IN transfer queued on %d.\n", _ep);
|
||||
}
|
||||
|
||||
static void synopsys_usb_update_out_ep(synopsys_usb_state *_state, uint8_t _ep)
|
||||
@@ -107,6 +107,157 @@ static void synopsys_usb_update_out_ep(synopsys_usb_state *_state, uint8_t _ep)
|
||||
;//printf("USB: OUT transfer queued on %d.\n", _ep);
|
||||
}
|
||||
|
||||
static int synopsys_usb_tcp_callback(tcp_usb_state_t *_state, void *_arg, tcp_usb_header_t *_hdr, char *_buffer)
|
||||
{
|
||||
synopsys_usb_state *state = _arg;
|
||||
|
||||
_hdr->addr = (state->dcfg & DCFG_DEVICEADDRMSK) >> DCFG_DEVICEADDR_SHIFT;
|
||||
|
||||
if(_hdr->flags & tcp_usb_reset)
|
||||
{
|
||||
state->gintsts |= GINTMSK_RESET;
|
||||
synopsys_usb_update_irq(state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(_hdr->flags & tcp_usb_enumdone)
|
||||
{
|
||||
state->gintsts |= GINTMSK_ENUMDONE;
|
||||
}
|
||||
|
||||
int ret;
|
||||
uint8_t ep = _hdr->ep & 0x7f;
|
||||
if(_hdr->ep & USB_DIR_IN)
|
||||
{
|
||||
synopsys_usb_ep_state *eps = &state->in_eps[ep];
|
||||
|
||||
if(eps->control & USB_EPCON_STALL)
|
||||
{
|
||||
eps->control &=~ USB_EPCON_STALL; // Should this be EP0 only
|
||||
//printf("USB: Stall.\n");
|
||||
ret = USB_RET_STALL;
|
||||
}
|
||||
else if(eps->control & USB_EPCON_ENABLE)
|
||||
{
|
||||
// Do IN transfer!
|
||||
eps->control &=~ USB_EPCON_ENABLE;
|
||||
|
||||
size_t sz = eps->tx_size & DEPTSIZ_XFERSIZ_MASK;
|
||||
size_t amtDone = sz;
|
||||
if(amtDone > _hdr->length)
|
||||
amtDone = _hdr->length;
|
||||
|
||||
if(eps->fifo >= USB_NUM_FIFOS)
|
||||
hw_error("usb_synopsys: USB transfer on non-existant FIFO %d!\n", eps->fifo);
|
||||
|
||||
size_t txfz = synopsys_usb_tx_fifo_size(state, eps->fifo);
|
||||
if(amtDone > txfz)
|
||||
amtDone = txfz;
|
||||
|
||||
size_t txfs = synopsys_usb_tx_fifo_start(state, eps->fifo);
|
||||
if(txfs + txfz > sizeof(state->fifos))
|
||||
hw_error("usb_synopsys: USB transfer would overflow FIFO buffer!\n");
|
||||
|
||||
printf("USB: Starting IN transfer on EP %d (%d bytes)...\n", ep, amtDone);
|
||||
|
||||
if(amtDone > 0)
|
||||
{
|
||||
if(eps->dma_address)
|
||||
{
|
||||
cpu_physical_memory_read(eps->dma_address, &state->fifos[txfs], amtDone);
|
||||
eps->dma_address += amtDone;
|
||||
}
|
||||
|
||||
memcpy(_buffer, (char*)&state->fifos[txfs], amtDone);
|
||||
}
|
||||
|
||||
printf("USB: IN transfer complete!\n");
|
||||
|
||||
eps->tx_size = (eps->tx_size &~ DEPTSIZ_XFERSIZ_MASK)
|
||||
| ((sz-amtDone) & DEPTSIZ_XFERSIZ_MASK);
|
||||
eps->interrupt_status |= USB_EPINT_XferCompl;
|
||||
|
||||
ret = amtDone;
|
||||
}
|
||||
else
|
||||
ret = USB_RET_NAK;
|
||||
}
|
||||
else // OUT
|
||||
{
|
||||
synopsys_usb_ep_state *eps = &state->out_eps[ep];
|
||||
|
||||
printf("EPS control: %d\n", eps->control);
|
||||
|
||||
if(eps->control & USB_EPCON_STALL)
|
||||
{
|
||||
eps->control &=~ USB_EPCON_STALL; // Should this be EP0 only
|
||||
//printf("USB: Stall.\n");
|
||||
ret = USB_RET_STALL;
|
||||
}
|
||||
else if(eps->control & USB_EPCON_ENABLE)
|
||||
{
|
||||
// Do OUT transfer!
|
||||
eps->control &=~ USB_EPCON_ENABLE;
|
||||
|
||||
size_t sz = eps->tx_size & DEPTSIZ_XFERSIZ_MASK;
|
||||
size_t amtDone = sz;
|
||||
if(amtDone > _hdr->length)
|
||||
amtDone = _hdr->length;
|
||||
|
||||
size_t rxfz = state->grxfsiz;
|
||||
if(amtDone > rxfz)
|
||||
amtDone = rxfz;
|
||||
|
||||
if(rxfz > sizeof(state->fifos))
|
||||
hw_error("usb_synopsys: USB transfer would overflow FIFO buffer!\n");
|
||||
|
||||
printf("USB: Starting OUT transfer on EP %d (%d)...\n", ep, amtDone);
|
||||
|
||||
if(amtDone > 0)
|
||||
{
|
||||
memcpy((char*)state->fifos, _buffer, amtDone);
|
||||
|
||||
if(eps->dma_address)
|
||||
{
|
||||
printf("USB: DMA copying to 0x%08x.\n", eps->dma_address);
|
||||
cpu_physical_memory_write(eps->dma_address, state->fifos, amtDone);
|
||||
eps->dma_address += amtDone;
|
||||
}
|
||||
}
|
||||
|
||||
printf("USB: OUT transfer complete!\n");
|
||||
|
||||
if(_hdr->flags & tcp_usb_setup)
|
||||
{
|
||||
/*
|
||||
printf("USB: Setup %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||||
state->fifos[0],
|
||||
state->fifos[1],
|
||||
state->fifos[2],
|
||||
state->fifos[3],
|
||||
state->fifos[4],
|
||||
state->fifos[5],
|
||||
state->fifos[6],
|
||||
state->fifos[7]);
|
||||
*/
|
||||
eps->interrupt_status |= USB_EPINT_SetUp;
|
||||
}
|
||||
else
|
||||
eps->interrupt_status |= USB_EPINT_XferCompl;
|
||||
|
||||
eps->tx_size = (eps->tx_size &~ DEPTSIZ_XFERSIZ_MASK)
|
||||
| ((sz-amtDone) & DEPTSIZ_XFERSIZ_MASK);
|
||||
|
||||
ret = amtDone;
|
||||
}
|
||||
else
|
||||
ret = USB_RET_NAK;
|
||||
}
|
||||
|
||||
synopsys_usb_update_irq(state);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint32_t synopsys_usb_in_ep_read(synopsys_usb_state *_state, uint8_t _ep, hwaddr _addr)
|
||||
{
|
||||
if(_ep >= USB_NUM_ENDPOINTS)
|
||||
@@ -283,6 +434,10 @@ static void synopsys_usb_in_ep_write(synopsys_usb_state *_state, int _ep, hwaddr
|
||||
return;
|
||||
}
|
||||
|
||||
if (_addr == 0x10) {
|
||||
printf("USB: Setting TX size of EP %d to %d.\n", _ep, _val);
|
||||
}
|
||||
|
||||
switch (_addr)
|
||||
{
|
||||
case 0x00:
|
||||
@@ -355,7 +510,7 @@ static void synopsys_usb_write(void *opaque, hwaddr _addr, uint64_t _val, unsign
|
||||
{
|
||||
synopsys_usb_state *state = (synopsys_usb_state *)opaque;
|
||||
|
||||
//printf("USB: Write 0x%08x to 0x%08x.\n", _val, _addr);
|
||||
printf("USB: Write 0x%08x to 0x%08x.\n", _val, _addr);
|
||||
|
||||
switch(_addr)
|
||||
{
|
||||
@@ -379,20 +534,22 @@ static void synopsys_usb_write(void *opaque, hwaddr _addr, uint64_t _val, unsign
|
||||
state->grstctl = GRSTCTL_CORESOFTRESET;
|
||||
|
||||
// Do reset stuff
|
||||
// if(state->server_host)
|
||||
// {
|
||||
// tcp_usb_cleanup(&state->tcp_state);
|
||||
// tcp_usb_init(&state->tcp_state, synopsys_usb_tcp_callback, NULL, state);
|
||||
if(state->server_host)
|
||||
{
|
||||
tcp_usb_cleanup(&state->tcp_state);
|
||||
tcp_usb_init(&state->tcp_state, synopsys_usb_tcp_callback, NULL, state);
|
||||
|
||||
// printf("Connecting to USB server at %s:%d...\n",
|
||||
// state->server_host, state->server_port);
|
||||
printf("Connecting to USB server at %s:%d...\n",
|
||||
state->server_host, state->server_port);
|
||||
|
||||
// int ret = tcp_usb_connect(&state->tcp_state, state->server_host, state->server_port);
|
||||
// if(ret < 0)
|
||||
// hw_error("Failed to connect to USB server (%d).\n", ret);
|
||||
|
||||
// printf("Connected to USB server.\n");
|
||||
// }
|
||||
int ret = tcp_usb_connect(&state->tcp_state, state->server_host, state->server_port);
|
||||
if(ret < 0) {
|
||||
printf("Failed to connect to USB server (%d).\n", ret);
|
||||
}
|
||||
else {
|
||||
printf("Connected to USB server.\n");
|
||||
}
|
||||
}
|
||||
|
||||
state->grstctl &= ~GRSTCTL_CORESOFTRESET;
|
||||
state->grstctl |= GRSTCTL_AHBIDLE;
|
||||
@@ -517,6 +674,7 @@ static void s5l8900_usb_otg_reset(DeviceState *d)
|
||||
state->ghwcfg4);
|
||||
|
||||
state->pcgcctl = 3;
|
||||
state->grstctl = GRSTCTL_AHBIDLE;
|
||||
|
||||
state->gahbcfg = 0;
|
||||
state->gusbcfg = 0;
|
||||
@@ -540,6 +698,9 @@ static void s5l8900_usb_otg_reset(DeviceState *d)
|
||||
state->grxfsiz = 0x100;
|
||||
state->gnptxfsiz = (0x100 << 16) | 0x100;
|
||||
|
||||
state->server_host = "localhost";
|
||||
state->server_port = 1235;
|
||||
|
||||
uint32_t counter = 0x200;
|
||||
int i;
|
||||
for(i = 0; i < USB_NUM_FIFOS; i++)
|
||||
|
||||
@@ -16,6 +16,8 @@ static uint64_t ipod_touch_usb_phys_read(void *opaque, hwaddr addr, unsigned siz
|
||||
return s->usb_unknown1;
|
||||
case REG_OPHYTUNE:
|
||||
return s->usb_ophytune;
|
||||
case REG_CABLE:
|
||||
return s->is_cable_connected;
|
||||
default:
|
||||
//return 0x0;
|
||||
printf("%s: read invalid location 0x%08x\n", __func__, addr);
|
||||
|
||||
@@ -11,6 +11,8 @@ system_ss.add(when: 'CONFIG_USB', if_true: files(
|
||||
'pcap.c',
|
||||
))
|
||||
|
||||
system_ss.add(when: 'CONFIG_USB', if_true: files('tcp_usb.c'))
|
||||
|
||||
# usb host adapters
|
||||
system_ss.add(when: 'CONFIG_USB_UHCI', if_true: files('hcd-uhci.c'))
|
||||
system_ss.add(when: 'CONFIG_USB_OHCI', if_true: files('hcd-ohci.c'))
|
||||
|
||||
@@ -0,0 +1,524 @@
|
||||
/*
|
||||
* USB over TCP driver.
|
||||
*
|
||||
* Copyright (c) 2011 Richard Ian Taylor.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "hw/usb/tcp_usb.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#define DEBUG_TCP_USB
|
||||
|
||||
void tcp_usb_init(tcp_usb_state_t *_state, tcp_usb_callback_t _cb, tcp_usb_closed_t _closed, void *_arg)
|
||||
{
|
||||
_state->data_callback = _cb;
|
||||
_state->closed_callback = _closed;
|
||||
_state->callback_arg = _arg;
|
||||
|
||||
_state->socket = -1;
|
||||
_state->closed = 1;
|
||||
|
||||
_state->state = tcp_usb_idle;
|
||||
_state->amount_done = 0;
|
||||
}
|
||||
|
||||
void tcp_usb_cleanup(tcp_usb_state_t *_state)
|
||||
{
|
||||
if(_state->socket >= 0)
|
||||
{
|
||||
close(_state->socket);
|
||||
qemu_set_fd_handler(_state->socket, NULL, NULL, NULL);
|
||||
_state->socket = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int tcp_usb_closed(tcp_usb_state_t *_state)
|
||||
{
|
||||
return _state->closed != 0;
|
||||
}
|
||||
|
||||
static void tcp_usb_do_closed(tcp_usb_state_t *_state)
|
||||
{
|
||||
if(_state->closed)
|
||||
return;
|
||||
|
||||
_state->closed = 1;
|
||||
|
||||
qemu_set_fd_handler(_state->socket, NULL, NULL, NULL);
|
||||
|
||||
if(_state->closed_callback)
|
||||
_state->closed_callback(_state, _state->callback_arg);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_TCP_USB
|
||||
static void hexdump(const void *_data, size_t _amt)
|
||||
{
|
||||
char *ptr = (char*)_data;
|
||||
int i;
|
||||
for(i = 0; i < _amt; i++)
|
||||
{
|
||||
if((i%8) == 0)
|
||||
fprintf(stderr, "\t");
|
||||
|
||||
fprintf(stderr, "%02x ", (ptr[i] & 0xFF));
|
||||
|
||||
if((i%8) == 7)
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
if((i%8) != 0)
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
# define debug_hexdump(title, data, amt); fprintf(stderr, title); hexdump((data), (amt));
|
||||
# define debug_printf(args...) printf(args);
|
||||
#else
|
||||
# define debug_hexdump(a, b, c)
|
||||
# define debug_printf(a...)
|
||||
#endif // DEBUG_TCP_USB
|
||||
|
||||
static void tcp_usb_callback(tcp_usb_state_t *state, int _can_read, int _can_write)
|
||||
{
|
||||
if(state->closed)
|
||||
return;
|
||||
|
||||
int ret;
|
||||
switch(state->state)
|
||||
{
|
||||
case tcp_usb_idle:
|
||||
if(!_can_read)
|
||||
return;
|
||||
|
||||
debug_printf("%s: tcp_usb_idle.\n", __func__);
|
||||
|
||||
// Receiving new request
|
||||
state->header = malloc(sizeof(*state->header));
|
||||
state->amount_done = 0;
|
||||
state->state = tcp_usb_read_request;
|
||||
|
||||
// Fall through
|
||||
case tcp_usb_read_request:
|
||||
debug_printf("%s: tcp_usb_read_request\n", __func__);
|
||||
|
||||
if(state->amount_done < sizeof(*state->header))
|
||||
{
|
||||
ret = read(state->socket,
|
||||
((char*)state->header) + state->amount_done,
|
||||
sizeof(*state->header) - state->amount_done);
|
||||
if(ret == 0 && _can_read)
|
||||
{
|
||||
free(state->header);
|
||||
tcp_usb_do_closed(state);
|
||||
return;
|
||||
}
|
||||
else if(ret == EWOULDBLOCK || ret == 0 || ret == -1)
|
||||
return;
|
||||
else if(ret < 0)
|
||||
{
|
||||
fprintf(stderr, "tcp_usb: Error %d reading request header!\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
_can_read = 0;
|
||||
state->amount_done += ret;
|
||||
|
||||
if(state->amount_done < sizeof(*state->header))
|
||||
return;
|
||||
|
||||
debug_hexdump("tcp_usb: Got Header: ", state->header, sizeof(*state->header));
|
||||
debug_printf("Length: %d\n", state->header->length);
|
||||
|
||||
if(state->header->length > 0)
|
||||
state->buffer = malloc(state->header->length);
|
||||
else
|
||||
state->buffer = NULL;
|
||||
}
|
||||
|
||||
if((state->header->ep & USB_DIR_IN) == 0 && state->header->length > 0) // OUT
|
||||
{
|
||||
ret = read(state->socket,
|
||||
((char*)state->buffer) + (state->amount_done - sizeof(*state->header)),
|
||||
state->header->length - (state->amount_done - sizeof(*state->header)));
|
||||
printf("RET: %d\n", ret);
|
||||
if(ret == 0 && _can_read)
|
||||
{
|
||||
free(state->header);
|
||||
if(state->buffer)
|
||||
free(state->buffer);
|
||||
tcp_usb_do_closed(state);
|
||||
return;
|
||||
}
|
||||
else if(ret == EWOULDBLOCK || ret == 0 || ret == -1)
|
||||
return;
|
||||
else if(ret < 0)
|
||||
{
|
||||
printf("tcp_usb: Error %d reading request data!\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
_can_read = 0;
|
||||
state->amount_done += ret;
|
||||
|
||||
if(state->amount_done < sizeof(*state->header) + state->header->length)
|
||||
return;
|
||||
|
||||
debug_hexdump("tcp_usb: Read: ", state->buffer, state->header->length);
|
||||
}
|
||||
|
||||
// Transfer complete! Call callback!
|
||||
if(state->data_callback)
|
||||
{
|
||||
state->state = tcp_usb_write_response;
|
||||
state->amount_done = 0;
|
||||
|
||||
debug_printf("tcp_usb: Calling callback.\n");
|
||||
ret = state->data_callback(state, state->callback_arg, state->header, state->buffer);
|
||||
state->header->length = ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "tcp_usb: Packet received but no callback!\n");
|
||||
state->state = tcp_usb_idle;
|
||||
return;
|
||||
}
|
||||
|
||||
// Fall through
|
||||
case tcp_usb_write_response:
|
||||
debug_printf("%s: tcp_usb_write_response\n", __func__);
|
||||
|
||||
if(state->amount_done < sizeof(*state->header))
|
||||
{
|
||||
ret = write(state->socket,
|
||||
((char*)state->header) + state->amount_done,
|
||||
sizeof(*state->header) - state->amount_done);
|
||||
if(ret == 0 && _can_write)
|
||||
{
|
||||
free(state->header);
|
||||
if(state->buffer)
|
||||
free(state->buffer);
|
||||
tcp_usb_do_closed(state);
|
||||
return;
|
||||
}
|
||||
else if(ret == EWOULDBLOCK || ret == 0 || ret == -1)
|
||||
return;
|
||||
else if(ret < 0)
|
||||
{
|
||||
fprintf(stderr, "tcp_usb: Error %d writing response header.\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
_can_write = 0;
|
||||
state->amount_done += ret;
|
||||
|
||||
if(state->amount_done < sizeof(*state->header))
|
||||
return;
|
||||
|
||||
debug_hexdump("tcp_usb: Sent Header (write response): ", state->header, sizeof(*state->header));
|
||||
}
|
||||
|
||||
if((state->header->ep & USB_DIR_IN) != 0 && state->header->length > 0) // IN
|
||||
{
|
||||
ret = write(state->socket,
|
||||
((char*)state->buffer) + (state->amount_done - sizeof(*state->header)),
|
||||
state->header->length - (state->amount_done - sizeof(*state->header)));
|
||||
if(ret == 0 && _can_write)
|
||||
{
|
||||
free(state->header);
|
||||
if(state->buffer)
|
||||
free(state->buffer);
|
||||
tcp_usb_do_closed(state);
|
||||
return;
|
||||
}
|
||||
else if(ret == EWOULDBLOCK || ret == 0 || ret == -1)
|
||||
return;
|
||||
else if(ret < 0)
|
||||
{
|
||||
fprintf(stderr, "tcp_usb: Error %d writing response data.\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
_can_write = 0;
|
||||
state->amount_done += ret;
|
||||
|
||||
if(state->amount_done < sizeof(*state->header) + state->header->length)
|
||||
return;
|
||||
|
||||
debug_hexdump("tcp_usb: Wrote: ", state->buffer, state->header->length);
|
||||
}
|
||||
|
||||
free(state->header);
|
||||
if(state->buffer)
|
||||
free(state->buffer);
|
||||
state->state = tcp_usb_idle;
|
||||
break;
|
||||
|
||||
case tcp_usb_write_request:
|
||||
debug_printf("%s: tcp_usb_write_request\n", __func__);
|
||||
|
||||
if(state->amount_done < sizeof(*state->header))
|
||||
{
|
||||
ret = write(state->socket,
|
||||
((char*)state->header) + state->amount_done,
|
||||
sizeof(*state->header) - state->amount_done);
|
||||
if(ret == 0 && _can_write)
|
||||
{
|
||||
tcp_usb_do_closed(state);
|
||||
return;
|
||||
}
|
||||
else if(ret == EWOULDBLOCK || ret == 0 || ret == -1)
|
||||
return;
|
||||
else if(ret < 0)
|
||||
{
|
||||
fprintf(stderr, "tcp_usb: Error %d writing request header!\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
_can_write = 0;
|
||||
state->amount_done += ret;
|
||||
|
||||
if(state->amount_done < sizeof(*state->header))
|
||||
return;
|
||||
|
||||
debug_hexdump("tcp_usb: Sent Header (write request): ", state->header, sizeof(*state->header));
|
||||
}
|
||||
|
||||
if((state->header->ep & USB_DIR_IN) == 0 && state->header->length > 0) // OUT
|
||||
{
|
||||
ret = write(state->socket,
|
||||
((char*)state->buffer) + (state->amount_done - sizeof(*state->header)),
|
||||
state->header->length - (state->amount_done - sizeof(*state->header)));
|
||||
if(ret == 0 && _can_write)
|
||||
{
|
||||
tcp_usb_do_closed(state);
|
||||
return;
|
||||
}
|
||||
else if(ret == EWOULDBLOCK || ret == 0 || ret == -1)
|
||||
return;
|
||||
else if(ret < 0)
|
||||
{
|
||||
fprintf(stderr, "tcp_usb: Error %d writing request data!\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
_can_write = 0;
|
||||
state->amount_done += ret;
|
||||
|
||||
if(state->amount_done < sizeof(*state->header) + state->header->length)
|
||||
return;
|
||||
|
||||
debug_hexdump("tcp_usb: Wrote: ", state->buffer, state->header->length);
|
||||
}
|
||||
|
||||
state->state = tcp_usb_read_response;
|
||||
state->amount_done = 0;
|
||||
|
||||
// Fall through
|
||||
case tcp_usb_read_response:
|
||||
debug_printf("%s: tcp_usb_read_response\n", __func__);
|
||||
|
||||
if(state->amount_done < sizeof(*state->header))
|
||||
{
|
||||
ret = read(state->socket,
|
||||
((char*)state->header) + state->amount_done,
|
||||
sizeof(*state->header) - state->amount_done);
|
||||
if(ret == 0 && _can_read)
|
||||
{
|
||||
tcp_usb_do_closed(state);
|
||||
return;
|
||||
}
|
||||
else if(ret == EWOULDBLOCK || ret == 0 || ret == -1)
|
||||
return;
|
||||
else if(ret < 0)
|
||||
{
|
||||
fprintf(stderr, "tcp_usb: Error %d reading response header.\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
_can_read = 0;
|
||||
state->amount_done += ret;
|
||||
|
||||
if(state->amount_done < sizeof(*state->header))
|
||||
return;
|
||||
|
||||
debug_hexdump("tcp_usb: Got Header: ", state->header, sizeof(*state->header));
|
||||
}
|
||||
|
||||
if((state->header->ep & USB_DIR_IN) != 0 && state->header->length > 0) // IN
|
||||
{
|
||||
ret = read(state->socket,
|
||||
((char*)state->buffer) + (state->amount_done - sizeof(*state->header)),
|
||||
state->header->length - (state->amount_done - sizeof(*state->header)));
|
||||
if(ret == 0 && _can_read)
|
||||
{
|
||||
tcp_usb_do_closed(state);
|
||||
return;
|
||||
}
|
||||
else if(ret == EWOULDBLOCK || ret == 0 || ret == -1)
|
||||
return;
|
||||
else if(ret < 0)
|
||||
{
|
||||
fprintf(stderr, "tcp_usb: Error %d reading response data.\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
_can_read = 0;
|
||||
state->amount_done += ret;
|
||||
|
||||
if(state->amount_done < sizeof(*state->header) + state->header->length)
|
||||
return;
|
||||
|
||||
debug_hexdump("tcp_usb: Read: ", state->buffer, state->header->length);
|
||||
}
|
||||
|
||||
state->state = tcp_usb_idle;
|
||||
|
||||
// Transfer complete! Call callback!
|
||||
if(state->data_callback)
|
||||
{
|
||||
debug_printf("tcp_usb: calling callback!\n");
|
||||
state->data_callback(state, state->callback_arg, state->header, state->buffer);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "tcp_usb: Request sent but no callback!\n");
|
||||
state->state = tcp_usb_idle;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void tcp_usb_read_callback(void *_arg)
|
||||
{
|
||||
tcp_usb_state_t *state = _arg;
|
||||
tcp_usb_callback(state, 1, 0);
|
||||
}
|
||||
|
||||
static void tcp_usb_write_callback(void *_arg)
|
||||
{
|
||||
tcp_usb_state_t *state = _arg;
|
||||
tcp_usb_callback(state, 0, 1);
|
||||
}
|
||||
|
||||
int tcp_usb_connect(tcp_usb_state_t *_state, char *_host, uint32_t _port)
|
||||
{
|
||||
_state->socket = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
struct hostent *hostname = gethostbyname(_host);
|
||||
|
||||
struct sockaddr_in server_addr;
|
||||
memset(&server_addr, 0, sizeof(server_addr));
|
||||
|
||||
server_addr.sin_family = AF_INET;
|
||||
server_addr.sin_port = htons(_port);
|
||||
memcpy(&server_addr.sin_addr.s_addr,
|
||||
hostname->h_addr, hostname->h_length);
|
||||
|
||||
int ret = connect(_state->socket, &server_addr, sizeof(server_addr));
|
||||
if(ret < 0)
|
||||
return -EIO;
|
||||
|
||||
_state->closed = 0;
|
||||
int flags = fcntl(_state->socket, F_GETFL, 0);
|
||||
fcntl(_state->socket, F_SETFL, flags | O_NONBLOCK);
|
||||
qemu_set_fd_handler(_state->socket, tcp_usb_read_callback, tcp_usb_write_callback, _state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tcp_usb_request(tcp_usb_state_t *_state, tcp_usb_header_t *_header, const char *_data)
|
||||
{
|
||||
debug_printf("%s.\n", __func__);
|
||||
|
||||
if(_state->state != tcp_usb_idle)
|
||||
return -EBUSY;
|
||||
|
||||
debug_printf("%s starting request.\n", __func__);
|
||||
|
||||
_state->state = tcp_usb_write_request;
|
||||
_state->amount_done = 0;
|
||||
_state->buffer = (char*)_data;
|
||||
_state->header = _header;
|
||||
|
||||
tcp_usb_callback(_state, 0, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tcp_usb_host_init(tcp_usb_host_state_t *_state)
|
||||
{
|
||||
_state->socket = -1;
|
||||
}
|
||||
|
||||
void tcp_usb_host_cleanup(tcp_usb_host_state_t *_state)
|
||||
{
|
||||
if(_state->socket >= 0)
|
||||
{
|
||||
close(_state->socket);
|
||||
_state->socket = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int tcp_usb_host_okay(tcp_usb_host_state_t *_state)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tcp_usb_host(tcp_usb_host_state_t *_state, uint32_t _port)
|
||||
{
|
||||
_state->socket = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if(_state->socket < 0)
|
||||
return -EIO;
|
||||
|
||||
struct linger linger;
|
||||
linger.l_onoff = 0;
|
||||
linger.l_linger = 0;
|
||||
setsockopt(_state->socket, SOL_SOCKET, SO_LINGER, (char *)&linger, sizeof(linger));
|
||||
|
||||
struct sockaddr_in server_addr;
|
||||
server_addr.sin_family = AF_INET;
|
||||
server_addr.sin_port = htons(_port);
|
||||
server_addr.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
if(bind(_state->socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0)
|
||||
return -EIO;
|
||||
|
||||
listen(_state->socket, 5);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tcp_usb_accept(tcp_usb_host_state_t *_host, tcp_usb_state_t *_client)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
size_t addr_sz = sizeof(addr);
|
||||
|
||||
debug_printf("%s: waiting on accept...\n", __func__);
|
||||
|
||||
_client->socket = accept(_host->socket, &addr, &addr_sz);
|
||||
if(_client->socket < 0)
|
||||
{
|
||||
fprintf(stderr, "%s: accept error %d.\n", __func__, errno);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
_client->closed = 0;
|
||||
int flags = fcntl(_client->socket, F_GETFL, 0);
|
||||
fcntl(_client->socket, F_SETFL, flags | O_NONBLOCK);
|
||||
qemu_set_fd_handler(_client->socket, tcp_usb_read_callback, tcp_usb_write_callback, _client);
|
||||
debug_printf("%s: USB device accepted!\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "hw/irq.h"
|
||||
#include "hw/usb.h"
|
||||
#include "hw/usb/tcp_usb.h"
|
||||
|
||||
#define DEVICE_NAME "usb_synopsys"
|
||||
|
||||
@@ -223,7 +224,7 @@ typedef struct synopsys_usb_state
|
||||
|
||||
char *server_host;
|
||||
uint32_t server_port;
|
||||
//tcp_usb_state_t tcp_state;
|
||||
tcp_usb_state_t tcp_state;
|
||||
|
||||
uint32_t pcgcctl;
|
||||
|
||||
|
||||
@@ -8,11 +8,12 @@
|
||||
#define TYPE_IPOD_TOUCH_USB_PHYS "ipodtouch.usbphys"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchUSBPhysState, IPOD_TOUCH_USB_PHYS)
|
||||
|
||||
#define REG_OPHYPWR 0x0
|
||||
#define REG_OPHYCLK 0x4
|
||||
#define REG_ORSTCON 0x8
|
||||
#define REG_OPHYPWR 0x0
|
||||
#define REG_OPHYCLK 0x4
|
||||
#define REG_ORSTCON 0x8
|
||||
#define REG_UNKNOWN1 0x1C
|
||||
#define REG_OPHYTUNE 0x20
|
||||
#define REG_CABLE 0x28
|
||||
|
||||
typedef struct IPodTouchUSBPhysState {
|
||||
SysBusDevice busdev;
|
||||
@@ -23,6 +24,7 @@ typedef struct IPodTouchUSBPhysState {
|
||||
uint32_t usb_orstcon;
|
||||
uint32_t usb_unknown1;
|
||||
uint32_t usb_ophytune;
|
||||
bool is_cable_connected;
|
||||
} IPodTouchUSBPhysState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,102 @@
|
||||
#ifndef HW_TCP_USB
|
||||
#define HW_TCP_USB
|
||||
|
||||
/*
|
||||
* USB over TCP driver.
|
||||
*
|
||||
* Copyright (c) 2011 Richard Ian Taylor.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "hw/usb.h"
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
enum
|
||||
{
|
||||
tcp_usb_setup = 1 << 0,
|
||||
tcp_usb_reset = 1 << 1,
|
||||
tcp_usb_enumdone = 1 << 2,
|
||||
};
|
||||
|
||||
typedef enum _tcp_usb_state_enum
|
||||
{
|
||||
tcp_usb_idle,
|
||||
|
||||
// Client
|
||||
tcp_usb_read_request,
|
||||
tcp_usb_write_response,
|
||||
|
||||
// Host
|
||||
tcp_usb_write_request,
|
||||
tcp_usb_read_response,
|
||||
|
||||
} tcp_usb_state_enum_t;
|
||||
|
||||
typedef struct _tcp_usb_header
|
||||
{
|
||||
uint8_t addr;
|
||||
uint8_t ep;
|
||||
uint8_t flags;
|
||||
int16_t length;
|
||||
|
||||
} __attribute__((packed)) tcp_usb_header_t;
|
||||
|
||||
struct _tcp_usb_state;
|
||||
typedef int (*tcp_usb_callback_t)(struct _tcp_usb_state *_status, void *_arg, tcp_usb_header_t *_hdr, char *_buffer);
|
||||
typedef void (*tcp_usb_closed_t)(struct _tcp_usb_state *_state, void *_arg);
|
||||
|
||||
typedef struct _tcp_usb_state
|
||||
{
|
||||
int socket;
|
||||
int closed;
|
||||
|
||||
tcp_usb_state_enum_t state;
|
||||
tcp_usb_header_t *header;
|
||||
char *buffer;
|
||||
size_t amount_done;
|
||||
|
||||
tcp_usb_closed_t closed_callback;
|
||||
tcp_usb_callback_t data_callback;
|
||||
void *callback_arg;
|
||||
} tcp_usb_state_t;
|
||||
|
||||
void tcp_usb_init(tcp_usb_state_t *_state, tcp_usb_callback_t _cb, tcp_usb_closed_t _closed, void *_arg);
|
||||
void tcp_usb_cleanup(tcp_usb_state_t *_state);
|
||||
|
||||
int tcp_usb_closed(tcp_usb_state_t *_state);
|
||||
|
||||
int tcp_usb_connect(tcp_usb_state_t *_state, char *_host, uint32_t _port);
|
||||
|
||||
int tcp_usb_request(tcp_usb_state_t *_state, tcp_usb_header_t *_header, const char *_data);
|
||||
|
||||
typedef struct _tcp_usb_host_state
|
||||
{
|
||||
int socket;
|
||||
|
||||
} tcp_usb_host_state_t;
|
||||
|
||||
void tcp_usb_host_init(tcp_usb_host_state_t *_state);
|
||||
void tcp_usb_host_cleanup(tcp_usb_host_state_t *_state);
|
||||
|
||||
int tcp_usb_host_okay(tcp_usb_host_state_t *_state);
|
||||
|
||||
int tcp_usb_host(tcp_usb_host_state_t *_state, uint32_t _port);
|
||||
int tcp_usb_accept(tcp_usb_host_state_t *_host, tcp_usb_state_t *_client);
|
||||
|
||||
#endif //HW_TCP_USB
|
||||
Reference in new issue
Block a user