1
0
Fork 0
forked from len0rd/rockbox

hwstub: add support for coprocessor operations

At the moment the stub only implement them for MIPS.

Change-Id: Ica835a0e9c70fa5675c3d655eae986e812a47de8
This commit is contained in:
Amaury Pouly 2016-08-04 17:06:11 +01:00
parent d91d9f6851
commit 8fabbb008c
11 changed files with 423 additions and 17 deletions

View file

@ -62,6 +62,8 @@ enum class error
NET_ERROR, /** Network error */
TIMEOUT, /** Operation timed out */
OVERFLW, /** Operation stopped to prevent buffer overflow */
UNIMPLEMENTED, /** Operation has not been implemented */
UNSUPPORTED, /** Operation is not supported by device/protocol */
};
/** Return a string explaining the error */
@ -266,6 +268,15 @@ public:
* according to the buffer size and call read_dev() and write_dev() */
error read(uint32_t addr, void *buf, size_t& sz, bool atomic);
error write(uint32_t addr, const void *buf, size_t& sz, bool atomic);
/** Execute a general cop operation: if out_data is not null, data is appended to header,
* if in_data is not null, a read operation follows to retrieve some data.
* The in_data parameters is updated to reflect the number of transfered bytes */
error cop_op(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], const void *out_data,
size_t out_size, void *in_data, size_t *in_size);
/** Execute a coprocessor read operation */
error read32_cop(uint8_t args[HWSTUB_COP_ARGS], uint32_t& value);
/** Execute a coprocessor write operation */
error write32_cop(uint8_t args[HWSTUB_COP_ARGS], uint32_t value);
/** Get device buffer size: any read() or write() greater than this size
* will be split into several transaction to avoid overflowing device
* buffer. */
@ -303,6 +314,11 @@ protected:
virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz) = 0;
virtual error get_dev_log(void *buf, size_t& buf_sz) = 0;
virtual error exec_dev(uint32_t addr, uint16_t flags) = 0;
/* cop operation: if out_data is not null, data is appended to header, if
* in_data is not null, a READ2 operation follows to retrieve some data
* The in_data parameters is updated to reflect the number of transfered bytes*/
virtual error cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], const void *out_data,
size_t out_size, void *in_data, size_t *in_size) = 0;
std::shared_ptr<device> m_dev; /* pointer to device */
std::atomic<int> m_refcnt; /* reference count */
@ -338,6 +354,8 @@ protected:
virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz);
virtual error get_dev_log(void *buf, size_t& buf_sz);
virtual error exec_dev(uint32_t addr, uint16_t flags);
virtual error cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], const void *out_data,
size_t out_size, void *in_data, size_t *in_size);
virtual error status() const;
virtual size_t get_buffer_size();

View file

@ -184,6 +184,8 @@ protected:
virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz);
virtual error get_dev_log(void *buf, size_t& buf_sz);
virtual error exec_dev(uint32_t addr, uint16_t flags);
virtual error cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], const void *out_data,
size_t out_size, void *in_data, size_t *in_size);
virtual error status() const;
virtual size_t get_buffer_size();

View file

