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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since c47e1a8 was c47e1a8, checked in by Lenka Trochtova <trochtova.lenka@…>, 15 years ago

merge mainline changes (rev. 451)

  • Property mode set to 100644
File size: 11.4 KB
RevLine 
[c16cf62]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 */
[52b7b1bb]37
[c16cf62]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>
[c47e1a8]47#include <str.h>
[c16cf62]48#include <ctype.h>
[66babbd]49#include <errno.h>
[c16cf62]50
51#include <devman.h>
52#include <ipc/devman.h>
[084ff99]53#include <ipc/driver.h>
[c16cf62]54
55#include "driver.h"
56
[7f8b581]57// driver structure
58
[c16cf62]59static driver_t *driver;
[7f8b581]60
61// devices
62
[084ff99]63LIST_INITIALIZE(devices);
[9a66bc2e]64FIBRIL_MUTEX_INITIALIZE(devices_mutex);
[084ff99]65
[7f8b581]66// interrupts
67
68static interrupt_context_list_t interrupt_contexts;
69
70static irq_cmd_t default_cmds[] = {
71 {
72 .cmd = CMD_ACCEPT
73 }
74};
75
76static irq_code_t default_pseudocode = {
77 sizeof(default_cmds) / sizeof(irq_cmd_t),
78 default_cmds
79};
80
81
82static void driver_irq_handler(ipc_callid_t iid, ipc_call_t *icall)
[2300b9d]83{
[7f8b581]84 int id = (int)IPC_GET_METHOD(*icall);
85 interrupt_context_t *ctx = find_interrupt_context_by_id(&interrupt_contexts, id);
[2300b9d]86 if (NULL != ctx && NULL != ctx->handler) {
87 (*ctx->handler)(ctx->dev, iid, icall);
88 }
[7f8b581]89}
90
[c47e1a8]91/** Wrapper for receiving strings
92 *
93 * This wrapper only makes it more comfortable to use async_data_write_*
94 * functions to receive strings.
95 *
96 * @param str Pointer to string pointer (which should be later disposed
97 * by free()). If the operation fails, the pointer is not
98 * touched.
99 * @param max_size Maximum size (in bytes) of the string to receive. 0 means
100 * no limit.
101 * @param received If not NULL, the size of the received data is stored here.
102 *
103 * @return Zero on success or a value from @ref errno.h on failure.
104 *
105 */
106static int async_string_receive(char **str, const size_t max_size, size_t *received)
107{
108 ipc_callid_t callid;
109 size_t size;
110 if (!async_data_write_receive(&callid, &size)) {
111 ipc_answer_0(callid, EINVAL);
112 return EINVAL;
113 }
114
115 if ((max_size > 0) && (size > max_size)) {
116 ipc_answer_0(callid, EINVAL);
117 return EINVAL;
118 }
119
120 char *data = (char *) malloc(size + 1);
121 if (data == NULL) {
122 ipc_answer_0(callid, ENOMEM);
123 return ENOMEM;
124 }
125
126 int rc = async_data_write_finalize(callid, data, size);
127 if (rc != EOK) {
128 free(data);
129 return rc;
130 }
131
132 data[size] = 0;
133 *str = data;
134 if (received != NULL)
135 *received = size;
136
137 return EOK;
138}
139
[7f8b581]140int register_interrupt_handler(device_t *dev, int irq, interrupt_handler_t *handler, irq_code_t *pseudocode)
141{
142 interrupt_context_t *ctx = create_interrupt_context();
143
144 ctx->dev = dev;
145 ctx->irq = irq;
146 ctx->handler = handler;
147
148 add_interrupt_context(&interrupt_contexts, ctx);
149
150 if (NULL == pseudocode) {
151 pseudocode = &default_pseudocode;
152 }
153
154 int res = ipc_register_irq(irq, dev->handle, ctx->id, pseudocode);
155 if (0 != res) {
156 remove_interrupt_context(&interrupt_contexts, ctx);
157 delete_interrupt_context(ctx);
158 }
159 return res;
160}
161
162int unregister_interrupt_handler(device_t *dev, int irq)
163{
164 interrupt_context_t *ctx = find_interrupt_context(&interrupt_contexts, dev, irq);
165 int res = ipc_unregister_irq(irq, dev->handle);
166 if (NULL != ctx) {
167 remove_interrupt_context(&interrupt_contexts, ctx);
168 delete_interrupt_context(ctx);
169 }
170 return res;
171}
172
[9a66bc2e]173static void add_to_devices_list(device_t *dev)
174{
175 fibril_mutex_lock(&devices_mutex);
176 list_append(&dev->link, &devices);
177 fibril_mutex_unlock(&devices_mutex);
178}
179
180static void remove_from_devices_list(device_t *dev)
181{
182 fibril_mutex_lock(&devices_mutex);
[5af21c5]183 list_remove(&dev->link);
[9a66bc2e]184 fibril_mutex_unlock(&devices_mutex);
185}
186
[52b7b1bb]187static device_t * driver_get_device(link_t *devices, device_handle_t handle)
188{
[a1769ee]189 device_t *dev = NULL;
[9a66bc2e]190
191 fibril_mutex_lock(&devices_mutex);
[a1769ee]192 link_t *link = devices->next;
193 while (link != devices) {
194 dev = list_get_instance(link, device_t, link);
195 if (handle == dev->handle) {
[9a66bc2e]196 fibril_mutex_unlock(&devices_mutex);
[a1769ee]197 return dev;
198 }
[9a66bc2e]199 link = link->next;
[a1769ee]200 }
[9a66bc2e]201 fibril_mutex_unlock(&devices_mutex);
[52b7b1bb]202
[a1769ee]203 return NULL;
204}
205
[52b7b1bb]206static void driver_add_device(ipc_callid_t iid, ipc_call_t *icall)
[084ff99]207{
[df747b9c]208 char *dev_name = NULL;
209 int res = EOK;
[9a66bc2e]210
[52b7b1bb]211 device_handle_t dev_handle = IPC_GET_ARG1(*icall);
[5af21c5]212 device_t *dev = create_device();
[084ff99]213 dev->handle = dev_handle;
[df747b9c]214
215 async_string_receive(&dev_name, 0, NULL);
216 dev->name = dev_name;
217
218 add_to_devices_list(dev);
219 res = driver->driver_ops->add_device(dev);
220 if (0 == res) {
[9a66bc2e]221 printf("%s: new device with handle = %x was added.\n", driver->name, dev_handle);
222 } else {
[df747b9c]223 printf("%s: failed to add a new device with handle = %d.\n", driver->name, dev_handle);
[9a66bc2e]224 remove_from_devices_list(dev);
[5af21c5]225 delete_device(dev);
[084ff99]226 }
[df747b9c]227
228 ipc_answer_0(iid, res);
[084ff99]229}
[c16cf62]230
231static void driver_connection_devman(ipc_callid_t iid, ipc_call_t *icall)
232{
233 /* Accept connection */
234 ipc_answer_0(iid, EOK);
[52b7b1bb]235
[c16cf62]236 bool cont = true;
237 while (cont) {
238 ipc_call_t call;
239 ipc_callid_t callid = async_get_call(&call);
[52b7b1bb]240
[c16cf62]241 switch (IPC_GET_METHOD(call)) {
242 case IPC_M_PHONE_HUNGUP:
243 cont = false;
244 continue;
245 case DRIVER_ADD_DEVICE:
[084ff99]246 driver_add_device(callid, &call);
[c16cf62]247 break;
248 default:
249 if (!(callid & IPC_CALLID_NOTIFICATION))
250 ipc_answer_0(callid, ENOENT);
251 }
[52b7b1bb]252 }
[c16cf62]253}
254
[52b7b1bb]255/**
[a1769ee]256 * Generic client connection handler both for applications and drivers.
[52b7b1bb]257 *
[9a66bc2e]258 * @param drv true for driver client, false for other clients (applications, services etc.).
[a1769ee]259 */
[9a66bc2e]260static void driver_connection_gen(ipc_callid_t iid, ipc_call_t *icall, bool drv)
[52b7b1bb]261{
262 // Answer the first IPC_M_CONNECT_ME_TO call and remember the handle of the device to which the client connected.
[9a66bc2e]263 device_handle_t handle = IPC_GET_ARG2(*icall);
[a1769ee]264 device_t *dev = driver_get_device(&devices, handle);
[52b7b1bb]265
[a1769ee]266 if (dev == NULL) {
[9a66bc2e]267 printf("%s: driver_connection_gen error - no device with handle %x was found.\n", driver->name, handle);
[a1769ee]268 ipc_answer_0(iid, ENOENT);
269 return;
270 }
[5cd136ab]271
272
273 // TODO - if the client is not a driver, check whether it is allowed to use the device
[a1769ee]274
[25a7e11d]275 int ret = EOK;
276 // open the device
277 if (NULL != dev->class && NULL != dev->class->open) {
278 ret = (*dev->class->open)(dev);
279 }
[a1769ee]280
[25a7e11d]281 ipc_answer_0(iid, ret);
[52b7b1bb]282
[a1769ee]283 while (1) {
284 ipc_callid_t callid;
285 ipc_call_t call;
286 callid = async_get_call(&call);
287 ipcarg_t method = IPC_GET_METHOD(call);
[3843ecb]288 int iface_idx;
289
[a1769ee]290 switch (method) {
[25a7e11d]291 case IPC_M_PHONE_HUNGUP:
292 // close the device
293 if (NULL != dev->class && NULL != dev->class->close) {
294 (*dev->class->close)(dev);
295 }
[a1769ee]296 ipc_answer_0(callid, EOK);
297 return;
[3843ecb]298 default:
299 // convert ipc interface id to interface index
300
301 iface_idx = DEV_IFACE_IDX(method);
302
303 if (!is_valid_iface_idx(iface_idx)) {
[08d9525a]304 remote_handler_t *default_handler;
305 if (NULL != (default_handler = device_get_default_handler(dev))) {
306 (*default_handler)(dev, callid, &call);
307 break;
308 }
309 // this is not device's interface and the default handler is not provided
[f658458]310 printf("%s: driver_connection_gen error - invalid interface id %d.", driver->name, iface_idx);
[52b7b1bb]311 ipc_answer_0(callid, ENOTSUP);
312 break;
313 }
314
315 // calling one of the device's interfaces
316
317 // get the device interface structure
[3843ecb]318 void *iface = device_get_iface(dev, iface_idx);
[52b7b1bb]319 if (NULL == iface) {
[9a66bc2e]320 printf("%s: driver_connection_gen error - ", driver->name);
[f658458]321 printf("device with handle %d has no interface with id %d.\n", handle, iface_idx);
[a1769ee]322 ipc_answer_0(callid, ENOTSUP);
[52b7b1bb]323 break;
[a1769ee]324 }
[52b7b1bb]325
326 // get the corresponding interface for remote request handling ("remote interface")
[3843ecb]327 remote_iface_t* rem_iface = get_remote_iface(iface_idx);
[52b7b1bb]328 assert(NULL != rem_iface);
329
330 // get the method of the remote interface
331 ipcarg_t iface_method_idx = IPC_GET_ARG1(call);
332 remote_iface_func_ptr_t iface_method_ptr = get_remote_method(rem_iface, iface_method_idx);
333 if (NULL == iface_method_ptr) {
334 // the interface has not such method
[9a66bc2e]335 printf("%s: driver_connection_gen error - invalid interface method.", driver->name);
[52b7b1bb]336 ipc_answer_0(callid, ENOTSUP);
337 break;
338 }
339
340 // call the remote interface's method, which will receive parameters from the remote client
341 // and it will pass it to the corresponding local interface method associated with the device
342 // by its driver
343 (*iface_method_ptr)(dev, iface, callid, &call);
[a1769ee]344 break;
345 }
346 }
347}
348
[c16cf62]349static void driver_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
350{
[52b7b1bb]351 driver_connection_gen(iid, icall, true);
[c16cf62]352}
353
354static void driver_connection_client(ipc_callid_t iid, ipc_call_t *icall)
355{
[52b7b1bb]356 driver_connection_gen(iid, icall, false);
[c16cf62]357}
358
359
360/** Function for handling connections to device driver.
361 *
362 */
363static void driver_connection(ipc_callid_t iid, ipc_call_t *icall)
364{
365 /* Select interface */
366 switch ((ipcarg_t) (IPC_GET_ARG1(*icall))) {
367 case DRIVER_DEVMAN:
368 // handle PnP events from device manager
369 driver_connection_devman(iid, icall);
370 break;
371 case DRIVER_DRIVER:
372 // handle request from drivers of child devices
373 driver_connection_driver(iid, icall);
374 break;
375 case DRIVER_CLIENT:
[52b7b1bb]376 // handle requests from client applications
[c16cf62]377 driver_connection_client(iid, icall);
378 break;
379
380 default:
[52b7b1bb]381 /* No such interface */
[c16cf62]382 ipc_answer_0(iid, ENOENT);
383 }
384}
385
[df747b9c]386int child_device_register(device_t *child, device_t *parent)
[7707954]387{
[2480e19]388 // printf("%s: child_device_register\n", driver->name);
[52b7b1bb]389
[bda60d9]390 assert(NULL != child->name);
[52b7b1bb]391
[df747b9c]392 int res;
393
[9a66bc2e]394 add_to_devices_list(child);
[df747b9c]395 if (EOK == (res = devman_child_device_register(child->name, &child->match_ids, parent->handle, &child->handle))) {
396 return res;
[7707954]397 }
[9a66bc2e]398 remove_from_devices_list(child);
[df747b9c]399 return res;
[7707954]400}
401
[52b7b1bb]402int driver_main(driver_t *drv)
[c16cf62]403{
404 // remember the driver structure - driver_ops will be called by generic handler for incoming connections
405 driver = drv;
[52b7b1bb]406
[7f8b581]407 // initialize the list of interrupt contexts
408 init_interrupt_context_list(&interrupt_contexts);
409
410 // set generic interrupt handler
411 async_set_interrupt_received(driver_irq_handler);
412
[c16cf62]413 // register driver by device manager with generic handler for incoming connections
[52b7b1bb]414 devman_driver_register(driver->name, driver_connection);
[c16cf62]415
416 async_manager();
417
418 // Never reached
[52b7b1bb]419 return 0;
[c16cf62]420}
421
422/**
423 * @}
[52b7b1bb]424 */
Note: See TracBrowser for help on using the repository browser.