source: mainline/uspace/lib/drv/generic/driver.c@ 0ca16307

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 0ca16307 was 0ca16307, checked in by Vojtech Horky <vojtechhorky@…>, 15 years ago

Add wrapper for adding child device with single match id

The mentioned wrapper neatly wraps calls for creating new device, new match
id and registering such device at devman.

This call is now used in root driver.

  • Property mode set to 100644
File size: 11.3 KB
Line 
1/*
2 * Copyright (c) 2010 Lenka Trochtova
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 * @defgroup libdrv generic device driver support.
31 * @brief HelenOS generic device driver support.
32 * @{
33 */
34
35/** @file
36 */
37
38#include <assert.h>
39#include <ipc/services.h>
40#include <ipc/ns.h>
41#include <async.h>
42#include <stdio.h>
43#include <errno.h>
44#include <bool.h>
45#include <fibril_synch.h>
46#include <stdlib.h>
47#include <str.h>
48#include <ctype.h>
49#include <errno.h>
50
51#include <ipc/driver.h>
52
53#include "driver.h"
54
55/* driver structure */
56
57static driver_t *driver;
58
59/* devices */
60
61LIST_INITIALIZE(devices);
62FIBRIL_MUTEX_INITIALIZE(devices_mutex);
63
64/* interrupts */
65
66static interrupt_context_list_t interrupt_contexts;
67
68static irq_cmd_t default_cmds[] = {
69 {
70 .cmd = CMD_ACCEPT
71 }
72};
73
74static irq_code_t default_pseudocode = {
75 sizeof(default_cmds) / sizeof(irq_cmd_t),
76 default_cmds
77};
78
79
80static void driver_irq_handler(ipc_callid_t iid, ipc_call_t *icall)
81{
82 int id = (int)IPC_GET_METHOD(*icall);
83 interrupt_context_t *ctx;
84
85 ctx = find_interrupt_context_by_id(&interrupt_contexts, id);
86 if (NULL != ctx && NULL != ctx->handler)
87 (*ctx->handler)(ctx->dev, iid, icall);
88}
89
90int
91register_interrupt_handler(device_t *dev, int irq, interrupt_handler_t *handler,
92 irq_code_t *pseudocode)
93{
94 interrupt_context_t *ctx = create_interrupt_context();
95
96 ctx->dev = dev;
97 ctx->irq = irq;
98 ctx->handler = handler;
99
100 add_interrupt_context(&interrupt_contexts, ctx);
101
102 if (NULL == pseudocode)
103 pseudocode = &default_pseudocode;
104
105 int res = ipc_register_irq(irq, dev->handle, ctx->id, pseudocode);
106 if (0 != res) {
107 remove_interrupt_context(&interrupt_contexts, ctx);
108 delete_interrupt_context(ctx);
109 }
110
111 return res;
112}
113
114int unregister_interrupt_handler(device_t *dev, int irq)
115{
116 interrupt_context_t *ctx = find_interrupt_context(&interrupt_contexts,
117 dev, irq);
118 int res = ipc_unregister_irq(irq, dev->handle);
119
120 if (NULL != ctx) {
121 remove_interrupt_context(&interrupt_contexts, ctx);
122 delete_interrupt_context(ctx);
123 }
124 return res;
125}
126
127static void add_to_devices_list(device_t *dev)
128{
129 fibril_mutex_lock(&devices_mutex);
130 list_append(&dev->link, &devices);
131 fibril_mutex_unlock(&devices_mutex);
132}
133
134static void remove_from_devices_list(device_t *dev)
135{
136 fibril_mutex_lock(&devices_mutex);
137 list_remove(&dev->link);
138 fibril_mutex_unlock(&devices_mutex);
139}
140
141static device_t * driver_get_device(link_t *devices, devman_handle_t handle)
142{
143 device_t *dev = NULL;
144
145 fibril_mutex_lock(&devices_mutex);
146 link_t *link = devices->next;
147 while (link != devices) {
148 dev = list_get_instance(link, device_t, link);
149 if (handle == dev->handle) {
150 fibril_mutex_unlock(&devices_mutex);
151 return dev;
152 }
153 link = link->next;
154 }
155 fibril_mutex_unlock(&devices_mutex);
156
157 return NULL;
158}
159
160static void driver_add_device(ipc_callid_t iid, ipc_call_t *icall)
161{
162 char *dev_name = NULL;
163 int res = EOK;
164
165 devman_handle_t dev_handle = IPC_GET_ARG1(*icall);
166 devman_handle_t parent_dev_handle = IPC_GET_ARG2(*icall);
167
168 device_t *dev = create_device();
169 dev->handle = dev_handle;
170
171 async_data_write_accept((void **) &dev_name, true, 0, 0, 0, 0);
172 dev->name = dev_name;
173
174 add_to_devices_list(dev);
175 dev->parent = driver_get_device(&devices, parent_dev_handle);
176
177 res = driver->driver_ops->add_device(dev);
178 if (0 == res) {
179 printf("%s: new device with handle = %x was added.\n",
180 driver->name, dev_handle);
181 } else {
182 printf("%s: failed to add a new device with handle = %d.\n",
183 driver->name, dev_handle);
184 remove_from_devices_list(dev);
185 delete_device(dev);
186 }
187
188 ipc_answer_0(iid, res);
189}
190
191static void driver_connection_devman(ipc_callid_t iid, ipc_call_t *icall)
192{
193 /* Accept connection */
194 ipc_answer_0(iid, EOK);
195
196 bool cont = true;
197 while (cont) {
198 ipc_call_t call;
199 ipc_callid_t callid = async_get_call(&call);
200
201 switch (IPC_GET_METHOD(call)) {
202 case IPC_M_PHONE_HUNGUP:
203 cont = false;
204 continue;
205 case DRIVER_ADD_DEVICE:
206 driver_add_device(callid, &call);
207 break;
208 default:
209 if (!(callid & IPC_CALLID_NOTIFICATION))
210 ipc_answer_0(callid, ENOENT);
211 }
212 }
213}
214
215/**
216 * Generic client connection handler both for applications and drivers.
217 *
218 * @param drv True for driver client, false for other clients
219 * (applications, services etc.).
220 */
221static void driver_connection_gen(ipc_callid_t iid, ipc_call_t *icall, bool drv)
222{
223 /*
224 * Answer the first IPC_M_CONNECT_ME_TO call and remember the handle of
225 * the device to which the client connected.
226 */
227 devman_handle_t handle = IPC_GET_ARG2(*icall);
228 device_t *dev = driver_get_device(&devices, handle);
229
230 if (dev == NULL) {
231 printf("%s: driver_connection_gen error - no device with handle"
232 " %x was found.\n", driver->name, handle);
233 ipc_answer_0(iid, ENOENT);
234 return;
235 }
236
237
238 /*
239 * TODO - if the client is not a driver, check whether it is allowed to
240 * use the device.
241 */
242
243 int ret = EOK;
244 /* open the device */
245 if (NULL != dev->ops && NULL != dev->ops->open)
246 ret = (*dev->ops->open)(dev);
247
248 ipc_answer_0(iid, ret);
249 if (EOK != ret)
250 return;
251
252 while (1) {
253 ipc_callid_t callid;
254 ipc_call_t call;
255 callid = async_get_call(&call);
256 ipcarg_t method = IPC_GET_METHOD(call);
257 int iface_idx;
258
259 switch (method) {
260 case IPC_M_PHONE_HUNGUP:
261 /* close the device */
262 if (NULL != dev->ops && NULL != dev->ops->close)
263 (*dev->ops->close)(dev);
264 ipc_answer_0(callid, EOK);
265 return;
266 default:
267 /* convert ipc interface id to interface index */
268
269 iface_idx = DEV_IFACE_IDX(method);
270
271 if (!is_valid_iface_idx(iface_idx)) {
272 remote_handler_t *default_handler =
273 device_get_default_handler(dev);
274 if (NULL != default_handler) {
275 (*default_handler)(dev, callid, &call);
276 break;
277 }
278 /*
279 * This is not device's interface and the
280 * default handler is not provided.
281 */
282 printf("%s: driver_connection_gen error - "
283 "invalid interface id %d.",
284 driver->name, iface_idx);
285 ipc_answer_0(callid, ENOTSUP);
286 break;
287 }
288
289 /* calling one of the device's interfaces */
290
291 /* get the device interface structure */
292 void *iface = device_get_iface(dev, iface_idx);
293 if (NULL == iface) {
294 printf("%s: driver_connection_gen error - ",
295 driver->name);
296 printf("device with handle %d has no interface "
297 "with id %d.\n", handle, iface_idx);
298 ipc_answer_0(callid, ENOTSUP);
299 break;
300 }
301
302 /*
303 * Get the corresponding interface for remote request
304 * handling ("remote interface").
305 */
306 remote_iface_t* rem_iface = get_remote_iface(iface_idx);
307 assert(NULL != rem_iface);
308
309 /* get the method of the remote interface */
310 ipcarg_t iface_method_idx = IPC_GET_ARG1(call);
311 remote_iface_func_ptr_t iface_method_ptr =
312 get_remote_method(rem_iface, iface_method_idx);
313 if (NULL == iface_method_ptr) {
314 // the interface has not such method
315 printf("%s: driver_connection_gen error - "
316 "invalid interface method.", driver->name);
317 ipc_answer_0(callid, ENOTSUP);
318 break;
319 }
320
321 /*
322 * Call the remote interface's method, which will
323 * receive parameters from the remote client and it will
324 * pass it to the corresponding local interface method
325 * associated with the device by its driver.
326 */
327 (*iface_method_ptr)(dev, iface, callid, &call);
328 break;
329 }
330 }
331}
332
333static void driver_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
334{
335 driver_connection_gen(iid, icall, true);
336}
337
338static void driver_connection_client(ipc_callid_t iid, ipc_call_t *icall)
339{
340 driver_connection_gen(iid, icall, false);
341}
342
343
344/** Function for handling connections to device driver. */
345static void driver_connection(ipc_callid_t iid, ipc_call_t *icall)
346{
347 /* Select interface */
348 switch ((ipcarg_t) (IPC_GET_ARG1(*icall))) {
349 case DRIVER_DEVMAN:
350 /* handle PnP events from device manager */
351 driver_connection_devman(iid, icall);
352 break;
353 case DRIVER_DRIVER:
354 /* handle request from drivers of child devices */
355 driver_connection_driver(iid, icall);
356 break;
357 case DRIVER_CLIENT:
358 /* handle requests from client applications */
359 driver_connection_client(iid, icall);
360 break;
361
362 default:
363 /* No such interface */
364 ipc_answer_0(iid, ENOENT);
365 }
366}
367
368int child_device_register(device_t *child, device_t *parent)
369{
370 assert(NULL != child->name);
371
372 int res;
373
374 add_to_devices_list(child);
375 res = devman_child_device_register(child->name, &child->match_ids,
376 parent->handle, &child->handle);
377 if (EOK == res)
378 return res;
379 remove_from_devices_list(child);
380 return res;
381}
382
383/** Wrapper for child_device_register for devices with single match id.
384 *
385 * @param parent Parent device.
386 * @param child_name Child device name.
387 * @param child_match_id Child device match id.
388 * @param child_match_score Child device match score.
389 * @return Error code.
390 */
391int child_device_register_wrapper(device_t *parent, const char *child_name,
392 const char *child_match_id, int child_match_score)
393{
394 device_t *child = NULL;
395 match_id_t *match_id = NULL;
396 int rc;
397
398 child = create_device();
399 if (child == NULL) {
400 rc = ENOMEM;
401 goto failure;
402 }
403
404 child->name = child_name;
405
406 match_id = create_match_id();
407 if (match_id == NULL) {
408 rc = ENOMEM;
409 goto failure;
410 }
411
412 match_id->id = child_match_id;
413 match_id->score = child_match_score;
414 add_match_id(&child->match_ids, match_id);
415
416 rc = child_device_register(child, parent);
417 if (EOK != rc)
418 goto failure;
419
420 goto leave;
421
422failure:
423 if (match_id != NULL) {
424 match_id->id = NULL;
425 delete_match_id(match_id);
426 }
427
428 if (child != NULL) {
429 child->name = NULL;
430 delete_device(child);
431 }
432
433leave:
434 return rc;
435}
436
437int driver_main(driver_t *drv)
438{
439 /*
440 * Remember the driver structure - driver_ops will be called by generic
441 * handler for incoming connections.
442 */
443 driver = drv;
444
445 /* Initialize the list of interrupt contexts. */
446 init_interrupt_context_list(&interrupt_contexts);
447
448 /* Set generic interrupt handler. */
449 async_set_interrupt_received(driver_irq_handler);
450
451 /*
452 * Register driver by device manager with generic handler for incoming
453 * connections.
454 */
455 devman_driver_register(driver->name, driver_connection);
456
457 async_manager();
458
459 /* Never reached. */
460 return 0;
461}
462
463/**
464 * @}
465 */
Note: See TracBrowser for help on using the repository browser.