chiark / gitweb /
hostside: more length for bavarian
[trains.git] / hostside / hidrawconv.c
1 /*
2  * usage:
3  *   .../hidrawconv-<controller> -d
4  *   .../hidrawconv-<controller> -e </dev/hidrawN
5  * where -a means all, and the other letters are:
6  *   -d   print expected descriptor (as for hidraw-ioctl -d)
7  *   -e   pretend to be evdev-manip
8  *   -E   pretend to be evdev-manip, print to stderr about the device at start
9  * exit status:
10  *   0      all ok
11  *   other  some other problem
12  *
13  * joystick values are always doubles from -1 to 0 to +1
14  */
15
16 #include "hidrawconv.h"
17
18 void die_vprintf_hook(const char *fmt, va_list al) { }
19 void die_hook(void) { }
20
21 static LastReports lasts;
22
23
24 /*---------- helpful macros for reporter functions ----------*/
25
26 /*
27  * These expect/define:
28  *
29  *   const uint8_t msg[], last[];
30  *   int len;
31  *   const EntType *ent;
32  *   uint8_t mb, lb;  // bits from the message, masked but unshifted
33  *
34  *   typedef struct {
35  *     const char *str;
36  *     uint8_t mask;
37  *     int pos;
38  *   } EntType;
39  */
40
41 #define FOR_REPORTENTRIES(ent)                  \
42   /* Like for(;;) */                            \
43   for (; (ent)->str; (ent)++)                   \
44     if ((ent)->pos >= len) continue;            \
45     else
46
47 #define MSG_BITS(ent)  (msg[(ent)->pos] & (ent)->mask)
48 #define LAST_BITS(ent) (last[(ent)->pos] & (ent)->mask)
49   /* uint8_t MSG_BITS(EntType *ent);
50    * uint8_t LAST_BITS(EntType *ent);
51    */
52
53 #define DEFINE_STANDARD_REPORTER(EntType, ent, BODY_BLOCK)              \
54   void report##ent##s(const uint8_t msg[], const uint8_t last[],        \
55                       int len, const EntType *ent) {                    \
56     FOR_REPORTENTRIES(ent) {                                            \
57       uint8_t mb= MSG_BITS(ent);                                        \
58       uint8_t lb= LAST_BITS(ent);                                       \
59       if (mb==lb) continue;                                             \
60       BODY_BLOCK                                                        \
61     }                                                                   \
62   }
63
64
65 /*---------- reporter functions ----------*/
66
67 DEFINE_STANDARD_REPORTER(KeyBit, bit, {
68     printf("%s %d\n", bit->str, !!mb);
69   })
70
71 DEFINE_STANDARD_REPORTER(ValLoc, val, {
72     mb >>= val->rshift;
73     mb -= val->zero;
74     double v= (int8_t)mb;
75     v /= (v >= 0 ? 127 : 128);
76     printf("%s %.5f\n", val->str, val->sign * v);
77   })
78
79 DEFINE_STANDARD_REPORTER(HatLoc, hat, {
80     /* nyi */
81   })
82
83
84 /*---------- core functions ----------*/
85
86 void dispatch(LastReports *lasts, const char *message_prefix,
87               ProcessReport *const report_processors[MAXREPORTS],
88               const uint8_t *msg, int l) {
89   if (!l) {
90     fprintf(stderr,"%s:%s report too short\n", progname, message_prefix);
91     return;
92   }
93
94   ProcessReport *pr= report_processors[msg[0]];
95   Last *last= &lasts->lasts[msg[0]];
96   if (!pr) {
97     if (!last->len)
98       fprintf(stderr,"%s:%s unexpected report 0x%02x\n",
99               progname, message_prefix, msg[0]);
100     last->len= l;
101     return;
102   }
103   if (last->len < l) {
104     last->msg= mrealloc(last->msg, l);
105     memset(last->msg + last->len, 0, l - last->len);
106     last->len= l;
107   }
108   pr(msg, l, last->msg);
109   memcpy(last->msg, msg, l);
110 }  
111
112 /*---------- main program ----------*/
113
114 static void events(int verbose) {
115   uint8_t msg[MAXREPORTLEN+1];
116   char phys[PATH_MAX], name[PATH_MAX];
117   int rphys, errnophys=0, rname, errnoname=0;
118   int reportnumbug;
119
120   rphys= ioctl(0, HIDIOCGRAWPHYS(PATH_MAX), phys);  errnophys=errno;
121   rname= ioctl(0, HIDIOCGRAWNAME(PATH_MAX), name);  errnoname=errno;
122   if (rphys>=0 && rname>=0) {
123     reportnumbug= 0;
124     if (verbose)
125       fprintf(stderr,"%s: %.*s %.*s\n",progname,rphys,phys,rname,name);
126   } else if (rphys<0 && errnophys==EINVAL &&
127              rname<0 && errnoname==EINVAL) {
128     fprintf(stderr,"%s: warning, HIDIOCGRAWPHYS/NAME gave EINVAL,"
129             " assuming kernel eats report number, assuming reports are 00\n",
130             progname);
131     reportnumbug= 1;
132   } else {
133     die("HIDIOCGRAWPHYS %s / HIDIOCGRAWNAME %s",
134         rphys<0 ? strerror(errnophys) : "ok",
135         rname<0 ? strerror(errnoname) : "ok");
136   }
137
138   if (reportnumbug) msg[0]=0;
139
140   for (;;) {
141     int l= read(0, msg+reportnumbug, sizeof(msg)-reportnumbug);
142     if (!l) break;
143     if (l<0) { perror("hidrawconv: read"); exit(-1); }
144     l += reportnumbug;
145     dispatch(&lasts,"",report_processors, msg,l);
146     if (ferror(stdout) || fflush(stdout))
147       diee("failed flushing event to stdout");
148   }
149 }
150
151 int main(int argc, char **argv) {
152   const char *how;
153
154   if (!*argv || !(how=*++argv) || *how++!='-' || !*how || how[1] || *++argv)
155     badusage("need exactly one argument, -d, -e or -E");
156
157   switch (how[0]) {
158   case 'd':
159     puts(descriptor);
160     break;
161
162   case 'E':
163     events(1);
164     break;
165
166   case 'e':
167     events(0);
168     break;
169
170   default:
171     badusage("unknown option/mode");
172   }
173
174   if (ferror(stdout) || fflush(stdout))
175     diee("write/flush stdout");
176
177   return 0;
178 }