1/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
2
3/*
4 * Copyright (C) 2014 Rob Clark <robclark@freedesktop.org>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 *
25 * Authors:
26 *    Rob Clark <robclark@freedesktop.org>
27 */
28
29#define LUA_COMPAT_APIINTCASTS
30
31#include <assert.h>
32#include <lauxlib.h>
33#include <lua.h>
34#include <lualib.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38
39#include "cffdec.h"
40#include "rnnutil.h"
41#include "script.h"
42
43static lua_State *L;
44
45#if 0
46#define DBG(fmt, ...)                                                          \
47   do {                                                                        \
48      printf(" ** %s:%d ** " fmt "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__); \
49   } while (0)
50#else
51#define DBG(fmt, ...)                                                          \
52   do {                                                                        \
53   } while (0)
54#endif
55
56/* An rnn based decoder, which can either be decoding current register
57 * values, or domain based decoding of a pm4 packet.
58 *
59 */
60struct rnndec {
61   struct rnn base;
62
63   /* for pm4 packet decoding: */
64   uint32_t sizedwords;
65   uint32_t *dwords;
66};
67
68static inline struct rnndec *
69to_rnndec(struct rnn *rnn)
70{
71   return (struct rnndec *)rnn;
72}
73
74static uint32_t
75rnn_val(struct rnn *rnn, uint32_t regbase)
76{
77   struct rnndec *rnndec = to_rnndec(rnn);
78
79   if (!rnndec->sizedwords) {
80      return reg_val(regbase);
81   } else if (regbase < rnndec->sizedwords) {
82      return rnndec->dwords[regbase];
83   } else {
84      // XXX throw an error
85      return -1;
86   }
87}
88
89/* does not return */
90static void
91error(const char *fmt)
92{
93   fprintf(stderr, fmt, lua_tostring(L, -1));
94   exit(1);
95}
96
97/*
98 * An enum type that can be used as string or number:
99 */
100
101struct rnndenum {
102   const char *str;
103   int val;
104};
105
106static int
107l_meta_rnn_enum_tostring(lua_State *L)
108{
109   struct rnndenum *e = lua_touserdata(L, 1);
110   if (e->str) {
111      lua_pushstring(L, e->str);
112   } else {
113      char buf[32];
114      sprintf(buf, "%u", e->val);
115      lua_pushstring(L, buf);
116   }
117   return 1;
118}
119
120/* so, this doesn't actually seem to be implemented yet, but hopefully
121 * some day lua comes to it's senses
122 */
123static int
124l_meta_rnn_enum_tonumber(lua_State *L)
125{
126   struct rnndenum *e = lua_touserdata(L, 1);
127   lua_pushinteger(L, e->val);
128   return 1;
129}
130
131static const struct luaL_Reg l_meta_rnn_enum[] = {
132   {"__tostring", l_meta_rnn_enum_tostring},
133   {"__tonumber", l_meta_rnn_enum_tonumber},
134   {NULL, NULL} /* sentinel */
135};
136
137static void
138pushenum(struct lua_State *L, int val, struct rnnenum *info)
139{
140   struct rnndenum *e = lua_newuserdata(L, sizeof(*e));
141
142   e->val = val;
143   e->str = NULL;
144
145   for (int i = 0; i < info->valsnum; i++) {
146      if (info->vals[i]->valvalid && (info->vals[i]->value == val)) {
147         e->str = info->vals[i]->name;
148         break;
149      }
150   }
151
152   luaL_newmetatable(L, "rnnmetaenum");
153   luaL_setfuncs(L, l_meta_rnn_enum, 0);
154   lua_pop(L, 1);
155
156   luaL_setmetatable(L, "rnnmetaenum");
157}
158
159/* Expose rnn decode to script environment as "rnn" library:
160 */
161
162struct rnndoff {
163   struct rnn *rnn;
164   struct rnndelem *elem;
165   uint64_t offset;
166};
167
168static void
169push_rnndoff(lua_State *L, struct rnn *rnn, struct rnndelem *elem,
170             uint64_t offset)
171{
172   struct rnndoff *rnndoff = lua_newuserdata(L, sizeof(*rnndoff));
173   rnndoff->rnn = rnn;
174   rnndoff->elem = elem;
175   rnndoff->offset = offset;
176}
177
178static int l_rnn_etype_array(lua_State *L, struct rnn *rnn,
179                             struct rnndelem *elem, uint64_t offset);
180static int l_rnn_etype_reg(lua_State *L, struct rnn *rnn, struct rnndelem *elem,
181                           uint64_t offset);
182
183static int
184pushdecval(struct lua_State *L, struct rnn *rnn, uint32_t regval,
185           struct rnntypeinfo *info)
186{
187   union rnndecval val;
188   switch (rnn_decodelem(rnn, info, regval, &val)) {
189   case RNN_TTYPE_ENUM:
190   case RNN_TTYPE_INLINE_ENUM:
191      pushenum(L, val.i, info->eenum);
192      return 1;
193   case RNN_TTYPE_INT:
194      lua_pushinteger(L, val.i);
195      return 1;
196   case RNN_TTYPE_UINT:
197   case RNN_TTYPE_HEX:
198      lua_pushunsigned(L, val.u);
199      return 1;
200   case RNN_TTYPE_FLOAT:
201      lua_pushnumber(L, val.f);
202      return 1;
203   case RNN_TTYPE_BOOLEAN:
204      lua_pushboolean(L, val.u);
205      return 1;
206   case RNN_TTYPE_INVALID:
207   default:
208      return 0;
209   }
210}
211
212static int
213l_rnn_etype(lua_State *L, struct rnn *rnn, struct rnndelem *elem,
214            uint64_t offset)
215{
216   int ret;
217   uint32_t regval;
218   DBG("elem=%p (%d), offset=%lu", elem, elem->type, offset);
219   switch (elem->type) {
220   case RNN_ETYPE_REG:
221      /* if a register has no bitfields, just return
222       * the raw value:
223       */
224      regval = rnn_val(rnn, offset);
225      regval <<= elem->typeinfo.shr;
226      ret = pushdecval(L, rnn, regval, &elem->typeinfo);
227      if (ret)
228         return ret;
229      return l_rnn_etype_reg(L, rnn, elem, offset);
230   case RNN_ETYPE_ARRAY:
231      return l_rnn_etype_array(L, rnn, elem, offset);
232   default:
233      /* hmm.. */
234      printf("unhandled type: %d\n", elem->type);
235      return 0;
236   }
237}
238
239/*
240 * Struct Object:
241 * To implement stuff like 'RB_MRT[n].CONTROL' we need a struct-object
242 * to represent the current array index (ie. 'RB_MRT[n]')
243 */
244
245static int
246l_rnn_struct_meta_index(lua_State *L)
247{
248   struct rnndoff *rnndoff = lua_touserdata(L, 1);
249   const char *name = lua_tostring(L, 2);
250   struct rnndelem *elem = rnndoff->elem;
251   int i;
252
253   for (i = 0; i < elem->subelemsnum; i++) {
254      struct rnndelem *subelem = elem->subelems[i];
255      if (!strcmp(name, subelem->name)) {
256         return l_rnn_etype(L, rnndoff->rnn, subelem,
257                            rnndoff->offset + subelem->offset);
258      }
259   }
260
261   return 0;
262}
263
264static const struct luaL_Reg l_meta_rnn_struct[] = {
265   {"__index", l_rnn_struct_meta_index}, {NULL, NULL} /* sentinel */
266};
267
268static int
269l_rnn_etype_struct(lua_State *L, struct rnn *rnn, struct rnndelem *elem,
270                   uint64_t offset)
271{
272   push_rnndoff(L, rnn, elem, offset);
273
274   luaL_newmetatable(L, "rnnmetastruct");
275   luaL_setfuncs(L, l_meta_rnn_struct, 0);
276   lua_pop(L, 1);
277
278   luaL_setmetatable(L, "rnnmetastruct");
279
280   return 1;
281}
282
283/*
284 * Array Object:
285 */
286
287static int
288l_rnn_array_meta_index(lua_State *L)
289{
290   struct rnndoff *rnndoff = lua_touserdata(L, 1);
291   int idx = lua_tointeger(L, 2);
292   struct rnndelem *elem = rnndoff->elem;
293   uint64_t offset = rnndoff->offset + (elem->stride * idx);
294
295   DBG("rnndoff=%p, idx=%d, numsubelems=%d", rnndoff, idx,
296       rnndoff->elem->subelemsnum);
297
298   /* if just a single sub-element, it is directly a register,
299    * otherwise we need to accumulate the array index while
300    * we wait for the register name within the array..
301    */
302   if (elem->subelemsnum == 1) {
303      return l_rnn_etype(L, rnndoff->rnn, elem->subelems[0], offset);
304   } else {
305      return l_rnn_etype_struct(L, rnndoff->rnn, elem, offset);
306   }
307
308   return 0;
309}
310
311static const struct luaL_Reg l_meta_rnn_array[] = {
312   {"__index", l_rnn_array_meta_index}, {NULL, NULL} /* sentinel */
313};
314
315static int
316l_rnn_etype_array(lua_State *L, struct rnn *rnn, struct rnndelem *elem,
317                  uint64_t offset)
318{
319   push_rnndoff(L, rnn, elem, offset);
320
321   luaL_newmetatable(L, "rnnmetaarray");
322   luaL_setfuncs(L, l_meta_rnn_array, 0);
323   lua_pop(L, 1);
324
325   luaL_setmetatable(L, "rnnmetaarray");
326
327   return 1;
328}
329
330/*
331 * Register element:
332 */
333
334static int
335l_rnn_reg_meta_index(lua_State *L)
336{
337   struct rnndoff *rnndoff = lua_touserdata(L, 1);
338   const char *name = lua_tostring(L, 2);
339   struct rnndelem *elem = rnndoff->elem;
340   struct rnntypeinfo *info = &elem->typeinfo;
341   struct rnnbitfield **bitfields;
342   int bitfieldsnum;
343   int i;
344
345   switch (info->type) {
346   case RNN_TTYPE_BITSET:
347      bitfields = info->ebitset->bitfields;
348      bitfieldsnum = info->ebitset->bitfieldsnum;
349      break;
350   case RNN_TTYPE_INLINE_BITSET:
351      bitfields = info->bitfields;
352      bitfieldsnum = info->bitfieldsnum;
353      break;
354   default:
355      printf("invalid register type: %d\n", info->type);
356      return 0;
357   }
358
359   for (i = 0; i < bitfieldsnum; i++) {
360      struct rnnbitfield *bf = bitfields[i];
361      if (!strcmp(name, bf->name)) {
362         uint32_t regval = rnn_val(rnndoff->rnn, rnndoff->offset);
363
364         regval &= typeinfo_mask(&bf->typeinfo);
365         regval >>= bf->typeinfo.low;
366         regval <<= bf->typeinfo.shr;
367
368         DBG("name=%s, info=%p, subelemsnum=%d, type=%d, regval=%x", name, info,
369             rnndoff->elem->subelemsnum, bf->typeinfo.type, regval);
370
371         return pushdecval(L, rnndoff->rnn, regval, &bf->typeinfo);
372      }
373   }
374
375   printf("invalid member: %s\n", name);
376   return 0;
377}
378
379static int
380l_rnn_reg_meta_tostring(lua_State *L)
381{
382   struct rnndoff *rnndoff = lua_touserdata(L, 1);
383   uint32_t regval = rnn_val(rnndoff->rnn, rnndoff->offset);
384   struct rnndecaddrinfo *info = rnn_reginfo(rnndoff->rnn, rnndoff->offset);
385   char *decoded;
386   if (info && info->typeinfo) {
387      decoded = rnndec_decodeval(rnndoff->rnn->vc, info->typeinfo, regval);
388   } else {
389      asprintf(&decoded, "%08x", regval);
390   }
391   lua_pushstring(L, decoded);
392   free(decoded);
393   if (info) {
394      free(info->name);
395      free(info);
396   }
397   return 1;
398}
399
400static int
401l_rnn_reg_meta_tonumber(lua_State *L)
402{
403   struct rnndoff *rnndoff = lua_touserdata(L, 1);
404   uint32_t regval = rnn_val(rnndoff->rnn, rnndoff->offset);
405
406   regval <<= rnndoff->elem->typeinfo.shr;
407
408   lua_pushnumber(L, regval);
409   return 1;
410}
411
412static const struct luaL_Reg l_meta_rnn_reg[] = {
413   {"__index", l_rnn_reg_meta_index},
414   {"__tostring", l_rnn_reg_meta_tostring},
415   {"__tonumber", l_rnn_reg_meta_tonumber},
416   {NULL, NULL} /* sentinel */
417};
418
419static int
420l_rnn_etype_reg(lua_State *L, struct rnn *rnn, struct rnndelem *elem,
421                uint64_t offset)
422{
423   push_rnndoff(L, rnn, elem, offset);
424
425   luaL_newmetatable(L, "rnnmetareg");
426   luaL_setfuncs(L, l_meta_rnn_reg, 0);
427   lua_pop(L, 1);
428
429   luaL_setmetatable(L, "rnnmetareg");
430
431   return 1;
432}
433
434/*
435 *
436 */
437
438static int
439l_rnn_meta_index(lua_State *L)
440{
441   struct rnn *rnn = lua_touserdata(L, 1);
442   const char *name = lua_tostring(L, 2);
443   struct rnndelem *elem;
444
445   elem = rnn_regelem(rnn, name);
446   if (!elem)
447      return 0;
448
449   return l_rnn_etype(L, rnn, elem, elem->offset);
450}
451
452static int
453l_rnn_meta_gc(lua_State *L)
454{
455   // TODO
456   // struct rnn *rnn = lua_touserdata(L, 1);
457   // rnn_deinit(rnn);
458   return 0;
459}
460
461static const struct luaL_Reg l_meta_rnn[] = {
462   {"__index", l_rnn_meta_index},
463   {"__gc", l_rnn_meta_gc},
464   {NULL, NULL} /* sentinel */
465};
466
467static int
468l_rnn_init(lua_State *L)
469{
470   const char *gpuname = lua_tostring(L, 1);
471   struct rnndec *rnndec = lua_newuserdata(L, sizeof(*rnndec));
472   _rnn_init(&rnndec->base, 0);
473   rnn_load(&rnndec->base, gpuname);
474   rnndec->sizedwords = 0;
475
476   luaL_newmetatable(L, "rnnmeta");
477   luaL_setfuncs(L, l_meta_rnn, 0);
478   lua_pop(L, 1);
479
480   luaL_setmetatable(L, "rnnmeta");
481
482   return 1;
483}
484
485static int
486l_rnn_enumname(lua_State *L)
487{
488   struct rnn *rnn = lua_touserdata(L, 1);
489   const char *name = lua_tostring(L, 2);
490   uint32_t val = (uint32_t)lua_tonumber(L, 3);
491   lua_pushstring(L, rnn_enumname(rnn, name, val));
492   return 1;
493}
494
495static int
496l_rnn_regname(lua_State *L)
497{
498   struct rnn *rnn = lua_touserdata(L, 1);
499   uint32_t regbase = (uint32_t)lua_tonumber(L, 2);
500   lua_pushstring(L, rnn_regname(rnn, regbase, 1));
501   return 1;
502}
503
504static int
505l_rnn_regval(lua_State *L)
506{
507   struct rnn *rnn = lua_touserdata(L, 1);
508   uint32_t regbase = (uint32_t)lua_tonumber(L, 2);
509   uint32_t regval = (uint32_t)lua_tonumber(L, 3);
510   struct rnndecaddrinfo *info = rnn_reginfo(rnn, regbase);
511   char *decoded;
512   if (info && info->typeinfo) {
513      decoded = rnndec_decodeval(rnn->vc, info->typeinfo, regval);
514   } else {
515      asprintf(&decoded, "%08x", regval);
516   }
517   lua_pushstring(L, decoded);
518   free(decoded);
519   if (info) {
520      free(info->name);
521      free(info);
522   }
523   return 1;
524}
525
526static const struct luaL_Reg l_rnn[] = {
527   {"init", l_rnn_init},
528   {"enumname", l_rnn_enumname},
529   {"regname", l_rnn_regname},
530   {"regval", l_rnn_regval},
531   {NULL, NULL} /* sentinel */
532};
533
534/* Expose the register state to script enviroment as a "regs" library:
535 */
536
537static int
538l_reg_written(lua_State *L)
539{
540   uint32_t regbase = (uint32_t)lua_tonumber(L, 1);
541   lua_pushnumber(L, reg_written(regbase));
542   return 1;
543}
544
545static int
546l_reg_lastval(lua_State *L)
547{
548   uint32_t regbase = (uint32_t)lua_tonumber(L, 1);
549   lua_pushnumber(L, reg_lastval(regbase));
550   return 1;
551}
552
553static int
554l_reg_val(lua_State *L)
555{
556   uint32_t regbase = (uint32_t)lua_tonumber(L, 1);
557   lua_pushnumber(L, reg_val(regbase));
558   return 1;
559}
560
561static const struct luaL_Reg l_regs[] = {
562   {"written", l_reg_written},
563   {"lastval", l_reg_lastval},
564   {"val", l_reg_val},
565   {NULL, NULL} /* sentinel */
566};
567
568/* Expose API to lookup snapshot buffers:
569 */
570
571uint64_t gpubaseaddr(uint64_t gpuaddr);
572unsigned hostlen(uint64_t gpuaddr);
573
574/* given address, return base-address of buffer: */
575static int
576l_bo_base(lua_State *L)
577{
578   uint64_t addr = (uint64_t)lua_tonumber(L, 1);
579   lua_pushnumber(L, gpubaseaddr(addr));
580   return 1;
581}
582
583/* given address, return the remaining size of the buffer: */
584static int
585l_bo_size(lua_State *L)
586{
587   uint64_t addr = (uint64_t)lua_tonumber(L, 1);
588   lua_pushnumber(L, hostlen(addr));
589   return 1;
590}
591
592static const struct luaL_Reg l_bos[] = {
593   {"base", l_bo_base}, {"size", l_bo_size}, {NULL, NULL} /* sentinel */
594};
595
596static void
597openlib(const char *lib, const luaL_Reg *reg)
598{
599   lua_newtable(L);
600   luaL_setfuncs(L, reg, 0);
601   lua_setglobal(L, lib);
602}
603
604/* called at start to load the script: */
605int
606script_load(const char *file)
607{
608   int ret;
609
610   assert(!L);
611
612   L = luaL_newstate();
613   luaL_openlibs(L);
614   openlib("bos", l_bos);
615   openlib("regs", l_regs);
616   openlib("rnn", l_rnn);
617
618   ret = luaL_loadfile(L, file);
619   if (ret)
620      error("%s\n");
621
622   ret = lua_pcall(L, 0, LUA_MULTRET, 0);
623   if (ret)
624      error("%s\n");
625
626   return 0;
627}
628
629/* called at start of each cmdstream file: */
630void
631script_start_cmdstream(const char *name)
632{
633   if (!L)
634      return;
635
636   lua_getglobal(L, "start_cmdstream");
637
638   /* if no handler just ignore it: */
639   if (!lua_isfunction(L, -1)) {
640      lua_pop(L, 1);
641      return;
642   }
643
644   lua_pushstring(L, name);
645
646   /* do the call (1 arguments, 0 result) */
647   if (lua_pcall(L, 1, 0, 0) != 0)
648      error("error running function `f': %s\n");
649}
650
651/* called at each DRAW_INDX, calls script drawidx fxn to process
652 * the current state
653 */
654void
655script_draw(const char *primtype, uint32_t nindx)
656{
657   if (!L)
658      return;
659
660   lua_getglobal(L, "draw");
661
662   /* if no handler just ignore it: */
663   if (!lua_isfunction(L, -1)) {
664      lua_pop(L, 1);
665      return;
666   }
667
668   lua_pushstring(L, primtype);
669   lua_pushnumber(L, nindx);
670
671   /* do the call (2 arguments, 0 result) */
672   if (lua_pcall(L, 2, 0, 0) != 0)
673      error("error running function `f': %s\n");
674}
675
676static int
677l_rnn_meta_dom_index(lua_State *L)
678{
679   struct rnn *rnn = lua_touserdata(L, 1);
680   uint32_t offset = (uint32_t)lua_tonumber(L, 2);
681   struct rnndelem *elem;
682
683   /* TODO might be nicer if the arg isn't a number, to search the domain
684    * for matching bitfields.. so that the script could do something like
685    * 'pkt.WIDTH' insteadl of 'pkt[1].WIDTH', ie. not have to remember the
686    * offset of the dword containing the bitfield..
687    */
688
689   elem = rnn_regoff(rnn, offset);
690   if (!elem)
691      return 0;
692
693   return l_rnn_etype(L, rnn, elem, elem->offset);
694}
695
696/*
697 * A wrapper object for rnndomain based decoding of an array of dwords
698 * (ie. for pm4 packet decoding).  Mostly re-uses the register-value
699 * decoding for the individual dwords and bitfields.
700 */
701
702static int
703l_rnn_meta_dom_gc(lua_State *L)
704{
705   // TODO
706   // struct rnn *rnn = lua_touserdata(L, 1);
707   // rnn_deinit(rnn);
708   return 0;
709}
710
711static const struct luaL_Reg l_meta_rnn_dom[] = {
712   {"__index", l_rnn_meta_dom_index},
713   {"__gc", l_rnn_meta_dom_gc},
714   {NULL, NULL} /* sentinel */
715};
716
717/* called to general pm4 packet decoding, such as texture/sampler state
718 */
719void
720script_packet(uint32_t *dwords, uint32_t sizedwords, struct rnn *rnn,
721              struct rnndomain *dom)
722{
723   if (!L)
724      return;
725
726   lua_getglobal(L, dom->name);
727
728   /* if no handler for the packet, just ignore it: */
729   if (!lua_isfunction(L, -1)) {
730      lua_pop(L, 1);
731      return;
732   }
733
734   struct rnndec *rnndec = lua_newuserdata(L, sizeof(*rnndec));
735
736   rnndec->base = *rnn;
737   rnndec->base.dom[0] = dom;
738   rnndec->base.dom[1] = NULL;
739   rnndec->dwords = dwords;
740   rnndec->sizedwords = sizedwords;
741
742   luaL_newmetatable(L, "rnnmetadom");
743   luaL_setfuncs(L, l_meta_rnn_dom, 0);
744   lua_pop(L, 1);
745
746   luaL_setmetatable(L, "rnnmetadom");
747
748   lua_pushnumber(L, sizedwords);
749
750   if (lua_pcall(L, 2, 0, 0) != 0)
751      error("error running function `f': %s\n");
752}
753
754/* helper to call fxn that takes and returns void: */
755static void
756simple_call(const char *name)
757{
758   if (!L)
759      return;
760
761   lua_getglobal(L, name);
762
763   /* if no handler just ignore it: */
764   if (!lua_isfunction(L, -1)) {
765      lua_pop(L, 1);
766      return;
767   }
768
769   /* do the call (0 arguments, 0 result) */
770   if (lua_pcall(L, 0, 0, 0) != 0)
771      error("error running function `f': %s\n");
772}
773
774/* called at end of each cmdstream file: */
775void
776script_end_cmdstream(void)
777{
778   simple_call("end_cmdstream");
779}
780
781/* called at start of submit/issueibcmds: */
782void
783script_start_submit(void)
784{
785   simple_call("start_submit");
786}
787
788/* called at end of submit/issueibcmds: */
789void
790script_end_submit(void)
791{
792   simple_call("end_submit");
793}
794
795/* called after last cmdstream file: */
796void
797script_finish(void)
798{
799   if (!L)
800      return;
801
802   simple_call("finish");
803
804   lua_close(L);
805   L = NULL;
806}
807