xref: /openbmc/qemu/chardev/char-parallel.c (revision 7f709ce7)
1 /*
2  * QEMU System Emulator
3  *
4  * Copyright (c) 2003-2008 Fabrice Bellard
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 #include "qemu/osdep.h"
25 #include "chardev/char.h"
26 #include "qapi/error.h"
27 #include <sys/ioctl.h>
28 
29 #ifdef CONFIG_BSD
30 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
31 #include <dev/ppbus/ppi.h>
32 #include <dev/ppbus/ppbconf.h>
33 #elif defined(__DragonFly__)
34 #include <dev/misc/ppi/ppi.h>
35 #include <bus/ppbus/ppbconf.h>
36 #endif
37 #else
38 #ifdef __linux__
39 #include <linux/ppdev.h>
40 #include <linux/parport.h>
41 #endif
42 #endif
43 
44 #include "chardev/char-fd.h"
45 #include "chardev/char-parallel.h"
46 
47 #if defined(__linux__)
48 
49 typedef struct {
50     Chardev parent;
51     int fd;
52     int mode;
53 } ParallelChardev;
54 
55 #define PARALLEL_CHARDEV(obj) \
56     OBJECT_CHECK(ParallelChardev, (obj), TYPE_CHARDEV_PARALLEL)
57 
58 static int pp_hw_mode(ParallelChardev *s, uint16_t mode)
59 {
60     if (s->mode != mode) {
61         int m = mode;
62         if (ioctl(s->fd, PPSETMODE, &m) < 0) {
63             return 0;
64         }
65         s->mode = mode;
66     }
67     return 1;
68 }
69 
70 static int pp_ioctl(Chardev *chr, int cmd, void *arg)
71 {
72     ParallelChardev *drv = PARALLEL_CHARDEV(chr);
73     int fd = drv->fd;
74     uint8_t b;
75 
76     switch (cmd) {
77     case CHR_IOCTL_PP_READ_DATA:
78         if (ioctl(fd, PPRDATA, &b) < 0) {
79             return -ENOTSUP;
80         }
81         *(uint8_t *)arg = b;
82         break;
83     case CHR_IOCTL_PP_WRITE_DATA:
84         b = *(uint8_t *)arg;
85         if (ioctl(fd, PPWDATA, &b) < 0) {
86             return -ENOTSUP;
87         }
88         break;
89     case CHR_IOCTL_PP_READ_CONTROL:
90         if (ioctl(fd, PPRCONTROL, &b) < 0) {
91             return -ENOTSUP;
92         }
93         /* Linux gives only the lowest bits, and no way to know data
94            direction! For better compatibility set the fixed upper
95            bits. */
96         *(uint8_t *)arg = b | 0xc0;
97         break;
98     case CHR_IOCTL_PP_WRITE_CONTROL:
99         b = *(uint8_t *)arg;
100         if (ioctl(fd, PPWCONTROL, &b) < 0) {
101             return -ENOTSUP;
102         }
103         break;
104     case CHR_IOCTL_PP_READ_STATUS:
105         if (ioctl(fd, PPRSTATUS, &b) < 0) {
106             return -ENOTSUP;
107         }
108         *(uint8_t *)arg = b;
109         break;
110     case CHR_IOCTL_PP_DATA_DIR:
111         if (ioctl(fd, PPDATADIR, (int *)arg) < 0) {
112             return -ENOTSUP;
113         }
114         break;
115     case CHR_IOCTL_PP_EPP_READ_ADDR:
116         if (pp_hw_mode(drv, IEEE1284_MODE_EPP | IEEE1284_ADDR)) {
117             struct ParallelIOArg *parg = arg;
118             int n = read(fd, parg->buffer, parg->count);
119             if (n != parg->count) {
120                 return -EIO;
121             }
122         }
123         break;
124     case CHR_IOCTL_PP_EPP_READ:
125         if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
126             struct ParallelIOArg *parg = arg;
127             int n = read(fd, parg->buffer, parg->count);
128             if (n != parg->count) {
129                 return -EIO;
130             }
131         }
132         break;
133     case CHR_IOCTL_PP_EPP_WRITE_ADDR:
134         if (pp_hw_mode(drv, IEEE1284_MODE_EPP | IEEE1284_ADDR)) {
135             struct ParallelIOArg *parg = arg;
136             int n = write(fd, parg->buffer, parg->count);
137             if (n != parg->count) {
138                 return -EIO;
139             }
140         }
141         break;
142     case CHR_IOCTL_PP_EPP_WRITE:
143         if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
144             struct ParallelIOArg *parg = arg;
145             int n = write(fd, parg->buffer, parg->count);
146             if (n != parg->count) {
147                 return -EIO;
148             }
149         }
150         break;
151     default:
152         return -ENOTSUP;
153     }
154     return 0;
155 }
156 
157 static void qemu_chr_open_pp_fd(Chardev *chr,
158                                 int fd,
159                                 bool *be_opened,
160                                 Error **errp)
161 {
162     ParallelChardev *drv = PARALLEL_CHARDEV(chr);
163 
164     if (ioctl(fd, PPCLAIM) < 0) {
165         error_setg_errno(errp, errno, "not a parallel port");
166         close(fd);
167         return;
168     }
169 
170     drv->fd = fd;
171     drv->mode = IEEE1284_MODE_COMPAT;
172 }
173 #endif /* __linux__ */
174 
175 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
176 
177 typedef struct {
178     Chardev parent;
179     int fd;
180 } ParallelChardev;
181 
182 #define PARALLEL_CHARDEV(obj)                                   \
183     OBJECT_CHECK(ParallelChardev, (obj), TYPE_CHARDEV_PARALLEL)
184 
185 static int pp_ioctl(Chardev *chr, int cmd, void *arg)
186 {
187     ParallelChardev *drv = PARALLEL_CHARDEV(chr);
188     uint8_t b;
189 
190     switch (cmd) {
191     case CHR_IOCTL_PP_READ_DATA:
192         if (ioctl(drv->fd, PPIGDATA, &b) < 0) {
193             return -ENOTSUP;
194         }
195         *(uint8_t *)arg = b;
196         break;
197     case CHR_IOCTL_PP_WRITE_DATA:
198         b = *(uint8_t *)arg;
199         if (ioctl(drv->fd, PPISDATA, &b) < 0) {
200             return -ENOTSUP;
201         }
202         break;
203     case CHR_IOCTL_PP_READ_CONTROL:
204         if (ioctl(drv->fd, PPIGCTRL, &b) < 0) {
205             return -ENOTSUP;
206         }
207         *(uint8_t *)arg = b;
208         break;
209     case CHR_IOCTL_PP_WRITE_CONTROL:
210         b = *(uint8_t *)arg;
211         if (ioctl(drv->fd, PPISCTRL, &b) < 0) {
212             return -ENOTSUP;
213         }
214         break;
215     case CHR_IOCTL_PP_READ_STATUS:
216         if (ioctl(drv->fd, PPIGSTATUS, &b) < 0) {
217             return -ENOTSUP;
218         }
219         *(uint8_t *)arg = b;
220         break;
221     default:
222         return -ENOTSUP;
223     }
224     return 0;
225 }
226 
227 static void qemu_chr_open_pp_fd(Chardev *chr,
228                                 int fd,
229                                 bool *be_opened,
230                                 Error **errp)
231 {
232     ParallelChardev *drv = PARALLEL_CHARDEV(chr);
233     drv->fd = fd;
234     *be_opened = false;
235 }
236 #endif
237 
238 #ifdef HAVE_CHARDEV_PARPORT
239 static void qmp_chardev_open_parallel(Chardev *chr,
240                                       ChardevBackend *backend,
241                                       bool *be_opened,
242                                       Error **errp)
243 {
244     ChardevHostdev *parallel = backend->u.parallel.data;
245     int fd;
246 
247     fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp);
248     if (fd < 0) {
249         return;
250     }
251     qemu_chr_open_pp_fd(chr, fd, be_opened, errp);
252 }
253 
254 static void qemu_chr_parse_parallel(QemuOpts *opts, ChardevBackend *backend,
255                                     Error **errp)
256 {
257     const char *device = qemu_opt_get(opts, "path");
258     ChardevHostdev *parallel;
259 
260     if (device == NULL) {
261         error_setg(errp, "chardev: parallel: no device path given");
262         return;
263     }
264     backend->type = CHARDEV_BACKEND_KIND_PARALLEL;
265     parallel = backend->u.parallel.data = g_new0(ChardevHostdev, 1);
266     qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(parallel));
267     parallel->device = g_strdup(device);
268 }
269 
270 static void char_parallel_class_init(ObjectClass *oc, void *data)
271 {
272     ChardevClass *cc = CHARDEV_CLASS(oc);
273 
274     cc->parse = qemu_chr_parse_parallel;
275     cc->open = qmp_chardev_open_parallel;
276 #if defined(__linux__)
277     cc->chr_ioctl = pp_ioctl;
278 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
279     defined(__DragonFly__)
280     cc->chr_ioctl = pp_ioctl;
281 #endif
282 }
283 
284 static void char_parallel_finalize(Object *obj)
285 {
286 #if defined(__linux__)
287     Chardev *chr = CHARDEV(obj);
288     ParallelChardev *drv = PARALLEL_CHARDEV(chr);
289     int fd = drv->fd;
290 
291     pp_hw_mode(drv, IEEE1284_MODE_COMPAT);
292     ioctl(fd, PPRELEASE);
293     close(fd);
294     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
295 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
296     defined(__DragonFly__)
297     /* FIXME: close fd? */
298 #endif
299 }
300 
301 static const TypeInfo char_parallel_type_info = {
302     .name = TYPE_CHARDEV_PARALLEL,
303     .parent = TYPE_CHARDEV,
304     .instance_size = sizeof(ParallelChardev),
305     .instance_finalize = char_parallel_finalize,
306     .class_init = char_parallel_class_init,
307 };
308 
309 static void register_types(void)
310 {
311     type_register_static(&char_parallel_type_info);
312 }
313 
314 type_init(register_types);
315 
316 #endif
317