This repository has been archived by the owner on Sep 21, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 15
Implement code generator #9
Closed
Closed
Changes from 3 commits
Commits
Show all changes
31 commits
Select commit
Hold shift + click to select a range
e8b7235
Implement code generator
roblabla 9f394ee
Handle refcounted construction and destruction properly
roblabla 166213e
Hide dependency on sm, use goto for err handling
roblabla 5667dc7
Properly decrement refcount on init failure
roblabla a326031
Document struct fields
roblabla 6cbf6a4
Add usb:ds documentation
roblabla 58bff8c
Add interface-level comments
roblabla 76584c9
Fix silly mistakes
roblabla 204ba97
Add command version range in the AST
roblabla c94537d
Add KObject specification
roblabla 66dab4a
Generate docs
roblabla fbf5d05
Implement decorator syntax proposed by hthh
roblabla cbdc4aa
Show the version number in docs, fix a small bug, and document versio…
roblabla 26e2e57
Run gendocs
roblabla 21f828b
Add a switchbrew scraper
roblabla 128cd99
Grab interface name from switchbrew
roblabla 0f49d4e
Add a script to fuse auto.id and switchbrew data
roblabla 4926013
Now with 100% more sorting, 200% more interfaces, and 300% more fusing
roblabla 205f169
Remove PM, as switchbrew has more info
roblabla 2932e23
Fix the links
roblabla 41713ab
Rerun gendocs
roblabla 67e6808
Namespace types, replace void with unknown
roblabla 80ffc28
Rename destructor to %s_destruct
roblabla 51f26bc
Various improvements
roblabla 33e4f0b
Python3 compatibility
roblabla 23440bc
Python3 compat, use argparse instead of getopt
roblabla cd058c9
Fix fsp-srv definition
roblabla 5ecd021
Fix a bunch of bugs. Start generating the header
roblabla aaabbf0
Merge branch 'feature-betterIPC' into feature-codegen
roblabla 2a491f7
Port gencode to new version
roblabla d28ee46
Document undocumented functions
roblabla File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,317 @@ | ||
import sys | ||
sys.path.append('.') | ||
import idparser | ||
|
||
BUILTINS = { | ||
"u8": { "len": 1, "ctype": "uint8_t" }, | ||
"u16": { "len": 2, "ctype": "uint16_t" }, | ||
"u32": { "len": 4, "ctype": "uint32_t" }, | ||
"u64": { "len": 8, "ctype": "uint64_t" }, | ||
"u128": { "len": 16, "ctype": "uint128_t" }, # DERP | ||
} | ||
|
||
def camelToSnake(s): | ||
""" | ||
Is it ironic that this function is written in camel case, yet it | ||
converts to snake case? hmm.. | ||
""" | ||
import re | ||
_underscorer1 = re.compile(r'(.)([A-Z][a-z]+)') | ||
_underscorer2 = re.compile('([a-z0-9])([A-Z])') | ||
subbed = _underscorer1.sub(r'\1_\2', s) | ||
return _underscorer2.sub(r'\1_\2', subbed).lower() | ||
|
||
def ninty_to_c(s): | ||
return s.replace("-", "_").replace("::", "_").replace(":", "_") | ||
|
||
def findCmd(ifname, id): | ||
for name, cmd in ifaces[ifname].items(): | ||
if cmd['cmdId'] == id: | ||
return (name, cmd) | ||
return None | ||
|
||
def emitInt(x): | ||
return '0x%x' % x if x > 9 else str(x) | ||
|
||
def formatArgs(out_elems, in_elems, output=False): | ||
from functools import partial | ||
if output and len(elems) > 1: | ||
return '(%s)' % format(elems) | ||
|
||
def sub(output, elem): | ||
idx, elem = elem | ||
name, elem = elem | ||
ret = None | ||
|
||
if name is None and idx is not None: | ||
name = 'unk%s' % idx | ||
|
||
if elem[0] == 'array': | ||
ret = 'array<%s, %s>' % (sub(output, (idx, (None, elem[1]))), emitInt(elem[2])) | ||
elif elem[0] == 'buffer': | ||
if elem[3] == 0: | ||
return sub(True, (None, (name, elem[1]))) + ", " + sub(output, (None, (name + "_size", ["u64"]))) | ||
else: | ||
ret = 'buffer<%s, %s, %s>' % (sub(output, (None, (None, elem[1]))), emitInt(elem[2]), emitInt(elem[3])) | ||
elif elem[0] == 'object': | ||
ret = "ipc_object_t" | ||
#it = elem[1][0] | ||
#if it in ifaces: | ||
# ret = 'object<%s>' % it | ||
#else: | ||
# raise Exception("Unknown object %s" % it) | ||
#ret = it | ||
elif elem[0] == 'KObject': | ||
ret = 'KObject' | ||
elif elem[0] == 'align': | ||
ret = 'align<%s, %s>' % (emitInt(elem[1]), sub(output, (None, (None, elem[2])))) | ||
elif elem[0] == 'bytes': | ||
ret = '%suint8_t %s[%s]' % ("const " if not output else "", name, emitInt(elem[1])) | ||
name = None | ||
elif elem[0] == 'pid': | ||
return '' | ||
elif elem[0] in types: | ||
ret = elem[0] | ||
elif elem[0] in BUILTINS: | ||
assert len(elem) == 1 | ||
ret = BUILTINS[elem[0]]['ctype'] | ||
else: | ||
assert len(elem) == 1 | ||
ret = elem[0] | ||
|
||
assert ret is not None | ||
|
||
if name: | ||
return '%s %s%s' % (ninty_to_c(ret), "*" if output else "", name) | ||
else: | ||
return ret | ||
|
||
return ', '.join(filter(None, map(partial(sub, True), enumerate(out_elems))) + filter(None, map(partial(sub, False), enumerate(in_elems)))) | ||
|
||
def generate_input_code(name, ty, rawoffset, bufferlist): | ||
buf = "" | ||
if ty[0] == 'pid': | ||
buf += "\trq.send_pid = true;" | ||
elif ty[0] == 'buffer': | ||
buf += "\tipc_buffer_t %s_in_buf = {\n" % name | ||
buf += "\t\t.addr = %s,\n" % name | ||
buf += "\t\t.size = %s,\n" % (ty[3] if ty[3] != 0 else (name + "_size")) | ||
buf += "\t\t.type = %s,\n" % ty[2] | ||
buf += "\t};" | ||
bufferlist.append("%s_in_buf" % name) | ||
elif ty[0] == 'KObject': | ||
buf += "KObject %s NOTSUPPORTED" % name | ||
elif ty[0] == 'align': | ||
buf += "align %s NOTSUPPORTED" % name | ||
elif ty[0] == 'bytes': | ||
buf += "\tmemcpy(rq.raw_data + %d, %s, %d);" % (rawoffset, name, ty[1]) | ||
rawoffset += ty[1] | ||
elif ty[0] in types: | ||
#print "\tmemcpy(raw + %d, %s, sizeof(%s));" % (rawoffset, name, ninty_to_c(ty[0])) | ||
return generate_input_code(name, types[ty[0]], rawoffset, bufferlist) | ||
elif ty[0] in BUILTINS: | ||
buf += "\t*(%s*)(rq.raw_data + %d) = %s;" % (BUILTINS[ty[0]]["ctype"], rawoffset, name) | ||
rawoffset += BUILTINS[ty[0]]["len"] | ||
else: | ||
raise Exception("Unknown type %s" % ty[0]) | ||
return (buf, rawoffset) | ||
|
||
def generate_output_code(name, ty, objectlen, rawoffset, objectoffset, bufferlist): | ||
buf_pre = "" | ||
buf_post = "" | ||
if ty[0] == 'buffer': | ||
buf_pre += "\tipc_buffer_t %s_out_buf = {\n" % name | ||
buf_pre += "\t\t.addr = %s,\n" % name | ||
buf_pre += "\t\t.size = %s,\n" % (ty[3] if ty[3] != 0 else (name + "_size")) | ||
buf_pre += "\t\t.type = %s,\n" % ty[2] | ||
buf_pre += "\t};" | ||
bufferlist.append("%s_out_buf" % name) | ||
elif ty[0] == 'object': | ||
if objectlen == 1 and objectoffset == 0: | ||
buf_pre += "\trs.objects = %s;" % name | ||
buf_pre += "\trs.num_objects = 1;" | ||
elif objectlen > 1: | ||
buf_post += "\t*%s = rs.objects[%d];" % (name, objectoffset) | ||
else: | ||
raise Exception("Weird bug happened !") | ||
objectoffset += 1 | ||
elif ty[0] == 'KObject': | ||
buf_pre += "KObject %s NOTSUPPORTED" % name | ||
elif ty[0] == 'align': | ||
buf_pre += "align %s NOTSUPPORTED" % name | ||
elif ty[0] == 'bytes': | ||
buf_pre += "\tmemcpy(%s, rs.raw_data + %d, %d);" % (name, rawoffset, ty[1]) | ||
rawoffset += ty[1] | ||
elif ty[0] in types: | ||
#print "\tmemcpy(raw + %d, %s, sizeof(%s));" % (rawoffset, name, ninty_to_c(ty[0])) | ||
return generate_output_code(name, types[ty[0]], objectlen, rawoffset, objectoffset, bufferlist) | ||
elif ty[0] in BUILTINS: | ||
buf_pre += "\t*%s = (%s)(rs.raw_data + %d);" % (name, BUILTINS[ty[0]]["ctype"], rawoffset) | ||
rawoffset += BUILTINS[ty[0]]["len"] | ||
else: | ||
raise Exception("Unknown type %s" % ty[0]) | ||
return (buf_pre, buf_post, rawoffset, objectoffset) | ||
|
||
types, ifaces, services = idparser.getAll() | ||
invServices = {svc : ifname for ifname, svcs in services.items() for svc in svcs} | ||
returnedBy = {} | ||
takenBy = {} | ||
|
||
for name, cmds in ifaces.items(): | ||
for cmd in cmds.values(): | ||
for _, elem in cmd['inputs']: | ||
if elem[0] == 'object': | ||
c = elem[1][0] | ||
if c not in takenBy: | ||
takenBy[c] = [] | ||
takenBy[c].append((name, cmd['cmdId'])) | ||
for _, elem in cmd['outputs']: | ||
if elem[0] == 'object': | ||
c = elem[1][0] | ||
if c not in returnedBy: | ||
returnedBy[c] = [] | ||
returnedBy[c].append((name, cmd['cmdId'])) | ||
|
||
if len(sys.argv) < 2: | ||
print "Usage: %s <servicename>" % sys.argv[0] | ||
exit() | ||
|
||
ifacename = sys.argv[1] | ||
c_ifacename = ninty_to_c(ifacename) | ||
iface = ifaces[invServices[ifacename]] | ||
|
||
def gen_init(): | ||
cmd = iface.get('Initialize') | ||
args = "void" if cmd is None else formatArgs(cmd['outputs'], cmd['inputs']) | ||
|
||
print "result_t %s_init(%s) {" % (c_ifacename, args) | ||
print "\tif(%s_initializations++ > 0) {" % c_ifacename | ||
print "\t\treturn RESULT_OK;" | ||
print "\t}" | ||
print "" | ||
print "\tresult_t res;" | ||
print "\tres = sm_init();" | ||
print "\tif (res != RESULT_OK) {" | ||
print "\t\tgoto fail;" | ||
print "\t}" | ||
print "" | ||
print "\tres = sm_get_service(&%s_object, \"%s\");" % (c_ifacename, ifacename) | ||
print "\tif (res != RESULT_OK) {" | ||
print "\t\tgoto fail_sm;" | ||
print "\t}" | ||
print "" | ||
print "\tsm_finalize();" | ||
if cmd is not None: | ||
print "" | ||
gen_ipc_method(cmd) | ||
print "\tif (res != RESULT_OK) {" | ||
print "\t\tgoto fail;" | ||
print "\t}" | ||
print "\treturn RESULT_OK;" | ||
print "fail_sm:" | ||
print "\tsm_finalize();" | ||
print "fail:" | ||
print "\treturn res;" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. don't forget to decrease references here |
||
print "}" | ||
|
||
def gen_finalize(): | ||
# force_finalize | ||
print "static void %s_force_finalize() {" % c_ifacename | ||
print "\tipc_close(%s_object);" % c_ifacename | ||
print "\t%s_initializations = 0;" % c_ifacename | ||
print "}" | ||
|
||
# finalize | ||
print "void %s_finalize() {" % c_ifacename | ||
print "\tif(--%s_initializations == 0) {" % c_ifacename | ||
print "\t\t%s_force_finalize();" % c_ifacename | ||
print "\t}" | ||
print "}" | ||
|
||
# destructor | ||
print "static __attribute__((destructor)) void %s_finalize() {" % c_ifacename | ||
print "\tif(%s_initializations > 0) {" % c_ifacename | ||
print "\t\t%s_force_finalize();" % c_ifacename | ||
print "\t}" | ||
print "}" | ||
|
||
|
||
def gen_ipc_method(cmd): | ||
print "\tipc_request_t rq = ipc_default_request;" | ||
print "\tipc_response_fmt_t rs = ipc_default_response_fmt;" | ||
print "\trq.request_id = %d;" % cmd['cmdId'] | ||
|
||
print "" | ||
|
||
bufferlist = [] | ||
rawoffset = 0 | ||
buf = "" | ||
for (idx, (name, ty)) in enumerate(cmd['inputs']): | ||
if name is None: | ||
name = 'unk%s' % idx | ||
tmpbuf, rawoffset = generate_input_code(name, ty, rawoffset, bufferlist) | ||
buf += tmpbuf + "\n" | ||
|
||
if rawoffset != 0: | ||
print "\tuint8_t raw[%d];" % rawoffset | ||
print "\trq.raw_data = raw;" | ||
print "\trq.raw_data_size = %d;" % rawoffset | ||
print "" | ||
|
||
if buf != "": | ||
print buf | ||
print "" | ||
|
||
rawoffset = 0 | ||
bufpre = "" | ||
bufpost = "" | ||
objectoffset = 0 | ||
objectlen = 1 | ||
for (idx, (name, ty)) in enumerate(cmd['outputs']): | ||
if name is None: | ||
name = 'unk%s' % (idx + len(cmd['inputs'])) | ||
tmpbufpre, tmpbufpost, rawoffset, objectoffset = generate_output_code(name, ty, objectlen, rawoffset, objectoffset, bufferlist) | ||
bufpre += tmpbufpre + "\n" | ||
bufpost += tmpbufpost + "\n" | ||
|
||
if rawoffset != 0: | ||
print "\tuint8_t output_raw[%d];" % rawoffset | ||
print "\trs.raw_data = output_raw;" | ||
print "\trs.raw_data_size = %d;" % rawoffset | ||
|
||
if objectlen > 1: | ||
print "\tipc_object_t objects[%d];" % objectlen | ||
print "\trs.objects = objects;" | ||
print "\trs.num_objects = %d;" % objectlen | ||
|
||
if rawoffset != 0 or objectlen > 1 or len(bufferlist) > 0: | ||
print "" | ||
|
||
if bufpre != "": | ||
print bufpre | ||
print "" | ||
|
||
if len(bufferlist) > 0: | ||
print "\tipc_buffer_t *buffers[] = {" | ||
print ",\n".join(["\t\t&%s" % buf for buf in bufferlist]) | ||
print "\t};" | ||
print "\trq.num_buffers = %d;" % len(bufferlist) | ||
print "\trq.buffers = buffers;" | ||
|
||
print "\tres = ipc_send(%s_object, &rq, &rs);" % c_ifacename | ||
|
||
if bufpost != "": | ||
print bufpost | ||
|
||
print "static ipc_object_t %s_object;" % c_ifacename | ||
print "static int %s_initializations = 0;" % c_ifacename | ||
gen_init() | ||
gen_finalize() | ||
for cname, cmd in sorted(iface.items(), key=lambda x: x[1]['cmdId']): | ||
if cname == "Initialize": | ||
continue | ||
print "result_t %s_%s(%s) {" % (c_ifacename, camelToSnake(cname), formatArgs(cmd['outputs'], cmd['inputs'])) | ||
print "\tresult_t res;" | ||
gen_ipc_method(cmd) | ||
print "\treturn res;" | ||
print "}" |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IPC modules should initialize
sm
before callingsm_get_service
so as to hide their dependency on it, and make sure to also close it afterwards. You also need to decreaseinitializations
on failure. See https://github.com/reswitched/libtransistor/blob/master/lib/ipc/vi.c#L42I normally don't nitpick on this, but I'd like autogenerated code to follow our coding style, so it should look more like this: