1
0
Fork 0
forked from len0rd/rockbox

hwstub/qeditor: add support for atomic read/writes

The current code assumed that READ/WRITE would produce atomic read/writes for
8/16/32-bit words, which in turned put assumption on the memcpy function.
Since some memcpy implementation do not always guarantee such strong assumption,
introduce two new operation READ/WRITE_ATOMIC which provide the necessary
tools to do correct read and write to register in a single memory access.

Change-Id: I37451bd5057bb0dcaf5a800d8aef8791c792a090
This commit is contained in:
Marcin Bukat 2014-11-18 23:27:26 +01:00
parent 794169a18f
commit cd04a5f1aa
12 changed files with 254 additions and 24 deletions

View file

@ -356,6 +356,49 @@ static void handle_get_log(struct usb_ctrlrequest *req)
enable_logf(true);
}
/* default implementation, relying on the compiler to produce correct code,
* targets should reimplement this... */
uint8_t __attribute__((weak)) target_read8(const void *addr)
{
return *(volatile uint8_t *)addr;
}
uint16_t __attribute__((weak)) target_read16(const void *addr)
{
return *(volatile uint16_t *)addr;
}
uint32_t __attribute__((weak)) target_read32(const void *addr)
{
return *(volatile uint32_t *)addr;
}
void __attribute__((weak)) target_write8(void *addr, uint8_t val)
{
*(volatile uint8_t *)addr = val;
}
void __attribute__((weak)) target_write16(void *addr, uint16_t val)
{
*(volatile uint16_t *)addr = val;
}
void __attribute__((weak)) target_write32(void *addr, uint32_t val)
{
*(volatile uint32_t *)addr = val;
}
static bool read_atomic(void *dst, void *src, size_t sz)
{
switch(sz)
{
case 1: *(uint8_t *)dst = target_read8(src); return true;
case 2: *(uint16_t *)dst = target_read16(src); return true;
case 4: *(uint32_t *)dst = target_read32(src); return true;
default: return false;
}
}
static void handle_read(struct usb_ctrlrequest *req)
{
static uint32_t last_addr = 0;
@ -377,13 +420,30 @@ static void handle_read(struct usb_ctrlrequest *req)
{
if(id != last_id)
return usb_drv_stall(EP_CONTROL, true, true);
memcpy(usb_buffer, (void *)last_addr, req->wLength);
if(req->bRequest == HWSTUB_READ2_ATOMIC)
{
if(!read_atomic(usb_buffer, (void *)last_addr, req->wLength))
return usb_drv_stall(EP_CONTROL, true, true);
}
else
memcpy(usb_buffer, (void *)last_addr, req->wLength);
asm volatile("nop" : : : "memory");
usb_drv_send(EP_CONTROL, usb_buffer, req->wLength);
usb_drv_recv(EP_CONTROL, NULL, 0);
}
};
static bool write_atomic(void *dst, void *src, size_t sz)
{
switch(sz)
{
case 1: target_write8(dst, *(uint8_t *)src); return true;
case 2: target_write16(dst, *(uint16_t *)src); return true;
case 4: target_write32(dst, *(uint32_t *)src); return true;
default: return false;
}
}
static void handle_write(struct usb_ctrlrequest *req)
{
int size = usb_drv_recv(EP_CONTROL, usb_buffer, req->wLength);
@ -392,7 +452,13 @@ static void handle_write(struct usb_ctrlrequest *req)
int sz_hdr = sizeof(struct hwstub_write_req_t);
if(size < sz_hdr)
return usb_drv_stall(EP_CONTROL, true, true);
memcpy((void *)write->dAddress, usb_buffer + sz_hdr, size - sz_hdr);
if(req->bRequest == HWSTUB_WRITE_ATOMIC)
{
if(!write_atomic((void *)write->dAddress, usb_buffer + sz_hdr, size - sz_hdr))
return usb_drv_stall(EP_CONTROL, true, true);
}
else
memcpy((void *)write->dAddress, usb_buffer + sz_hdr, size - sz_hdr);
usb_drv_send(EP_CONTROL, NULL, 0);
}
@ -451,8 +517,10 @@ static void handle_class_intf_req(struct usb_ctrlrequest *req)
return handle_get_log(req);
case HWSTUB_READ:
case HWSTUB_READ2:
case HWSTUB_READ2_ATOMIC:
return handle_read(req);
case HWSTUB_WRITE:
case HWSTUB_WRITE_ATOMIC:
return handle_write(req);
case HWSTUB_EXEC:
return handle_exec(req);