xref: /openbmc/qemu/chardev/char-parallel.c (revision e7bbc9b1)
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 
25 #include "qemu/osdep.h"
26 #include "chardev/char.h"
27 #include "qapi/error.h"
28 #include "qemu/option.h"
29 #include <sys/ioctl.h>
30 
31 #ifdef CONFIG_BSD
32 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
33 #include <dev/ppbus/ppi.h>
34 #include <dev/ppbus/ppbconf.h>
35 #elif defined(__DragonFly__)
36 #include <dev/misc/ppi/ppi.h>
37 #include <bus/ppbus/ppbconf.h>
38 #endif
39 #else
40 #ifdef __linux__
41 #include <linux/ppdev.h>
42 #include <linux/parport.h>
43 #endif
44 #endif
45 
46 #include "chardev/char-fd.h"
47 #include "chardev/char-parallel.h"
48 
49 #if defined(__linux__)
50 
51 typedef struct {
52     Chardev parent;
53     int fd;
54     int mode;
55 } ParallelChardev;
56 
57 #define PARALLEL_CHARDEV(obj) \
58     OBJECT_CHECK(ParallelChardev, (obj), TYPE_CHARDEV_PARALLEL)
59 
60 static int pp_hw_mode(ParallelChardev *s, uint16_t mode)
61 {
62     if (s->mode != mode) {
63         int m = mode;
64         if (ioctl(s->fd, PPSETMODE, &m) < 0) {
65             return 0;
66         }
67         s->mode = mode;
68     }
69     return 1;
70 }
71 
72 static int pp_ioctl(Chardev *chr, int cmd, void *arg)
73 {
74     ParallelChardev *drv = PARALLEL_CHARDEV(chr);
75     int fd = drv->fd;
76     uint8_t b;
77 
78     switch (cmd) {
79     case CHR_IOCTL_PP_READ_DATA:
80         if (ioctl(fd, PPRDATA, &b) < 0) {
81             return -ENOTSUP;
82         }
83         *(uint8_t *)arg = b;
84         break;
85     case CHR_IOCTL_PP_WRITE_DATA:
86         b = *(uint8_t *)arg;
87         if (ioctl(fd, PPWDATA, &b) < 0) {
88             return -ENOTSUP;
89         }
90         break;
91     case CHR_IOCTL_PP_READ_CONTROL:
92         if (ioctl(fd, PPRCONTROL, &b) < 0) {
93             return -ENOTSUP;
94         }
95         /* Linux gives only the lowest bits, and no way to know data
96            direction! For better compatibility set the fixed upper
97            bits. */
98         *(uint8_t *)arg = b | 0xc0;
99         break;
100     case CHR_IOCTL_PP_WRITE_CONTROL:
101         b = *(uint8_t *)arg;
102         if (ioctl(fd, PPWCONTROL, &b) < 0) {
103             return -ENOTSUP;
104         }
105         break;
106     case CHR_IOCTL_PP_READ_STATUS:
107         if (ioctl(fd, PPRSTATUS, &b) < 0) {
108             return -ENOTSUP;
109         }
110         *(uint8_t *)arg = b;
111         break;
112     case CHR_IOCTL_PP_DATA_DIR:
113         if (ioctl(fd, PPDATADIR, (int *)arg) < 0) {
114             return -ENOTSUP;
115         }
116         break;
117     case CHR_IOCTL_PP_EPP_READ_ADDR:
118         if (pp_hw_mode(drv, IEEE1284_MODE_EPP | IEEE1284_ADDR)) {
119             struct ParallelIOArg *parg = arg;
120             int n = read(fd, parg->buffer, parg->count);
121             if (n != parg->count) {
122                 return -EIO;
123             }
124         }
125         break;
126     case CHR_IOCTL_PP_EPP_READ:
127         if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
128             struct ParallelIOArg *parg = arg;
129             int n = read(fd, parg->buffer, parg->count);
130             if (n != parg->count) {
131                 return -EIO;
132             }
133         }
134         break;
135     case CHR_IOCTL_PP_EPP_WRITE_ADDR:
136         if (pp_hw_mode(drv, IEEE1284_MODE_EPP | IEEE1284_ADDR)) {
137             struct ParallelIOArg *parg = arg;
138             int n = write(fd, parg->buffer, parg->count);
139             if (n != parg->count) {
140                 return -EIO;
141             }
142         }
143         break;
144     case CHR_IOCTL_PP_EPP_WRITE:
145         if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
146             struct ParallelIOArg *parg = arg;
147             int n = write(fd, parg->buffer, parg->count);
148             if (n != parg->count) {
149                 return -EIO;
150             }
151         }
152         break;
153     default:
154         return -ENOTSUP;
155     }
156     return 0;
157 }
158 
159 static void qemu_chr_open_pp_fd(Chardev *chr,
160                                 int fd,
161                                 bool *be_opened,
162                                 Error **errp)
163 {
164     ParallelChardev *drv = PARALLEL_CHARDEV(chr);
165 
166     if (ioctl(fd, PPCLAIM) < 0) {
167         error_setg_errno(errp, errno, "not a parallel port");
168         close(fd);
169         return;
170     }
171 
172     drv->fd = fd;
173     drv->mode = IEEE1284_MODE_COMPAT;
174 }
175 #endif /* __linux__ */
176 
177 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
178 
179 typedef struct {
180     Chardev parent;
181     int fd;
182 } ParallelChardev;
183 
184 #define PARALLEL_CHARDEV(obj)                                   \
185     OBJECT_CHECK(ParallelChardev, (obj), TYPE_CHARDEV_PARALLEL)
186 
187 static int pp_ioctl(Chardev *chr, int cmd, void *arg)
188 {
189     ParallelChardev *drv = PARALLEL_CHARDEV(chr);
190     uint8_t b;
191 
192     switch (cmd) {
193     case CHR_IOCTL_PP_READ_DATA:
194         if (ioctl(drv->fd, PPIGDATA, &b) < 0) {
195             return -ENOTSUP;
196         }
197         *(uint8_t *)arg = b;
198         break;
199     case CHR_IOCTL_PP_WRITE_DATA:
200         b = *(uint8_t *)arg;
201         if (ioctl(drv->fd, PPISDATA, &b) < 0) {
202             return -ENOTSUP;
203         }
204         break;
205     case CHR_IOCTL_PP_READ_CONTROL:
206         if (ioctl(drv->fd, PPIGCTRL, &b) < 0) {
207             return -ENOTSUP;
208         }
209         *(uint8_t *)arg = b;
210         break;
211     case CHR_IOCTL_PP_WRITE_CONTROL:
212         b = *(uint8_t *)arg;
213         if (ioctl(drv->fd, PPISCTRL, &b) < 0) {
214             return -ENOTSUP;
215         }
216         break;
217     case CHR_IOCTL_PP_READ_STATUS:
218         if (ioctl(drv->fd, PPIGSTATUS, &b) < 0) {
219             return -ENOTSUP;
220         }
221         *(uint8_t *)arg = b;
222         break;
223     default:
224         return -ENOTSUP;
225     }
226     return 0;
227 }
228 
229 static void qemu_chr_open_pp_fd(Chardev *chr,
230                                 int fd,
231                                 bool *be_opened,
232                                 Error **errp)
233 {
234     ParallelChardev *drv = PARALLEL_CHARDEV(chr);
235     drv->fd = fd;
236     *be_opened = false;
237 }
238 #endif
239 
240 #ifdef HAVE_CHARDEV_PARPORT
241 static void qmp_chardev_open_parallel(Chardev *chr,
242                                       ChardevBackend *backend,
243                                       bool *be_opened,
244                                       Error **errp)
245 {
246     ChardevHostdev *parallel = backend->u.parallel.data;
247     int fd;
248 
249     fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp);
250     if (fd < 0) {
251         return;
252     }
253     qemu_chr_open_pp_fd(chr, fd, be_opened, errp);
254 }
255 
256 static void qemu_chr_parse_parallel(QemuOpts *opts, ChardevBackend *backend,
257                                     Error **errp)
258 {
259     const char *device = qemu_opt_get(opts, "path");
260     ChardevHostdev *parallel;
261 
262     if (device == NULL) {
263         error_setg(errp, "chardev: parallel: no device path given");
264         return;
265     }
266     backend->type = CHARDEV_BACKEND_KIND_PARALLEL;
267     parallel = backend->u.parallel.data = g_new0(ChardevHostdev, 1);
268     qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(parallel));
269     parallel->device = g_strdup(device);
270 }
271 
272 static void char_parallel_class_init(ObjectClass *oc, void *data)
273 {
274     ChardevClass *cc = CHARDEV_CLASS(oc);
275 
276     cc->parse = qemu_chr_parse_parallel;
277     cc->open = qmp_chardev_open_parallel;
278 #if defined(__linux__)
279     cc->chr_ioctl = pp_ioctl;
280 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
281     defined(__DragonFly__)
282     cc->chr_ioctl = pp_ioctl;
283 #endif
284 }
285 
286 static void char_parallel_finalize(Object *obj)
287 {
288 #if defined(__linux__)
289     Chardev *chr = CHARDEV(obj);
290     ParallelChardev *drv = PARALLEL_CHARDEV(chr);
291     int fd = drv->fd;
292 
293     pp_hw_mode(drv, IEEE1284_MODE_COMPAT);
294     ioctl(fd, PPRELEASE);
295     close(fd);
296     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
297 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
298     defined(__DragonFly__)
299     /* FIXME: close fd? */
300 #endif
301 }
302 
303 static const TypeInfo char_parallel_type_info = {
304     .name = TYPE_CHARDEV_PARALLEL,
305     .parent = TYPE_CHARDEV,
306     .instance_size = sizeof(ParallelChardev),
307     .instance_finalize = char_parallel_finalize,
308     .class_init = char_parallel_class_init,
309 };
310 
311 static void register_types(void)
312 {
313     type_register_static(&char_parallel_type_info);
314 }
315 
316 type_init(register_types);
317 
318 #endif
319