Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
disp.c
Go to the documentation of this file.
1 /*
2  * disp.c
3  *
4  * DSP-BIOS Bridge driver support functions for TI OMAP processors.
5  *
6  * Node Dispatcher interface. Communicates with Resource Manager Server
7  * (RMS) on DSP. Access to RMS is synchronized in NODE.
8  *
9  * Copyright (C) 2005-2006 Texas Instruments, Inc.
10  *
11  * This package is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License version 2 as
13  * published by the Free Software Foundation.
14  *
15  * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
17  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18  */
19 #include <linux/types.h>
20 
21 /* ----------------------------------- Host OS */
22 #include <dspbridge/host_os.h>
23 
24 /* ----------------------------------- DSP/BIOS Bridge */
25 #include <dspbridge/dbdefs.h>
26 
27 /* ----------------------------------- OS Adaptation Layer */
28 #include <dspbridge/sync.h>
29 
30 /* ----------------------------------- Link Driver */
31 #include <dspbridge/dspdefs.h>
32 
33 /* ----------------------------------- Platform Manager */
34 #include <dspbridge/dev.h>
35 #include <dspbridge/chnldefs.h>
36 
37 /* ----------------------------------- Resource Manager */
38 #include <dspbridge/nodedefs.h>
39 #include <dspbridge/nodepriv.h>
40 #include <dspbridge/rms_sh.h>
41 
42 /* ----------------------------------- This */
43 #include <dspbridge/disp.h>
44 
45 /* Size of a reply from RMS */
46 #define REPLYSIZE (3 * sizeof(rms_word))
47 
48 /* Reserved channel offsets for communication with RMS */
49 #define CHNLTORMSOFFSET 0
50 #define CHNLFROMRMSOFFSET 1
51 
52 #define CHNLIOREQS 1
53 
54 /*
55  * ======== disp_object ========
56  */
57 struct disp_object {
58  struct dev_object *dev_obj; /* Device for this processor */
59  /* Function interface to Bridge driver */
61  struct chnl_mgr *chnl_mgr; /* Channel manager */
62  struct chnl_object *chnl_to_dsp; /* Chnl for commands to RMS */
63  struct chnl_object *chnl_from_dsp; /* Chnl for replies from RMS */
64  u8 *buf; /* Buffer for commands, replies */
65  u32 bufsize; /* buf size in bytes */
66  u32 bufsize_rms; /* buf size in RMS words */
67  u32 char_size; /* Size of DSP character */
68  u32 word_size; /* Size of DSP word */
69  u32 data_mau_size; /* Size of DSP Data MAU */
70 };
71 
72 static void delete_disp(struct disp_object *disp_obj);
73 static int fill_stream_def(rms_word *pdw_buf, u32 *ptotal, u32 offset,
74  struct node_strmdef strm_def, u32 max,
75  u32 chars_in_rms_word);
76 static int send_message(struct disp_object *disp_obj, u32 timeout,
77  u32 ul_bytes, u32 *pdw_arg);
78 
79 /*
80  * ======== disp_create ========
81  * Create a NODE Dispatcher object.
82  */
83 int disp_create(struct disp_object **dispatch_obj,
84  struct dev_object *hdev_obj,
85  const struct disp_attr *disp_attrs)
86 {
87  struct disp_object *disp_obj;
88  struct bridge_drv_interface *intf_fxns;
89  u32 ul_chnl_id;
90  struct chnl_attr chnl_attr_obj;
91  int status = 0;
92  u8 dev_type;
93 
94  *dispatch_obj = NULL;
95 
96  /* Allocate Node Dispatcher object */
97  disp_obj = kzalloc(sizeof(struct disp_object), GFP_KERNEL);
98  if (disp_obj == NULL)
99  status = -ENOMEM;
100  else
101  disp_obj->dev_obj = hdev_obj;
102 
103  /* Get Channel manager and Bridge function interface */
104  if (!status) {
105  status = dev_get_chnl_mgr(hdev_obj, &(disp_obj->chnl_mgr));
106  if (!status) {
107  (void)dev_get_intf_fxns(hdev_obj, &intf_fxns);
108  disp_obj->intf_fxns = intf_fxns;
109  }
110  }
111 
112  /* check device type and decide if streams or messag'ing is used for
113  * RMS/EDS */
114  if (status)
115  goto func_cont;
116 
117  status = dev_get_dev_type(hdev_obj, &dev_type);
118 
119  if (status)
120  goto func_cont;
121 
122  if (dev_type != DSP_UNIT) {
123  status = -EPERM;
124  goto func_cont;
125  }
126 
127  disp_obj->char_size = DSPWORDSIZE;
128  disp_obj->word_size = DSPWORDSIZE;
129  disp_obj->data_mau_size = DSPWORDSIZE;
130  /* Open channels for communicating with the RMS */
131  chnl_attr_obj.uio_reqs = CHNLIOREQS;
132  chnl_attr_obj.event_obj = NULL;
133  ul_chnl_id = disp_attrs->chnl_offset + CHNLTORMSOFFSET;
134  status = (*intf_fxns->chnl_open) (&(disp_obj->chnl_to_dsp),
135  disp_obj->chnl_mgr,
136  CHNL_MODETODSP, ul_chnl_id,
137  &chnl_attr_obj);
138 
139  if (!status) {
140  ul_chnl_id = disp_attrs->chnl_offset + CHNLFROMRMSOFFSET;
141  status =
142  (*intf_fxns->chnl_open) (&(disp_obj->chnl_from_dsp),
143  disp_obj->chnl_mgr,
144  CHNL_MODEFROMDSP, ul_chnl_id,
145  &chnl_attr_obj);
146  }
147  if (!status) {
148  /* Allocate buffer for commands, replies */
149  disp_obj->bufsize = disp_attrs->chnl_buf_size;
150  disp_obj->bufsize_rms = RMS_COMMANDBUFSIZE;
151  disp_obj->buf = kzalloc(disp_obj->bufsize, GFP_KERNEL);
152  if (disp_obj->buf == NULL)
153  status = -ENOMEM;
154  }
155 func_cont:
156  if (!status)
157  *dispatch_obj = disp_obj;
158  else
159  delete_disp(disp_obj);
160 
161  return status;
162 }
163 
164 /*
165  * ======== disp_delete ========
166  * Delete the NODE Dispatcher.
167  */
168 void disp_delete(struct disp_object *disp_obj)
169 {
170  delete_disp(disp_obj);
171 }
172 
173 /*
174  * ======== disp_node_change_priority ========
175  * Change the priority of a node currently running on the target.
176  */
178  struct node_object *hnode,
179  u32 rms_fxn, nodeenv node_env, s32 prio)
180 {
181  u32 dw_arg;
182  struct rms_command *rms_cmd;
183  int status = 0;
184 
185  /* Send message to RMS to change priority */
186  rms_cmd = (struct rms_command *)(disp_obj->buf);
187  rms_cmd->fxn = (rms_word) (rms_fxn);
188  rms_cmd->arg1 = (rms_word) node_env;
189  rms_cmd->arg2 = prio;
190  status = send_message(disp_obj, node_get_timeout(hnode),
191  sizeof(struct rms_command), &dw_arg);
192 
193  return status;
194 }
195 
196 /*
197  * ======== disp_node_create ========
198  * Create a node on the DSP by remotely calling the node's create function.
199  */
200 int disp_node_create(struct disp_object *disp_obj,
201  struct node_object *hnode, u32 rms_fxn,
202  u32 ul_create_fxn,
203  const struct node_createargs *pargs,
204  nodeenv *node_env)
205 {
206  struct node_msgargs node_msg_args;
207  struct node_taskargs task_arg_obj;
208  struct rms_command *rms_cmd;
209  struct rms_msg_args *pmsg_args;
210  struct rms_more_task_args *more_task_args;
211  enum node_type node_type;
212  u32 dw_length;
213  rms_word *pdw_buf = NULL;
214  u32 ul_bytes;
215  u32 i;
216  u32 total;
217  u32 chars_in_rms_word;
218  s32 task_args_offset;
219  s32 sio_in_def_offset;
220  s32 sio_out_def_offset;
221  s32 sio_defs_offset;
222  s32 args_offset = -1;
223  s32 offset;
224  struct node_strmdef strm_def;
225  u32 max;
226  int status = 0;
227  struct dsp_nodeinfo node_info;
228  u8 dev_type;
229 
230  status = dev_get_dev_type(disp_obj->dev_obj, &dev_type);
231 
232  if (status)
233  goto func_end;
234 
235  if (dev_type != DSP_UNIT) {
236  dev_dbg(bridge, "%s: unknown device type = 0x%x\n",
237  __func__, dev_type);
238  goto func_end;
239  }
240  node_type = node_get_type(hnode);
241  node_msg_args = pargs->asa.node_msg_args;
242  max = disp_obj->bufsize_rms; /*Max # of RMS words that can be sent */
243  chars_in_rms_word = sizeof(rms_word) / disp_obj->char_size;
244  /* Number of RMS words needed to hold arg data */
245  dw_length =
246  (node_msg_args.arg_length + chars_in_rms_word -
247  1) / chars_in_rms_word;
248  /* Make sure msg args and command fit in buffer */
249  total = sizeof(struct rms_command) / sizeof(rms_word) +
250  sizeof(struct rms_msg_args)
251  / sizeof(rms_word) - 1 + dw_length;
252  if (total >= max) {
253  status = -EPERM;
254  dev_dbg(bridge, "%s: Message args too large for buffer! size "
255  "= %d, max = %d\n", __func__, total, max);
256  }
257  /*
258  * Fill in buffer to send to RMS.
259  * The buffer will have the following format:
260  *
261  * RMS command:
262  * Address of RMS_CreateNode()
263  * Address of node's create function
264  * dummy argument
265  * node type
266  *
267  * Message Args:
268  * max number of messages
269  * segid for message buffer allocation
270  * notification type to use when message is received
271  * length of message arg data
272  * message args data
273  *
274  * Task Args (if task or socket node):
275  * priority
276  * stack size
277  * system stack size
278  * stack segment
279  * misc
280  * number of input streams
281  * pSTRMInDef[] - offsets of STRM definitions for input streams
282  * number of output streams
283  * pSTRMOutDef[] - offsets of STRM definitions for output
284  * streams
285  * STRMInDef[] - array of STRM definitions for input streams
286  * STRMOutDef[] - array of STRM definitions for output streams
287  *
288  * Socket Args (if DAIS socket node):
289  *
290  */
291  if (!status) {
292  total = 0; /* Total number of words in buffer so far */
293  pdw_buf = (rms_word *) disp_obj->buf;
294  rms_cmd = (struct rms_command *)pdw_buf;
295  rms_cmd->fxn = (rms_word) (rms_fxn);
296  rms_cmd->arg1 = (rms_word) (ul_create_fxn);
297  if (node_get_load_type(hnode) == NLDR_DYNAMICLOAD) {
298  /* Flush ICACHE on Load */
299  rms_cmd->arg2 = 1; /* dummy argument */
300  } else {
301  /* Do not flush ICACHE */
302  rms_cmd->arg2 = 0; /* dummy argument */
303  }
304  rms_cmd->data = node_get_type(hnode);
305  /*
306  * args_offset is the offset of the data field in struct
307  * rms_command structure. We need this to calculate stream
308  * definition offsets.
309  */
310  args_offset = 3;
311  total += sizeof(struct rms_command) / sizeof(rms_word);
312  /* Message args */
313  pmsg_args = (struct rms_msg_args *)(pdw_buf + total);
314  pmsg_args->max_msgs = node_msg_args.max_msgs;
315  pmsg_args->segid = node_msg_args.seg_id;
316  pmsg_args->notify_type = node_msg_args.notify_type;
317  pmsg_args->arg_length = node_msg_args.arg_length;
318  total += sizeof(struct rms_msg_args) / sizeof(rms_word) - 1;
319  memcpy(pdw_buf + total, node_msg_args.pdata,
320  node_msg_args.arg_length);
321  total += dw_length;
322  }
323  if (status)
324  goto func_end;
325 
326  /* If node is a task node, copy task create arguments into buffer */
327  if (node_type == NODE_TASK || node_type == NODE_DAISSOCKET) {
328  task_arg_obj = pargs->asa.task_arg_obj;
329  task_args_offset = total;
330  total += sizeof(struct rms_more_task_args) / sizeof(rms_word) +
331  1 + task_arg_obj.num_inputs + task_arg_obj.num_outputs;
332  /* Copy task arguments */
333  if (total < max) {
334  total = task_args_offset;
335  more_task_args = (struct rms_more_task_args *)(pdw_buf +
336  total);
337  /*
338  * Get some important info about the node. Note that we
339  * don't just reach into the hnode struct because
340  * that would break the node object's abstraction.
341  */
342  get_node_info(hnode, &node_info);
343  more_task_args->priority = node_info.execution_priority;
344  more_task_args->stack_size = task_arg_obj.stack_size;
345  more_task_args->sysstack_size =
346  task_arg_obj.sys_stack_size;
347  more_task_args->stack_seg = task_arg_obj.stack_seg;
348  more_task_args->heap_addr = task_arg_obj.dsp_heap_addr;
349  more_task_args->heap_size = task_arg_obj.heap_size;
350  more_task_args->misc = task_arg_obj.dais_arg;
351  more_task_args->num_input_streams =
352  task_arg_obj.num_inputs;
353  total +=
354  sizeof(struct rms_more_task_args) /
355  sizeof(rms_word);
356  dev_dbg(bridge, "%s: dsp_heap_addr %x, heap_size %x\n",
357  __func__, task_arg_obj.dsp_heap_addr,
358  task_arg_obj.heap_size);
359  /* Keep track of pSIOInDef[] and pSIOOutDef[]
360  * positions in the buffer, since this needs to be
361  * filled in later. */
362  sio_in_def_offset = total;
363  total += task_arg_obj.num_inputs;
364  pdw_buf[total++] = task_arg_obj.num_outputs;
365  sio_out_def_offset = total;
366  total += task_arg_obj.num_outputs;
367  sio_defs_offset = total;
368  /* Fill SIO defs and offsets */
369  offset = sio_defs_offset;
370  for (i = 0; i < task_arg_obj.num_inputs; i++) {
371  if (status)
372  break;
373 
374  pdw_buf[sio_in_def_offset + i] =
375  (offset - args_offset)
376  * (sizeof(rms_word) / DSPWORDSIZE);
377  strm_def = task_arg_obj.strm_in_def[i];
378  status =
379  fill_stream_def(pdw_buf, &total, offset,
380  strm_def, max,
381  chars_in_rms_word);
382  offset = total;
383  }
384  for (i = 0; (i < task_arg_obj.num_outputs) &&
385  (!status); i++) {
386  pdw_buf[sio_out_def_offset + i] =
387  (offset - args_offset)
388  * (sizeof(rms_word) / DSPWORDSIZE);
389  strm_def = task_arg_obj.strm_out_def[i];
390  status =
391  fill_stream_def(pdw_buf, &total, offset,
392  strm_def, max,
393  chars_in_rms_word);
394  offset = total;
395  }
396  } else {
397  /* Args won't fit */
398  status = -EPERM;
399  }
400  }
401  if (!status) {
402  ul_bytes = total * sizeof(rms_word);
403  status = send_message(disp_obj, node_get_timeout(hnode),
404  ul_bytes, node_env);
405  }
406 func_end:
407  return status;
408 }
409 
410 /*
411  * ======== disp_node_delete ========
412  * purpose:
413  * Delete a node on the DSP by remotely calling the node's delete function.
414  *
415  */
416 int disp_node_delete(struct disp_object *disp_obj,
417  struct node_object *hnode, u32 rms_fxn,
418  u32 ul_delete_fxn, nodeenv node_env)
419 {
420  u32 dw_arg;
421  struct rms_command *rms_cmd;
422  int status = 0;
423  u8 dev_type;
424 
425  status = dev_get_dev_type(disp_obj->dev_obj, &dev_type);
426 
427  if (!status) {
428 
429  if (dev_type == DSP_UNIT) {
430 
431  /*
432  * Fill in buffer to send to RMS
433  */
434  rms_cmd = (struct rms_command *)disp_obj->buf;
435  rms_cmd->fxn = (rms_word) (rms_fxn);
436  rms_cmd->arg1 = (rms_word) node_env;
437  rms_cmd->arg2 = (rms_word) (ul_delete_fxn);
438  rms_cmd->data = node_get_type(hnode);
439 
440  status = send_message(disp_obj, node_get_timeout(hnode),
441  sizeof(struct rms_command),
442  &dw_arg);
443  }
444  }
445  return status;
446 }
447 
448 /*
449  * ======== disp_node_run ========
450  * purpose:
451  * Start execution of a node's execute phase, or resume execution of a node
452  * that has been suspended (via DISP_NodePause()) on the DSP.
453  */
454 int disp_node_run(struct disp_object *disp_obj,
455  struct node_object *hnode, u32 rms_fxn,
456  u32 ul_execute_fxn, nodeenv node_env)
457 {
458  u32 dw_arg;
459  struct rms_command *rms_cmd;
460  int status = 0;
461  u8 dev_type;
462 
463  status = dev_get_dev_type(disp_obj->dev_obj, &dev_type);
464 
465  if (!status) {
466 
467  if (dev_type == DSP_UNIT) {
468 
469  /*
470  * Fill in buffer to send to RMS.
471  */
472  rms_cmd = (struct rms_command *)disp_obj->buf;
473  rms_cmd->fxn = (rms_word) (rms_fxn);
474  rms_cmd->arg1 = (rms_word) node_env;
475  rms_cmd->arg2 = (rms_word) (ul_execute_fxn);
476  rms_cmd->data = node_get_type(hnode);
477 
478  status = send_message(disp_obj, node_get_timeout(hnode),
479  sizeof(struct rms_command),
480  &dw_arg);
481  }
482  }
483 
484  return status;
485 }
486 
487 /*
488  * ======== delete_disp ========
489  * purpose:
490  * Frees the resources allocated for the dispatcher.
491  */
492 static void delete_disp(struct disp_object *disp_obj)
493 {
494  int status = 0;
495  struct bridge_drv_interface *intf_fxns;
496 
497  if (disp_obj) {
498  intf_fxns = disp_obj->intf_fxns;
499 
500  /* Free Node Dispatcher resources */
501  if (disp_obj->chnl_from_dsp) {
502  /* Channel close can fail only if the channel handle
503  * is invalid. */
504  status = (*intf_fxns->chnl_close)
505  (disp_obj->chnl_from_dsp);
506  if (status) {
507  dev_dbg(bridge, "%s: Failed to close channel "
508  "from RMS: 0x%x\n", __func__, status);
509  }
510  }
511  if (disp_obj->chnl_to_dsp) {
512  status =
513  (*intf_fxns->chnl_close) (disp_obj->
514  chnl_to_dsp);
515  if (status) {
516  dev_dbg(bridge, "%s: Failed to close channel to"
517  " RMS: 0x%x\n", __func__, status);
518  }
519  }
520  kfree(disp_obj->buf);
521 
522  kfree(disp_obj);
523  }
524 }
525 
526 /*
527  * ======== fill_stream_def ========
528  * purpose:
529  * Fills stream definitions.
530  */
531 static int fill_stream_def(rms_word *pdw_buf, u32 *ptotal, u32 offset,
532  struct node_strmdef strm_def, u32 max,
533  u32 chars_in_rms_word)
534 {
535  struct rms_strm_def *strm_def_obj;
536  u32 total = *ptotal;
537  u32 name_len;
538  u32 dw_length;
539  int status = 0;
540 
541  if (total + sizeof(struct rms_strm_def) / sizeof(rms_word) >= max) {
542  status = -EPERM;
543  } else {
544  strm_def_obj = (struct rms_strm_def *)(pdw_buf + total);
545  strm_def_obj->bufsize = strm_def.buf_size;
546  strm_def_obj->nbufs = strm_def.num_bufs;
547  strm_def_obj->segid = strm_def.seg_id;
548  strm_def_obj->align = strm_def.buf_alignment;
549  strm_def_obj->timeout = strm_def.timeout;
550  }
551 
552  if (!status) {
553  /*
554  * Since we haven't added the device name yet, subtract
555  * 1 from total.
556  */
557  total += sizeof(struct rms_strm_def) / sizeof(rms_word) - 1;
558  dw_length = strlen(strm_def.sz_device) + 1;
559 
560  /* Number of RMS_WORDS needed to hold device name */
561  name_len =
562  (dw_length + chars_in_rms_word - 1) / chars_in_rms_word;
563 
564  if (total + name_len >= max) {
565  status = -EPERM;
566  } else {
567  /*
568  * Zero out last word, since the device name may not
569  * extend to completely fill this word.
570  */
571  pdw_buf[total + name_len - 1] = 0;
573  memcpy(pdw_buf + total, strm_def.sz_device, dw_length);
574  total += name_len;
575  *ptotal = total;
576  }
577  }
578 
579  return status;
580 }
581 
582 /*
583  * ======== send_message ======
584  * Send command message to RMS, get reply from RMS.
585  */
586 static int send_message(struct disp_object *disp_obj, u32 timeout,
587  u32 ul_bytes, u32 *pdw_arg)
588 {
589  struct bridge_drv_interface *intf_fxns;
590  struct chnl_object *chnl_obj;
591  u32 dw_arg = 0;
592  u8 *pbuf;
593  struct chnl_ioc chnl_ioc_obj;
594  int status = 0;
595 
596  *pdw_arg = (u32) NULL;
597  intf_fxns = disp_obj->intf_fxns;
598  chnl_obj = disp_obj->chnl_to_dsp;
599  pbuf = disp_obj->buf;
600 
601  /* Send the command */
602  status = (*intf_fxns->chnl_add_io_req) (chnl_obj, pbuf, ul_bytes, 0,
603  0L, dw_arg);
604  if (status)
605  goto func_end;
606 
607  status =
608  (*intf_fxns->chnl_get_ioc) (chnl_obj, timeout, &chnl_ioc_obj);
609  if (!status) {
610  if (!CHNL_IS_IO_COMPLETE(chnl_ioc_obj)) {
611  if (CHNL_IS_TIMED_OUT(chnl_ioc_obj))
612  status = -ETIME;
613  else
614  status = -EPERM;
615  }
616  }
617  /* Get the reply */
618  if (status)
619  goto func_end;
620 
621  chnl_obj = disp_obj->chnl_from_dsp;
622  ul_bytes = REPLYSIZE;
623  status = (*intf_fxns->chnl_add_io_req) (chnl_obj, pbuf, ul_bytes,
624  0, 0L, dw_arg);
625  if (status)
626  goto func_end;
627 
628  status =
629  (*intf_fxns->chnl_get_ioc) (chnl_obj, timeout, &chnl_ioc_obj);
630  if (!status) {
631  if (CHNL_IS_TIMED_OUT(chnl_ioc_obj)) {
632  status = -ETIME;
633  } else if (chnl_ioc_obj.byte_size < ul_bytes) {
634  /* Did not get all of the reply from the RMS */
635  status = -EPERM;
636  } else {
637  if (CHNL_IS_IO_COMPLETE(chnl_ioc_obj)) {
638  if (*((int *)chnl_ioc_obj.buf) < 0) {
639  /* Translate DSP's to kernel error */
640  status = -EREMOTEIO;
641  dev_dbg(bridge, "%s: DSP-side failed:"
642  " DSP errcode = 0x%x, Kernel "
643  "errcode = %d\n", __func__,
644  *(int *)pbuf, status);
645  }
646  *pdw_arg =
647  (((rms_word *) (chnl_ioc_obj.buf))[1]);
648  } else {
649  status = -EPERM;
650  }
651  }
652  }
653 func_end:
654  return status;
655 }