@ -33,7 +33,7 @@
*/
#define HWSTUB_VERSION_MAJOR 4
#define HWSTUB_VERSION_MINOR 2
#define HWSTUB_VERSION_MINOR 3
#define HWSTUB_VERSION__(maj, min) #maj"."#min
#define HWSTUB_VERSION_(maj, min) HWSTUB_VERSION__(maj, min)
@ -169,6 +169,7 @@ struct hwstub_net_hdr_t
#define HWSTUB_EXEC 0x44
#define HWSTUB_READ2_ATOMIC 0x45
#define HWSTUB_WRITE_ATOMIC 0x46
#define HWSTUB_COPROCESSOR_OP 0x47
/* the following commands and the ACK/NACK mechanism are net only */
#define HWSERVER_ACK(n) (0x100|(n))
@ -251,6 +252,35 @@ struct hwstub_exec_req_t
uint16_t bmFlags;
} __attribute__((packed));
/**
* HWSTUB_COPROCESSOR_OP
* Execute a coprocessor operation. The operation is describe in the header of
* the structure. There are currently two supported operations:
* - read: following the HWSTUB_COPROCESSOR_OP, the host can retrieve the data
* by sending a HWSTUB_READ2 request (just like a regular read) of
* the appropriate size
* - write: the header is followed by the data to write.
* If the request has two parts (second being READ2) then both requests must use
* the same ID in wValue, otherwise the second request will be STALLed.
* If a particular operation is not supported, it must be STALLed by the device.
*/
#define HWSTUB_COP_READ 0 /* read operation */
#define HWSTUB_COP_WRITE 1 /* write operation */
/* for MIPS */
#define HWSTUB_COP_MIPS_COP 0 /* coprocessor number */
#define HWSTUB_COP_MIPS_REG 1 /* coprocessor register */
#define HWSTUB_COP_MIPS_SEL 2 /* coprocessor select */
#define HWSTUB_COP_ARGS 7 /* maximum number of arguments */
struct hwstub_cop_req_t
{
uint8_t bOp; /* operation to execute */
uint8_t bArgs[HWSTUB_COP_ARGS]; /* arguments to the operation */
/* followed by data for WRITE operation */
};
/**
* HWSERVER_HELLO:
* Say hello to the server, give protocol version and get server version.

View file

@ -132,6 +132,8 @@ protected:
virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz);
virtual error get_dev_log(void *buf, size_t& buf_sz);
virtual error exec_dev(uint32_t addr, uint16_t flags);
virtual error cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], const void *out_data,
size_t out_size, void *in_data, size_t *in_size);
virtual error status() const;
virtual size_t get_buffer_size();
/* Probe a device to check if it is an hwstub device and return the interface
@ -162,6 +164,8 @@ protected:
virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz);
virtual error get_dev_log(void *buf, size_t& buf_sz);
virtual error exec_dev(uint32_t addr, uint16_t flags);
virtual error cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], const void *out_data,
size_t out_size, void *in_data, size_t *in_size);
virtual error status() const;
virtual size_t get_buffer_size();
error probe();

View file

@ -147,6 +147,8 @@ protected:
virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz);
virtual error get_dev_log(void *buf, size_t& buf_sz);
virtual error exec_dev(uint32_t addr, uint16_t flags);
virtual error cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], const void *out_data,
size_t out_size, void *in_data, size_t *in_size);
virtual error status() const;
virtual size_t get_buffer_size();

View file

@ -45,6 +45,8 @@ std::string error_string(error err)
case error::PROTOCOL_ERROR: return "network protocol error";
case error::TIMEOUT: return "timeout";
case error::OVERFLW: return "overflow";
case error::UNIMPLEMENTED: return "operation is not implemented";
case error::UNSUPPORTED: return "operation unsupported";
default: return "unknown error";
}
}
@ -457,6 +459,38 @@ error handle::write(uint32_t addr, const void *buf, size_t& sz, bool atomic)
return error::SUCCESS;
}
error handle::cop_op(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], const void *out_data,
size_t out_size, void *in_data, size_t *in_size)
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
/* get a pointer so that it's not destroyed during the runtime of the function,
* the pointer will be released at the end of the function */
std::shared_ptr<context> ctx = m_dev->get_context();
if(!ctx)
return error::NO_CONTEXT;
/* ensure valid status */
error err = status();
if(err != error::SUCCESS)
return err;
return cop_dev(op, args, out_data, out_size, in_data, in_size);
}
error handle::read32_cop(uint8_t args[HWSTUB_COP_ARGS], uint32_t& value)
{
size_t sz = sizeof(value);
error err = cop_op(HWSTUB_COP_READ, args, nullptr, 0, &value, &sz);
if(err != error::SUCCESS)
return err;
if(sz != sizeof(value))
return error::ERROR;
return error::SUCCESS;
}
error handle::write32_cop(uint8_t args[HWSTUB_COP_ARGS], uint32_t value)
{
return cop_op(HWSTUB_COP_WRITE, args, &value, sizeof(value), nullptr, nullptr);
}
error handle::status() const
{
/* check context */
@ -616,6 +650,18 @@ error dummy_handle::exec_dev(uint32_t addr, uint16_t flags)
return error::DUMMY;
}
error dummy_handle::cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS],
const void *out_data, size_t out_size, void *in_data, size_t *in_size)
{
(void) op;
(void) args;
(void) out_data;
(void) out_size;
(void) in_data;
(void) in_size;
return error::DUMMY;
}
error dummy_handle::status() const
{
error err = handle::status();

View file

@ -735,6 +735,18 @@ error handle::exec_dev(uint32_t addr, uint16_t flags)
return error::SUCCESS;
}
error handle::cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS],
const void *out_data, size_t out_size, void *in_data, size_t *in_size)
{
(void) op;
(void) args;
(void) out_data;
(void) out_size;
(void) in_data;
(void) in_size;
return error::UNIMPLEMENTED;
}
error handle::status() const
{
return hwstub::handle::status();

View file

@ -399,6 +399,46 @@ error rb_handle::write_dev(uint32_t addr, const void *buf, size_t& sz, bool atom
return ret;
}
error rb_handle::cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS],
const void *out_data, size_t out_size, void *in_data, size_t *in_size)
{
(void) op;
(void) args;
(void) out_data;
(void) out_size;
(void) in_data;
(void) in_size;
std::shared_ptr<hwstub::context> hctx = get_device()->get_context();
if(!hctx)
return error::NO_CONTEXT;
/* construct out request: header followed by (optional) data */
size_t hdr_sz = sizeof(struct hwstub_cop_req_t);
uint8_t *tmp_buf = new uint8_t[out_size + hdr_sz];
struct hwstub_cop_req_t *req = reinterpret_cast<struct hwstub_cop_req_t *>(tmp_buf);
req->bOp = op;
for(int i = 0; i < HWSTUB_COP_ARGS; i++)
req->bArgs[i] = args[i];
if(out_size > 0)
memcpy(tmp_buf + hdr_sz, out_data, out_size);
error ret = interpret_libusb_error(libusb_control_transfer(m_handle,
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT,
HWSTUB_COPROCESSOR_OP, m_transac_id++, m_intf,
(unsigned char *)req, out_size + hdr_sz, m_timeout), out_size + hdr_sz);
delete[] tmp_buf;
/* return errors if any */
if(ret != error::SUCCESS)
return ret;
/* return now if there is no read stage */
if(in_data == nullptr)
return error::SUCCESS;
/* perform read stage (use the same transaction ID) */
return interpret_libusb_size(libusb_control_transfer(m_handle,
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_IN,
HWSTUB_READ2, m_transac_id - 1, m_intf,
(unsigned char *)in_data, *in_size, m_timeout), *in_size);
}
bool rb_handle::find_intf(struct libusb_device_descriptor *dev,
struct libusb_config_descriptor *config, int& intf_idx)
{
@ -674,6 +714,18 @@ error jz_handle::write_dev(uint32_t addr, const void *buf, size_t& sz, bool atom
return ret;
}
error jz_handle::cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS],
const void *out_data, size_t out_size, void *in_data, size_t *in_size)
{
(void) op;
(void) args;
(void) out_data;
(void) out_size;
(void) in_data;
(void) in_size;
return error::UNSUPPORTED;
}
bool jz_handle::is_boot_dev(struct libusb_device_descriptor *dev,
struct libusb_config_descriptor *config)
{

View file

@ -221,6 +221,18 @@ error handle::exec_dev(uint32_t addr, uint16_t flags)
return p ? p->exec_dev(addr, flags) : error::DISCONNECTED;
}
error handle::cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS],
const void *out_data, size_t out_size, void *in_data, size_t *in_size)
{
(void) op;
(void) args;
(void) out_data;
(void) out_size;
(void) in_data;
(void) in_size;
return error::UNSUPPORTED;
}
error handle::status() const
{
return hwstub::handle::status();

View file

@ -400,10 +400,12 @@ static bool read_atomic(void *dst, void *src, size_t sz)
}
}
static void *last_read_addr = 0;
static uint16_t last_read_id = 0xffff;
static size_t last_read_max_size = 0;
static void handle_read(struct usb_ctrlrequest *req)
{
static uint32_t last_addr = 0;
static uint16_t last_id = 0xffff;
uint16_t id = req->wValue;
if(req->bRequest == HWSTUB_READ)
@ -413,26 +415,29 @@ static void handle_read(struct usb_ctrlrequest *req)
return usb_drv_stall(EP_CONTROL, true, true);
asm volatile("nop" : : : "memory");
struct hwstub_read_req_t *read = (void *)usb_buffer;
last_addr = read->dAddress;
last_id = id;
last_read_addr = (void *)read->dAddress;
last_read_max_size = usb_buffer_size;
last_read_id = id;
usb_drv_send(EP_CONTROL, NULL, 0);
}
else
{
if(id != last_id)
/* NOTE: READ2 is also called after a coprocessor operation */
if(id != last_read_id)
return usb_drv_stall(EP_CONTROL, true, true);
size_t len = MIN(req->wLength, last_read_max_size);
if(req->bRequest == HWSTUB_READ2_ATOMIC)
{
if(set_data_abort_jmp() == 0)
{
if(!read_atomic(usb_buffer, (void *)last_addr, req->wLength))
if(!read_atomic(usb_buffer, last_read_addr, len))
return usb_drv_stall(EP_CONTROL, true, true);
}
else
{
logf("trapped read data abort in [0x%x,0x%x]\n", last_addr,
last_addr + req->wLength);
logf("trapped read data abort in [0x%x,0x%x]\n", last_read_addr,
last_read_addr + len);
return usb_drv_stall(EP_CONTROL, true, true);
}
}
@ -440,19 +445,19 @@ static void handle_read(struct usb_ctrlrequest *req)
{
if(set_data_abort_jmp() == 0)
{
memcpy(usb_buffer, (void *)last_addr, req->wLength);
memcpy(usb_buffer, last_read_addr, len);
asm volatile("nop" : : : "memory");
}
else
{
logf("trapped read data abort in [0x%x,0x%x]\n", last_addr,
last_addr + req->wLength);
logf("trapped read data abort in [0x%x,0x%x]\n", last_read_addr,
last_read_addr + len);
return usb_drv_stall(EP_CONTROL, true, true);
}
}
usb_drv_send(EP_CONTROL, usb_buffer, req->wLength);
usb_drv_send(EP_CONTROL, usb_buffer, len);
usb_drv_recv(EP_CONTROL, NULL, 0);
}
}
@ -562,6 +567,163 @@ static void handle_exec(struct usb_ctrlrequest *req)
}
}
#ifdef CPU_MIPS
static uint32_t rw_cp0_inst_buffer[3];
typedef uint32_t (*read_cp0_inst_buffer_fn_t)(void);
typedef void (*write_cp0_inst_buffer_fn_t)(uint32_t);
uint32_t mips_read_cp0(unsigned reg, unsigned sel)
{
/* ok this is tricky because the coprocessor read instruction encoding
* contains the register and select, so we need to generate the instruction
* on the fly, we generate a "function like" buffer with three instructions:
* mfc0 v0, reg, sel
* jr ra
* nop
*/
rw_cp0_inst_buffer[0] = 0x40000000 | /*v0*/2 << 16 | (sel & 0x7) | (reg & 0x1f) << 11;
rw_cp0_inst_buffer[1] = /*ra*/31 << 21 | 0x8; /* jr ra */
rw_cp0_inst_buffer[2] = 0; /* nop */
#ifdef CONFIG_FLUSH_CACHES
target_flush_caches();
#endif
read_cp0_inst_buffer_fn_t fn = (read_cp0_inst_buffer_fn_t)rw_cp0_inst_buffer;
return fn();
}
void mips_write_cp0(unsigned reg, unsigned sel, uint32_t val)
{
/* ok this is tricky because the coprocessor write instruction encoding
* contains the register and select, so we need to generate the instruction
* on the fly, we generate a "function like" buffer with three instructions:
* mtc0 a0, reg, sel
* jr ra
* nop
*/
rw_cp0_inst_buffer[0] = 0x40800000 | /*a0*/4 << 16 | (sel & 0x7) | (reg & 0x1f) << 11;
rw_cp0_inst_buffer[1] = /*ra*/31 << 21 | 0x8; /* jr ra */
rw_cp0_inst_buffer[2] = 0; /* nop */
#ifdef CONFIG_FLUSH_CACHES
target_flush_caches();
#endif
write_cp0_inst_buffer_fn_t fn = (write_cp0_inst_buffer_fn_t)rw_cp0_inst_buffer;
fn(val);
}
#endif
/* coprocessor read: return <0 on error (-2 for dull dump), or size to return
* to host otherwise */
int cop_read(uint8_t args[HWSTUB_COP_ARGS], void *out_data, size_t out_max_sz)
{
/* virtually all targets do register-based operation, so 32-bit */
if(out_max_sz < 4)
{
logf("cop read failed: output buffer is too small\n");
return -1;
}
#ifdef CPU_MIPS
if(args[HWSTUB_COP_MIPS_COP] != 0)
{
logf("cop read failed: only mips cp0 is supported\n");
return -2;
}
*(uint32_t *)out_data = mips_read_cp0(args[HWSTUB_COP_MIPS_REG], args[HWSTUB_COP_MIPS_SEL]);
return 4;
#else
(void) args;
(void) out_data;
(void) out_max_sz;
logf("cop read failed: unsupported cpu\n");
return -1;
#endif
}
/* coprocessor write: return <0 on error (-2 for dull dump), or 0 on success */
int cop_write(uint8_t args[HWSTUB_COP_ARGS], const void *in_data, size_t in_sz)
{
/* virtually all targets do register-based operation, so 32-bit */
if(in_sz != 4)
{
logf("cop read failed: input buffer has wrong size\n");
return -1;
}
#ifdef CPU_MIPS
if(args[HWSTUB_COP_MIPS_COP] != 0)
{
logf("cop read failed: only mips cp0 is supported\n");
return -2;
}
mips_write_cp0(args[HWSTUB_COP_MIPS_REG], args[HWSTUB_COP_MIPS_SEL], *(uint32_t *)in_data);
return 0;
#else
(void) args;
(void) in_data;
(void) in_sz;
logf("cop write failed: unsupported cpu\n");
return -1;
#endif
}
/* return size to return to host or <0 on error */
int do_cop_op(struct hwstub_cop_req_t *cop, void *in_data, size_t in_sz,
void *out_data, size_t out_max_sz)
{
int ret = -2; /* -2 means full debug dump */
/* handle operations */
if(cop->bOp == HWSTUB_COP_READ)
{
/* read cannot have extra data */
if(in_sz > 0)
goto Lerr;
ret = cop_read(cop->bArgs, out_data, out_max_sz);
}
else if(cop->bOp == HWSTUB_COP_WRITE)
{
ret = cop_write(cop->bArgs, in_data, in_sz);
}
Lerr:
if(ret == -2)
{
/* debug output */
logf("invalid cop op: %d, ", cop->bOp);
for(int i = 0; i < HWSTUB_COP_ARGS; i++)
logf("%c0x%x", i == 0 ? '[' : ',', cop->bArgs[i]);
logf("] in:%d\n", in_sz);
}
return ret;
}
static void handle_cop(struct usb_ctrlrequest *req)
{
int size = usb_drv_recv(EP_CONTROL, usb_buffer, req->wLength);
int hdr_sz = sizeof(struct hwstub_cop_req_t);
asm volatile("nop" : : : "memory");
struct hwstub_cop_req_t *cop = (void *)usb_buffer;
/* request should at least contain the header */
if(size < hdr_sz)
return usb_drv_stall(EP_CONTROL, true, true);
/* perform coprocessor operation: put output buffer after the input one,
* limit output buffer size to maximum buffer size */
uint8_t *in_buf = usb_buffer + hdr_sz;
size_t in_sz = req->wLength - hdr_sz;
uint8_t *out_buf = in_buf + in_sz;
size_t out_max_sz = usb_buffer_size - req->wLength;
int ret = do_cop_op(cop, in_buf, in_sz, out_buf, out_max_sz);
/* STALL on error */
if(ret < 0)
return usb_drv_stall(EP_CONTROL, true, true);
/* acknowledge */
usb_drv_send(EP_CONTROL, NULL, 0);
/* if there is a read stage, prepare everything for the READ2 */
if(ret > 0)
{
last_read_id = req->wValue;
last_read_addr = out_buf;
last_read_max_size = ret;
}
}
static void handle_class_intf_req(struct usb_ctrlrequest *req)
{
unsigned intf = req->wIndex & 0xff;
@ -581,6 +743,8 @@ static void handle_class_intf_req(struct usb_ctrlrequest *req)
return handle_write(req);
case HWSTUB_EXEC:
return handle_exec(req);
case HWSTUB_COPROCESSOR_OP:
return handle_cop(req);
default:
usb_drv_stall(EP_CONTROL, true, true);
}

