source: mainline/uspace/drv/char/pl050/pl050.c@ 984a9ba

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 984a9ba was 984a9ba, checked in by Martin Decky <martin@…>, 7 years ago

do not expose the call capability handler from the async framework

Keep the call capability handler encapsulated within the async framework
and do not expose it explicitly via its API. Use the pointer to
ipc_call_t as the sole object identifying an IPC call in the code that
uses the async framework.

This plugs a major leak in the abstraction and also simplifies both the
async framework (slightly) and all IPC servers.

  • Property mode set to 100644
File size: 9.0 KB
Line 
1/*
2 * Copyright (c) 2009 Vineeth Pillai
3 * Copyright (c) 2014 Jiri Svoboda
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - 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 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @file
31 */
32
33#include <assert.h>
34#include <bitops.h>
35#include <stdio.h>
36#include <errno.h>
37#include <str_error.h>
38#include <ddf/driver.h>
39#include <ddf/interrupt.h>
40#include <ddf/log.h>
41#include <device/hw_res.h>
42#include <device/hw_res_parsed.h>
43#include <io/chardev_srv.h>
44
45#include "pl050_hw.h"
46
47#define NAME "pl050"
48
49enum {
50 buffer_size = 64
51};
52
53static errno_t pl050_dev_add(ddf_dev_t *);
54static errno_t pl050_fun_online(ddf_fun_t *);
55static errno_t pl050_fun_offline(ddf_fun_t *);
56static void pl050_char_conn(ipc_call_t *, void *);
57static errno_t pl050_read(chardev_srv_t *, void *, size_t, size_t *);
58static errno_t pl050_write(chardev_srv_t *, const void *, size_t, size_t *);
59
60static driver_ops_t driver_ops = {
61 .dev_add = &pl050_dev_add,
62 .fun_online = &pl050_fun_online,
63 .fun_offline = &pl050_fun_offline
64};
65
66static driver_t pl050_driver = {
67 .name = NAME,
68 .driver_ops = &driver_ops
69};
70
71static chardev_ops_t pl050_chardev_ops = {
72 .read = pl050_read,
73 .write = pl050_write
74};
75
76typedef struct {
77 async_sess_t *parent_sess;
78 ddf_dev_t *dev;
79 char *name;
80
81 ddf_fun_t *fun_a;
82 chardev_srvs_t cds;
83
84 uintptr_t iobase;
85 size_t iosize;
86 kmi_regs_t *regs;
87 uint8_t buffer[buffer_size];
88 size_t buf_rp;
89 size_t buf_wp;
90 fibril_condvar_t buf_cv;
91 fibril_mutex_t buf_lock;
92} pl050_t;
93
94static irq_pio_range_t pl050_ranges[] = {
95 {
96 .base = 0,
97 .size = 9,
98 }
99};
100
101static irq_cmd_t pl050_cmds[] = {
102 {
103 .cmd = CMD_PIO_READ_8,
104 .addr = NULL,
105 .dstarg = 1
106 },
107 {
108 .cmd = CMD_AND,
109 .value = BIT_V(uint8_t, kmi_stat_rxfull),
110 .srcarg = 1,
111 .dstarg = 3
112 },
113 {
114 .cmd = CMD_PREDICATE,
115 .value = 2,
116 .srcarg = 3
117 },
118 {
119 .cmd = CMD_PIO_READ_8,
120 .addr = NULL, /* Will be patched in run-time */
121 .dstarg = 2
122 },
123 {
124 .cmd = CMD_ACCEPT
125 }
126};
127
128static irq_code_t pl050_irq_code = {
129 sizeof(pl050_ranges) / sizeof(irq_pio_range_t),
130 pl050_ranges,
131 sizeof(pl050_cmds) / sizeof(irq_cmd_t),
132 pl050_cmds
133};
134
135static pl050_t *pl050_from_fun(ddf_fun_t *fun)
136{
137 return (pl050_t *)ddf_dev_data_get(ddf_fun_get_dev(fun));
138}
139
140static void pl050_interrupt(ipc_call_t *call, ddf_dev_t *dev)
141{
142 pl050_t *pl050 = (pl050_t *)ddf_dev_data_get(dev);
143 size_t nidx;
144
145 fibril_mutex_lock(&pl050->buf_lock);
146 nidx = (pl050->buf_wp + 1) % buffer_size;
147 if (nidx == pl050->buf_rp) {
148 /** Buffer overrunt */
149 ddf_msg(LVL_WARN, "Buffer overrun.");
150 fibril_mutex_unlock(&pl050->buf_lock);
151 return;
152 }
153
154 pl050->buffer[pl050->buf_wp] = IPC_GET_ARG2(*call);
155 pl050->buf_wp = nidx;
156 fibril_condvar_broadcast(&pl050->buf_cv);
157 fibril_mutex_unlock(&pl050->buf_lock);
158}
159
160static errno_t pl050_init(pl050_t *pl050)
161{
162 hw_res_list_parsed_t res;
163 void *regs;
164 errno_t rc;
165
166 fibril_mutex_initialize(&pl050->buf_lock);
167 fibril_condvar_initialize(&pl050->buf_cv);
168 pl050->buf_rp = pl050->buf_wp = 0;
169
170 pl050->parent_sess = ddf_dev_parent_sess_get(pl050->dev);
171 if (pl050->parent_sess == NULL) {
172 ddf_msg(LVL_ERROR, "Failed connecitng parent driver.");
173 rc = ENOMEM;
174 goto error;
175 }
176
177 hw_res_list_parsed_init(&res);
178 rc = hw_res_get_list_parsed(pl050->parent_sess, &res, 0);
179 if (rc != EOK) {
180 ddf_msg(LVL_ERROR, "Failed getting resource list.");
181 goto error;
182 }
183
184 if (res.mem_ranges.count != 1) {
185 ddf_msg(LVL_ERROR, "Expected exactly one memory range.");
186 rc = EINVAL;
187 goto error;
188 }
189
190 pl050->iobase = RNGABS(res.mem_ranges.ranges[0]);
191 pl050->iosize = RNGSZ(res.mem_ranges.ranges[0]);
192
193 pl050_irq_code.ranges[0].base = pl050->iobase;
194 kmi_regs_t *regsphys = (kmi_regs_t *) pl050->iobase;
195 pl050_irq_code.cmds[0].addr = &regsphys->stat;
196 pl050_irq_code.cmds[3].addr = &regsphys->data;
197
198 if (res.irqs.count != 1) {
199 ddf_msg(LVL_ERROR, "Expected exactly one IRQ.");
200 rc = EINVAL;
201 goto error;
202 }
203
204 ddf_msg(LVL_DEBUG, "iobase=%p irq=%d", (void *)pl050->iobase,
205 res.irqs.irqs[0]);
206
207 rc = pio_enable((void *)pl050->iobase, sizeof(kmi_regs_t), &regs);
208 if (rc != EOK) {
209 ddf_msg(LVL_ERROR, "Error enabling PIO");
210 goto error;
211 }
212
213 pl050->regs = regs;
214
215 cap_irq_handle_t ihandle;
216 rc = register_interrupt_handler(pl050->dev,
217 res.irqs.irqs[0], pl050_interrupt, &pl050_irq_code, &ihandle);
218 if (rc != EOK) {
219 ddf_msg(LVL_ERROR, "Failed registering interrupt handler. (%s)",
220 str_error_name(rc));
221 goto error;
222 }
223
224 rc = hw_res_enable_interrupt(pl050->parent_sess, res.irqs.irqs[0]);
225 if (rc != EOK) {
226 ddf_msg(LVL_ERROR, "Failed enabling interrupt: %s", str_error(rc));
227 goto error;
228 }
229
230 pio_write_8(&pl050->regs->cr,
231 BIT_V(uint8_t, kmi_cr_enable) |
232 BIT_V(uint8_t, kmi_cr_rxintr));
233
234 return EOK;
235error:
236 return rc;
237}
238
239static errno_t pl050_read(chardev_srv_t *srv, void *buffer, size_t size,
240 size_t *nread)
241{
242 pl050_t *pl050 = (pl050_t *)srv->srvs->sarg;
243 uint8_t *bp = buffer;
244 size_t left;
245
246 fibril_mutex_lock(&pl050->buf_lock);
247
248 left = size;
249 while (left > 0) {
250 while (left == size && pl050->buf_rp == pl050->buf_wp)
251 fibril_condvar_wait(&pl050->buf_cv, &pl050->buf_lock);
252 if (pl050->buf_rp == pl050->buf_wp)
253 break;
254 *bp++ = pl050->buffer[pl050->buf_rp];
255 --left;
256 pl050->buf_rp = (pl050->buf_rp + 1) % buffer_size;
257 }
258
259 fibril_mutex_unlock(&pl050->buf_lock);
260
261 *nread = size - left;
262 return EOK;
263}
264
265static errno_t pl050_write(chardev_srv_t *srv, const void *data, size_t size,
266 size_t *nwritten)
267{
268 pl050_t *pl050 = (pl050_t *)srv->srvs->sarg;
269 uint8_t *dp = (uint8_t *)data;
270 uint8_t status;
271 size_t i;
272
273 ddf_msg(LVL_NOTE, "%s/pl050_write(%zu bytes)", pl050->name, size);
274 for (i = 0; i < size; i++) {
275 while (true) {
276 status = pio_read_8(&pl050->regs->stat);
277 if ((status & BIT_V(uint8_t, kmi_stat_txempty)) != 0)
278 break;
279 }
280 pio_write_8(&pl050->regs->data, dp[i]);
281 }
282 ddf_msg(LVL_NOTE, "%s/pl050_write() success", pl050->name);
283
284 *nwritten = size;
285 return EOK;
286}
287
288void pl050_char_conn(ipc_call_t *icall, void *arg)
289{
290 pl050_t *pl050 = pl050_from_fun((ddf_fun_t *)arg);
291
292 chardev_conn(icall, &pl050->cds);
293}
294
295/** Add device. */
296static errno_t pl050_dev_add(ddf_dev_t *dev)
297{
298 ddf_fun_t *fun_a;
299 pl050_t *pl050 = NULL;
300 const char *mname;
301 errno_t rc;
302
303 ddf_msg(LVL_DEBUG, "pl050_dev_add()");
304
305 pl050 = ddf_dev_data_alloc(dev, sizeof(pl050_t));
306 if (pl050 == NULL) {
307 ddf_msg(LVL_ERROR, "Failed allocating soft state.\n");
308 rc = ENOMEM;
309 goto error;
310 }
311
312 pl050->name = (char *)ddf_dev_get_name(dev);
313 if (pl050->name == NULL) {
314 rc = ENOMEM;
315 goto error;
316 }
317
318 fun_a = ddf_fun_create(dev, fun_inner, "a");
319 if (fun_a == NULL) {
320 ddf_msg(LVL_ERROR, "Failed creating function 'a'.");
321 rc = ENOMEM;
322 goto error;
323 }
324
325 pl050->fun_a = fun_a;
326 pl050->dev = dev;
327
328 rc = pl050_init(pl050);
329 if (rc != EOK)
330 goto error;
331
332 if (str_cmp(pl050->name, "kbd") == 0)
333 mname = "char/atkbd";
334 else
335 mname = "char/ps2mouse";
336
337 rc = ddf_fun_add_match_id(fun_a, mname, 10);
338 if (rc != EOK) {
339 ddf_msg(LVL_ERROR, "Failed adding match IDs to function %s",
340 "char/xtkbd");
341 goto error;
342 }
343
344 chardev_srvs_init(&pl050->cds);
345 pl050->cds.ops = &pl050_chardev_ops;
346 pl050->cds.sarg = pl050;
347
348 ddf_fun_set_conn_handler(fun_a, pl050_char_conn);
349
350 rc = ddf_fun_bind(fun_a);
351 if (rc != EOK) {
352 ddf_msg(LVL_ERROR, "Failed binding function 'a': %s", str_error(rc));
353 ddf_fun_destroy(fun_a);
354 goto error;
355 }
356
357 ddf_msg(LVL_DEBUG, "Device added.");
358 return EOK;
359error:
360 if (pl050 != NULL)
361 free(pl050->name);
362 return rc;
363}
364
365static errno_t pl050_fun_online(ddf_fun_t *fun)
366{
367 ddf_msg(LVL_DEBUG, "pl050_fun_online()");
368 return ddf_fun_online(fun);
369}
370
371static errno_t pl050_fun_offline(ddf_fun_t *fun)
372{
373 ddf_msg(LVL_DEBUG, "pl050_fun_offline()");
374 return ddf_fun_offline(fun);
375}
376
377int main(int argc, char *argv[])
378{
379 errno_t rc;
380
381 printf(NAME ": HelenOS pl050 serial device driver\n");
382 rc = ddf_log_init(NAME);
383 if (rc != EOK) {
384 printf(NAME ": Error connecting logging service.");
385 return 1;
386 }
387
388 return ddf_driver_main(&pl050_driver);
389}
390
Note: See TracBrowser for help on using the repository browser.