1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2004 Mark R V Murray
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 *    in this position and unchanged.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD$");
32
33#include <sys/param.h>
34#include <sys/conf.h>
35#include <sys/kernel.h>
36#include <sys/ioccom.h>
37#include <sys/module.h>
38#include <sys/priv.h>
39#include <sys/proc.h>
40#include <sys/systm.h>
41
42#include <machine/iodev.h>
43
44#include <dev/io/iodev.h>
45
46static int	 ioopen(struct cdev *dev, int flags, int fmt,
47		    struct thread *td);
48static int	 ioclose(struct cdev *dev, int flags, int fmt,
49		    struct thread *td);
50static int	 ioioctl(struct cdev *dev, u_long cmd, caddr_t data,
51		    int fflag, struct thread *td);
52
53static int	 iopio_read(struct iodev_pio_req *req);
54static int	 iopio_write(struct iodev_pio_req *req);
55
56static struct cdev *iodev;
57
58static struct cdevsw io_cdevsw = {
59	.d_version =	D_VERSION,
60	.d_open =	ioopen,
61	.d_close =	ioclose,
62	.d_ioctl =	ioioctl,
63	.d_name =	"io",
64};
65
66/* ARGSUSED */
67static int
68ioopen(struct cdev *dev __unused, int flags __unused, int fmt __unused,
69    struct thread *td)
70{
71	int error;
72
73	error = priv_check(td, PRIV_IO);
74	if (error != 0)
75		return (error);
76	error = securelevel_gt(td->td_ucred, 0);
77	if (error != 0)
78		return (error);
79	error = iodev_open(td);
80
81        return (error);
82}
83
84/* ARGSUSED */
85static int
86ioclose(struct cdev *dev __unused, int flags __unused, int fmt __unused,
87    struct thread *td)
88{
89
90	return (iodev_close(td));
91}
92
93/* ARGSUSED */
94static int
95ioioctl(struct cdev *dev __unused, u_long cmd, caddr_t data,
96    int fflag __unused, struct thread *td __unused)
97{
98	struct iodev_pio_req *pio_req;
99	int error;
100
101	switch (cmd) {
102	case IODEV_PIO:
103		pio_req = (struct iodev_pio_req *)data;
104		switch (pio_req->access) {
105		case IODEV_PIO_READ:
106			error = iopio_read(pio_req);
107			break;
108		case IODEV_PIO_WRITE:
109			error = iopio_write(pio_req);
110			break;
111		default:
112			error = EINVAL;
113			break;
114		}
115		break;
116	default:
117		error = iodev_ioctl(cmd, data);
118	}
119
120	return (error);
121}
122
123static int
124iopio_read(struct iodev_pio_req *req)
125{
126
127	switch (req->width) {
128	case 1:
129		req->val = iodev_read_1(req->port);
130		break;
131	case 2:
132		if (req->port & 1) {
133			req->val = iodev_read_1(req->port);
134			req->val |= iodev_read_1(req->port + 1) << 8;
135		} else
136			req->val = iodev_read_2(req->port);
137		break;
138	case 4:
139		if (req->port & 1) {
140			req->val = iodev_read_1(req->port);
141			req->val |= iodev_read_2(req->port + 1) << 8;
142			req->val |= iodev_read_1(req->port + 3) << 24;
143		} else if (req->port & 2) {
144			req->val = iodev_read_2(req->port);
145			req->val |= iodev_read_2(req->port + 2) << 16;
146		} else
147			req->val = iodev_read_4(req->port);
148		break;
149	default:
150		return (EINVAL);
151	}
152
153	return (0);
154}
155
156static int
157iopio_write(struct iodev_pio_req *req)
158{
159
160	switch (req->width) {
161	case 1:
162		iodev_write_1(req->port, req->val);
163		break;
164	case 2:
165		if (req->port & 1) {
166			iodev_write_1(req->port, req->val);
167			iodev_write_1(req->port + 1, req->val >> 8);
168		} else
169			iodev_write_2(req->port, req->val);
170		break;
171	case 4:
172		if (req->port & 1) {
173			iodev_write_1(req->port, req->val);
174			iodev_write_2(req->port + 1, req->val >> 8);
175			iodev_write_1(req->port + 3, req->val >> 24);
176		} else if (req->port & 2) {
177			iodev_write_2(req->port, req->val);
178			iodev_write_2(req->port + 2, req->val >> 16);
179		} else
180			iodev_write_4(req->port, req->val);
181		break;
182	default:
183		return (EINVAL);
184	}
185
186	return (0);
187}
188
189/* ARGSUSED */
190static int
191io_modevent(module_t mod __unused, int type, void *data __unused)
192{
193	switch(type) {
194	case MOD_LOAD:
195		if (bootverbose)
196			printf("io: <I/O>\n");
197		iodev = make_dev(&io_cdevsw, 0,
198			UID_ROOT, GID_WHEEL, 0600, "io");
199		break;
200
201	case MOD_UNLOAD:
202		destroy_dev(iodev);
203		break;
204
205	case MOD_SHUTDOWN:
206		break;
207
208	default:
209		return(EOPNOTSUPP);
210		break;
211
212	}
213
214	return (0);
215}
216
217DEV_MODULE(io, io_modevent, NULL);
218MODULE_VERSION(io, 1);
219