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
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 <devman.h>
52#include <ipc/devman.h>
53#include <ipc/driver.h>
54
55#include "driver.h"
56
57// driver structure
58
59static driver_t *driver;
60
61// devices
62
63LIST_INITIALIZE(devices);
64FIBRIL_MUTEX_INITIALIZE(devices_mutex);
65
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)
83{
84 int id = (int)IPC_GET_METHOD(*icall);
85 interrupt_context_t *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}
90
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
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
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);
183 list_remove(&dev->link);
184 fibril_mutex_unlock(&devices_mutex);
185}
186
187static device_t * driver_get_device(link_t *devices, device_handle_t handle)
188{
189 device_t *dev = NULL;
190
191 fibril_mutex_lock(&devices_mutex);
192 link_t *link = devices->next;
193 while (link != devices) {
194 dev = list_get_instance(link, device_t, link);
195 if (handle == dev->handle) {
196 fibril_mutex_unlock(&devices_mutex);
197 return dev;
198 }
199 link = link->next;
200 }
201 fibril_mutex_unlock(&devices_mutex);
202
203 return NULL;
204}
205
206static void driver_add_device(ipc_callid_t iid, ipc_call_t *icall)
207{
208 char *dev_name = NULL;
209 int res = EOK;
210
211 device_handle_t dev_handle = IPC_GET_ARG1(*icall);
212 device_t *dev = create_device();
213 dev->handle = dev_handle;
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) {
221 printf("%s: new device with handle = %x was added.\n", driver->name, dev_handle);
222 } else {
223 printf("%s: failed to add a new device with handle = %d.\n", driver->name, dev_handle);
224 remove_from_devices_list(dev);
225 delete_device(dev);
226 }
227
228 ipc_answer_0(iid, res);
229}
230
231static void driver_connection_devman(ipc_callid_t iid, ipc_call_t *icall)
232{
233 /* Accept connection */
234 ipc_answer_0(iid, EOK);
235
236 bool cont = true;
237 while (cont) {
238 ipc_call_t call;
239 ipc_callid_t callid = async_get_call(&call);
240
241 switch (IPC_GET_METHOD(call)) {
242 case IPC_M_PHONE_HUNGUP:
243 cont = false;
244 continue;
245 case DRIVER_ADD_DEVICE:
246 driver_add_device(callid, &call);
247 break;
248 default:
249 if (!(callid & IPC_CALLID_NOTIFICATION))
250 ipc_answer_0(callid, ENOENT);
251 }
252 }
253}
254
255/**
256 * Generic client connection handler both for applications and drivers.
257 *
258 * @param drv true for driver client, false for other clients (applications, services etc.).
259 */
260static void driver_connection_gen(ipc_callid_t iid, ipc_call_t *icall, bool drv)
261{
262 // Answer the first IPC_M_CONNECT_ME_TO call and remember the handle of the device to which the client connected.
263 device_handle_t handle = IPC_GET_ARG2(*icall);
264 device_t *dev = driver_get_device(&devices, handle);
265
266 if (dev == NULL) {
267 printf("%s: driver_connection_gen error - no device with handle %x was found.\n", driver->name, handle);
268 ipc_answer_0(iid, ENOENT);
269 return;
270 }
271
272
273 // TODO - if the client is not a driver, check whether it is allowed to use the device
274
275 int ret = EOK;
276 // open the device
277 if (NULL != dev->class && NULL != dev->class->open) {
278 ret = (*dev->class->open)(dev);
279 }
280
281 ipc_answer_0(iid, ret);
282
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);
288 int iface_idx;
289
290 switch (method) {
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 }
296 ipc_answer_0(callid, EOK);
297 return;
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)) {
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
310 printf("%s: driver_connection_gen error - invalid interface id %d.", driver->name, iface_idx);
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
318 void *iface = device_get_iface(dev, iface_idx);
319 if (NULL == iface) {
320 printf("%s: driver_connection_gen error - ", driver->name);
321 printf("device with handle %d has no interface with id %d.\n", handle, iface_idx);
322 ipc_answer_0(callid, ENOTSUP);
323 break;
324 }
325
326 // get the corresponding interface for remote request handling ("remote interface")
327 remote_iface_t* rem_iface = get_remote_iface(iface_idx);
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
335 printf("%s: driver_connection_gen error - invalid interface method.", driver->name);
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);
344 break;
345 }
346 }
347}
348
349static void driver_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
350{
351 driver_connection_gen(iid, icall, true);
352}
353
354static void driver_connection_client(ipc_callid_t iid, ipc_call_t *icall)
355{
356 driver_connection_gen(iid, icall, false);
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:
376 // handle requests from client applications
377 driver_connection_client(iid, icall);
378 break;
379
380 default:
381 /* No such interface */
382 ipc_answer_0(iid, ENOENT);
383 }
384}
385
386int child_device_register(device_t *child, device_t *parent)
387{
388 // printf("%s: child_device_register\n", driver->name);
389
390 assert(NULL != child->name);
391
392 int res;
393
394 add_to_devices_list(child);
395 if (EOK == (res = devman_child_device_register(child->name, &child->match_ids, parent->handle, &child->handle))) {
396 return res;
397 }
398 remove_from_devices_list(child);
399 return res;
400}
401
402int driver_main(driver_t *drv)
403{
404 // remember the driver structure - driver_ops will be called by generic handler for incoming connections
405 driver = drv;
406
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
413 // register driver by device manager with generic handler for incoming connections
414 devman_driver_register(driver->name, driver_connection);
415
416 async_manager();
417
418 // Never reached
419 return 0;
420}
421
422/**
423 * @}
424 */
Note: See TracBrowser for help on using the repository browser.