View file

@ -310,9 +310,11 @@ int my_lua_call(lua_State *state)
{
int n = lua_gettop(state);
if(n != 1)
luaL_error(state, "call takes target address argument");
luaL_error(state, "call takes one argument: target address");
g_hwdev->exec(mylua_checkunsigned(state, 1), HWSTUB_EXEC_CALL);
error ret = g_hwdev->exec(luaL_checkunsigned(state, 1), HWSTUB_EXEC_CALL);
if(ret != error::SUCCESS)
luaL_error(state, "call failed");
return 0;
}
@ -320,9 +322,67 @@ int my_lua_jump(lua_State *state)
{
int n = lua_gettop(state);
if(n != 1)
luaL_error(state, "jump takes target address argument");
luaL_error(state, "jump takes one argument: target address");
g_hwdev->exec(mylua_checkunsigned(state, 1), HWSTUB_EXEC_JUMP);
error ret = g_hwdev->exec(luaL_checkunsigned(state, 1), HWSTUB_EXEC_JUMP);
if(ret != error::SUCCESS)
luaL_error(state, "jump failed");
return 0;
}
int my_lua_read32_cop(lua_State *state)
{
int n = lua_gettop(state);
if(n != 1)
luaL_error(state, "read32_cop takes one argument: arguments (array)");
uint8_t args[HWSTUB_COP_ARGS] = {0};
/* parse array */
luaL_checktype(state, 1, LUA_TTABLE);
size_t sz = lua_rawlen(state, 1);
if(sz > HWSTUB_COP_ARGS)
luaL_error(state, "coprocessor operation take at most %d arguments", HWSTUB_COP_ARGS);
for(size_t i = 0; i < sz; i++)
{
lua_pushinteger(state, i + 1); /* index start at 1 */
lua_gettable(state, 1);
/* check it is an integer */
args[i] = luaL_checkunsigned(state, -1);
/* pop it */
lua_pop(state, 1);
}
uint32_t val;
error ret = g_hwdev->read32_cop(args, val);
if(ret != error::SUCCESS)
luaL_error(state, "coprocessor read failed");
lua_pushunsigned(state, val);
return 1;
}
int my_lua_write32_cop(lua_State *state)
{
int n = lua_gettop(state);
if(n != 2)
luaL_error(state, "write32_cop takes two arguments: arguments (array) and value");
uint8_t args[HWSTUB_COP_ARGS] = {0};
/* parse array */
luaL_checktype(state, 1, LUA_TTABLE);
size_t sz = lua_rawlen(state, 1);
if(sz > HWSTUB_COP_ARGS)
luaL_error(state, "coprocessor operation take at most %d arguments", HWSTUB_COP_ARGS);
for(size_t i = 0; i < sz; i++)
{
lua_pushinteger(state, i + 1); /* index start at 1 */
lua_gettable(state, 1);
/* check it is an integer */
args[i] = luaL_checkunsigned(state, -1);
/* pop it */
lua_pop(state, 1);
}
error ret = g_hwdev->write32_cop(args, luaL_checkunsigned(state, 2));
if(ret != error::SUCCESS)
luaL_error(state, "coprocessor write failed");
return 0;
}
@ -753,6 +813,10 @@ bool my_lua_import_hwstub()
lua_setfield(g_lua, -2, "call");
lua_pushcclosure(g_lua, my_lua_jump, 0);
lua_setfield(g_lua, -2, "jump");
lua_pushcclosure(g_lua, my_lua_read32_cop, 0);
lua_setfield(g_lua, -2, "read32_cop");
lua_pushcclosure(g_lua, my_lua_write32_cop, 0);
lua_setfield(g_lua, -2, "write32_cop");
lua_setfield(g_lua, -2, "dev");