1 /*        $NetBSD: main.c,v 1.8 2024/07/12 22:31:40 andvar Exp $      */
2 
3 /*-
4  * Copyright (c) 2002 TAKEMRUA Shin
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of The NetBSD Foundation nor the names of its
16  *    contributors may be used to endorse or promote products derived
17  *    from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <stdio.h>
33 #include <strings.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <err.h>
37 #include <errno.h>
38 #include <time.h>
39 #include <termios.h>
40 #include <sys/fcntl.h>
41 #include <sys/mman.h>
42 #ifdef __STDC__
43 #include <stdarg.h>
44 #else
45 #include <varargs.h>
46 #endif
47 
48 #include "tpctl.h"
49 
50 #ifndef lint
51 #include <sys/cdefs.h>
52 __RCSID("$NetBSD: main.c,v 1.8 2024/07/12 22:31:40 andvar Exp $");
53 #endif /* not lint */
54 
55 void load_data(const char *, struct tpctl_data *);
56 void save_data(const char *, struct tpctl_data *);
57 int do_calibration(const char *, struct tp *, struct wsmouse_calibcoords *);
58 void drawcross(struct fb *, int, int, int, fb_pixel_t);
59 int check_esc(void *);
60 
61 int opt_verbose;
62 int opt_noupdate;
63 int opt_forceupdate;
64 
65 static __dead void
usage(void)66 usage(void)
67 {
68 
69           fprintf(stderr, "usage: %s [-D dispdev] [-d dev] [-f file] [-hnuv]\n",
70               getprogname());
71           exit(EXIT_FAILURE);
72           /* NOTREACHED */
73 }
74 
75 int
main(int argc,char * argv[])76 main(int argc, char *argv[])
77 {
78           int tpfd, ch;
79           struct tp tp;
80           struct wsmouse_calibcoords *pref;
81           struct tpctl_data data;
82           const char *data_file;
83           const char *dev_name;
84           const char *dispdev_name;
85 
86           /* set default values */
87           opt_verbose = 0;
88           opt_noupdate = 0;
89           opt_forceupdate = 0;
90           dev_name = TPCTL_TP_DEVICE;
91           dispdev_name = TPCTL_FB_DEVICE;
92           data_file = TPCTL_DB_FILENAME;
93 
94           /* parse command line */
95           while ((ch = getopt(argc, argv, "d:D:f:hnuv")) != -1) {
96                     switch (ch) {
97                     case 'D':
98                               dispdev_name = optarg;
99                               break;
100                     case 'd':
101                               dev_name = optarg;
102                               break;
103                     case 'f':
104                               data_file = optarg;
105                               break;
106                     case 'h':
107                               usage();
108                               /* NOTREACHED */
109                     case 'n':
110                               opt_noupdate = 1;
111                               break;
112                     case 'u':
113                               opt_forceupdate = 1;
114                               break;
115                     case 'v':
116                               opt_verbose = 1;
117                               break;
118                     default:
119                               usage();
120                               /* NOTREACHED */
121                     }
122           }
123           if (argv[optind] != NULL) {
124                     usage();
125                     /* NOTREACHED */
126           }
127 
128           /* load calibration parameters from specified file */
129           load_data(data_file, &data);
130 
131           /* open touch panel device and initialize touch panel routines */
132           if ((tpfd = open(dev_name, O_RDWR)) < 0)
133                     errx(EXIT_FAILURE, "can't open touch panel");
134           if (tp_init(&tp, tpfd) < 0)
135                     errx(EXIT_FAILURE, "can't initialize touch panel");
136 
137           /* find out saved parameters for the touch panel */
138           pref = search_data(&data, tp.id);
139           if (opt_forceupdate || pref == NULL) {
140                     /* if the parameters wasn't found or '-f' options was
141                        specified, do 'calibration' */
142                     struct wsmouse_calibcoords coords;
143 
144                     /* draw cursors and collect samples */
145                     if (do_calibration(dispdev_name, &tp, &coords) < 0) {
146                               /* ESC key was pressed to abort */
147                               exit(EXIT_FAILURE);
148                     }
149                     /* update parameters with new one */
150                     replace_data(&data, tp.id, &coords);
151                     pref = search_data(&data, tp.id);
152           } else {
153                     /* nothing is updated,
154                        so you don't have to write back the data */
155                     opt_noupdate = 1;
156           }
157 
158           if (opt_verbose)
159                     write_coords(stdout, tp.id, pref);
160 
161           /* set calibration parameters into touch panel device */
162           if (tp_setcalibcoords(&tp, pref) < 0)
163                     errx(EXIT_FAILURE, "can't set samples");
164 
165           /* save calibration parameters from specified file */
166           if (!opt_noupdate)
167                     save_data(data_file, &data);
168           /* dispose data */
169           free_data(&data);
170 
171           exit(EXIT_SUCCESS);
172           /* NOTREACHED */
173 }
174 
175 /*
176  * load calibration parameters from specified file
177  *
178  * return:          none (it won't return if some error occurs)
179  */
180 void
load_data(const char * data_file,struct tpctl_data * data)181 load_data(const char *data_file, struct tpctl_data *data)
182 {
183           int error;
184 
185           init_data(data);
186           error = read_data(data_file, data);
187           switch (error) {
188           case ERR_NONE:
189                     break;
190           case ERR_NOFILE:
191                     fprintf(stderr, "%s: can't open %s\n", getprogname(),
192                         data_file);
193                     /* it might be OK... */
194                     break;
195           case ERR_IO:
196                     fprintf(stderr, "%s: I/O error on %s\n", getprogname(),
197                         data_file);
198                     exit(EXIT_FAILURE);
199                     break;
200           case ERR_SYNTAX:
201                     fprintf(stderr, "%s: format error at %s, line %d\n",
202                         getprogname(), data_file, data->lineno);
203                     exit(EXIT_FAILURE);
204                     break;
205           case ERR_DUPNAME:
206                     fprintf(stderr, "%s: duplicate entry at %s, line %d\n",
207                         getprogname(), data_file, data->lineno);
208                     exit(EXIT_FAILURE);
209                     break;
210           default:
211                     fprintf(stderr, "%s: internal error\n", getprogname());
212                     exit(EXIT_FAILURE);
213                     break;
214           }
215 }
216 
217 /*
218  * save calibration parameters to specified file
219  *
220  * return:          none (it won't return if some error occurs)
221  */
222 void
save_data(const char * data_file,struct tpctl_data * data)223 save_data(const char *data_file, struct tpctl_data *data)
224 {
225           int error;
226 
227           error = write_data(data_file, data);
228           switch (error) {
229           case ERR_NONE:
230                     break;
231           case ERR_NOFILE:
232                     fprintf(stderr, "%s: can't open %s\n", getprogname(),
233                         data_file);
234                     exit(EXIT_FAILURE);
235                     break;
236           case ERR_IO:
237                     fprintf(stderr, "%s: I/O error on %s\n", getprogname(),
238                         data_file);
239                     exit(EXIT_FAILURE);
240                     break;
241           default:
242                     fprintf(stderr, "%s: internal error\n", getprogname());
243                     exit(EXIT_FAILURE);
244                     break;
245           }
246 }
247 
248 /*
249  * draw cursors on frame buffer and collect samples in
250  * wamouse_calibcoords structure.
251  *
252  * return:          0         succeeded
253  *                  -1        aborted by user (ESC key was pressed)
254  *                  (it won't return if some error occurs)
255  */
256 int
do_calibration(const char * dev,struct tp * tp,struct wsmouse_calibcoords * coords)257 do_calibration(const char *dev, struct tp *tp,
258     struct wsmouse_calibcoords *coords)
259 {
260           int fbfd;
261           struct fb fb;
262           int i, x, y, xm, ym, cursize, error, res;
263 
264           /* open frame buffer device and initialize frame buffer routine */
265           if ((fbfd = open(dev, O_RDWR)) < 0)
266                     errx(EXIT_FAILURE, "can't open frame buffer");
267           if (fb_init(&fb, fbfd) < 0)
268                     errx(EXIT_FAILURE, "can't map frame buffer");
269 
270           memset(coords, 0, sizeof(*coords));
271           coords->minx = 0;
272           coords->miny = 0;
273           coords->maxx = fb.conf.hf_width - 1;
274           coords->maxy = fb.conf.hf_height - 1;
275           coords->samplelen = 5;
276 
277           cursize = 20;
278           xm = fb.conf.hf_width/10;
279           ym = fb.conf.hf_height/10;
280 
281           /* center */
282           coords->samples[0].x = fb.conf.hf_width/2;
283           coords->samples[0].y = fb.conf.hf_height/2;
284 
285           /* top left */
286           coords->samples[1].x = xm;
287           coords->samples[1].y = ym;
288 
289           /* bottom left */
290           coords->samples[2].x = xm;
291           coords->samples[2].y = fb.conf.hf_height - ym;
292 
293           /* bottom right */
294           coords->samples[3].x = fb.conf.hf_width - xm;
295           coords->samples[3].y = fb.conf.hf_height - ym;
296 
297           /* top right */
298           coords->samples[4].x = fb.conf.hf_width - xm;
299           coords->samples[4].y = ym;
300 
301           tp_setrawmode(tp);
302           error = 0;
303           for (i = 0; i < coords->samplelen; i++) {
304                     drawcross(&fb,
305                         coords->samples[i].x,
306                         coords->samples[i].y,
307                         cursize, fb.white);
308                     fb_flush(&fb);
309                     tp_flush(tp);
310                     res = tp_get(tp, &x, &y, check_esc, 0 /* stdin */);
311                     if (res < 0) {
312                               error = errno;
313                               break;
314                     }
315                     if (0 < res) {
316                               fb_dispmode(&fb, WSDISPLAYIO_MODE_EMUL);
317                               return (-1); /* aborted by user */
318                     }
319                     coords->samples[i].rawx = x;
320                     coords->samples[i].rawy = y;
321                     drawcross(&fb,
322                         coords->samples[i].x,
323                         coords->samples[i].y,
324                         cursize, fb.black);
325                     fb_flush(&fb);
326                     tp_waitup(tp, 200, check_esc, 0 /* stdin */);
327           }
328 
329           fb_dispmode(&fb, WSDISPLAYIO_MODE_EMUL);
330           close(fbfd);
331 
332           if (opt_verbose) {
333                     printf("%s: %dx%d (%dbytes/line) %dbit offset=0x%lx\n",
334                         getprogname(),
335                         fb.conf.hf_width,
336                         fb.conf.hf_height,
337                         fb.conf.hf_bytes_per_line,
338                         fb.conf.hf_pixel_width,
339                         fb.conf.hf_offset);
340           }
341 
342           if (error) {
343                     errno = error;
344                     errx(EXIT_FAILURE, "can't get samples");
345           }
346 
347           return (0);
348 }
349 
350 /*
351  * draw cross cursor on frame buffer
352  *
353  * return:          none
354  */
355 void
drawcross(struct fb * fb,int x,int y,int size,fb_pixel_t pixel)356 drawcross(struct fb *fb, int x, int y, int size, fb_pixel_t pixel)
357 {
358           size /= 2;
359 
360           fb_drawline(fb, x,     y - size + 1, x,     y - 1,    pixel);
361           fb_drawline(fb, x + 1, y - size + 1, x + 1, y - 1,    pixel);
362           fb_drawline(fb, x,     y + 2,        x,     y + size, pixel);
363           fb_drawline(fb, x + 1, y + 2,        x + 1, y + size, pixel);
364 
365           fb_drawline(fb, x - size + 1, y,     x - 1,    y,     pixel);
366           fb_drawline(fb, x - size + 1, y + 1, x - 1,    y + 1, pixel);
367           fb_drawline(fb, x + 2,        y,     x + size, y,     pixel);
368           fb_drawline(fb, x + 2,        y + 1, x + size, y + 1, pixel);
369 }
370 
371 /*
372  * check ESC key
373  *
374  * date:  input file descriptor
375  *
376  * return:          0         nothing has occurred
377  *                  1         ESC key was pressed
378  *                  -1        error
379  */
380 int
check_esc(void * data)381 check_esc(void *data)
382 {
383           int fd = (int)(intptr_t)data;
384           int flg, n, error;
385           char buf[1];
386           struct termios tm, raw;
387 
388           if (tcgetattr(fd, &tm) < 0)
389                     return (-1);
390           raw = tm;
391           cfmakeraw(&raw);
392           if (tcsetattr(fd, TCSANOW, &raw) < 0)
393                     return (-1);
394           if ((flg = fcntl(fd, F_GETFL)) == -1)
395                     return (-1);
396           if (fcntl(fd, F_SETFL, flg | O_NONBLOCK) == -1)
397                     return (-1);
398           n = read(fd, buf, 1);
399           error = errno;
400           fcntl(fd, F_SETFL, flg);
401           tcsetattr(fd, TCSANOW, &tm);
402           if (n < 0)
403                     return (error == EWOULDBLOCK ? 0 : -1);
404           if (n == 0)
405                     return (0); /* EOF */
406           if (*buf == 0x1b)
407                     return (1); /* ESC */
408 
409           return (0);
410 }
411