Subversion Repositories HelenOS

Rev

Rev 3665 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright (c) 2006 Ondrej Palkovsky
  3.  * Copyright (c) 2006 Jakub Jermar
  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. /** @addtogroup genericipc
  31.  * @{
  32.  */
  33. /**
  34.  * @file
  35.  * @brief IRQ notification framework.
  36.  *
  37.  * This framework allows applications to register to receive a notification
  38.  * when interrupt is detected. The application may provide a simple 'top-half'
  39.  * handler as part of its registration, which can perform simple operations
  40.  * (read/write port/memory, add information to notification ipc message).
  41.  *
  42.  * The structure of a notification message is as follows:
  43.  * - METHOD: method as registered by the SYS_IPC_REGISTER_IRQ syscall
  44.  * - ARG1: payload modified by a 'top-half' handler
  45.  * - ARG2: payload modified by a 'top-half' handler
  46.  * - ARG3: payload modified by a 'top-half' handler
  47.  * - in_phone_hash: interrupt counter (may be needed to assure correct order
  48.  *         in multithreaded drivers)
  49.  */
  50.  
  51. #include <arch.h>
  52. #include <arch/asm.h>
  53. #include <mm/slab.h>
  54. #include <errno.h>
  55. #include <ddi/irq.h>
  56. #include <ipc/ipc.h>
  57. #include <ipc/irq.h>
  58. #include <syscall/copy.h>
  59. #include <console/console.h>
  60. #include <print.h>
  61.  
  62. /** Execute code associated with IRQ notification.
  63.  *
  64.  * @param call      Notification call.
  65.  * @param code      Top-half pseudocode.
  66.  */
  67. static void code_execute(call_t *call, irq_code_t *code)
  68. {
  69.     unsigned int i;
  70.     unative_t dstval = 0;
  71.    
  72.     if (!code)
  73.         return;
  74.    
  75.     for (i = 0; i < code->cmdcount; i++) {
  76.         switch (code->cmds[i].cmd) {
  77.         case CMD_MEM_READ_1:
  78.             dstval = *((uint8_t *) code->cmds[i].addr);
  79.             break;
  80.         case CMD_MEM_READ_2:
  81.             dstval = *((uint16_t *) code->cmds[i].addr);
  82.             break;
  83.         case CMD_MEM_READ_4:
  84.             dstval = *((uint32_t *) code->cmds[i].addr);
  85.             break;
  86.         case CMD_MEM_READ_8:
  87.             dstval = *((uint64_t *) code->cmds[i].addr);
  88.             break;
  89.         case CMD_MEM_WRITE_1:
  90.             *((uint8_t *) code->cmds[i].addr) = code->cmds[i].value;
  91.             break;
  92.         case CMD_MEM_WRITE_2:
  93.             *((uint16_t *) code->cmds[i].addr) =
  94.                 code->cmds[i].value;
  95.             break;
  96.         case CMD_MEM_WRITE_4:
  97.             *((uint32_t *) code->cmds[i].addr) =
  98.                 code->cmds[i].value;
  99.             break;
  100.         case CMD_MEM_WRITE_8:
  101.             *((uint64_t *) code->cmds[i].addr) =
  102.                 code->cmds[i].value;
  103.             break;
  104. #if defined(ia32) || defined(amd64) || defined(ia64)
  105.         case CMD_PORT_READ_1:
  106.             dstval = inb((long) code->cmds[i].addr);
  107.             break;
  108.         case CMD_PORT_WRITE_1:
  109.             outb((long) code->cmds[i].addr, code->cmds[i].value);
  110.             break;
  111. #endif
  112. #if defined(ia64) && defined(SKI)
  113.         case CMD_IA64_GETCHAR:
  114.             dstval = _getc(&ski_uconsole);
  115.             break;
  116. #endif
  117. #if defined(ppc32)
  118.         case CMD_PPC32_GETCHAR:
  119.             dstval = cuda_get_scancode();
  120.             break;
  121. #endif
  122.         default:
  123.             break;
  124.         }
  125.         if (code->cmds[i].dstarg && code->cmds[i].dstarg <
  126.             IPC_CALL_LEN) {
  127.             call->data.args[code->cmds[i].dstarg] = dstval;
  128.         }
  129.     }
  130. }
  131.  
  132. /** Free top-half pseudocode.
  133.  *
  134.  * @param code      Pointer to the top-half pseudocode.
  135.  */
  136. static void code_free(irq_code_t *code)
  137. {
  138.     if (code) {
  139.         free(code->cmds);
  140.         free(code);
  141.     }
  142. }
  143.  
  144. /** Copy top-half pseudocode from userspace into the kernel.
  145.  *
  146.  * @param ucode     Userspace address of the top-half pseudocode.
  147.  *
  148.  * @return      Kernel address of the copied pseudocode.
  149.  */
  150. static irq_code_t *code_from_uspace(irq_code_t *ucode)
  151. {
  152.     irq_code_t *code;
  153.     irq_cmd_t *ucmds;
  154.     int rc;
  155.  
  156.     code = malloc(sizeof(*code), 0);
  157.     rc = copy_from_uspace(code, ucode, sizeof(*code));
  158.     if (rc != 0) {
  159.         free(code);
  160.         return NULL;
  161.     }
  162.    
  163.     if (code->cmdcount > IRQ_MAX_PROG_SIZE) {
  164.         free(code);
  165.         return NULL;
  166.     }
  167.     ucmds = code->cmds;
  168.     code->cmds = malloc(sizeof(code->cmds[0]) * code->cmdcount, 0);
  169.     rc = copy_from_uspace(code->cmds, ucmds,
  170.         sizeof(code->cmds[0]) * code->cmdcount);
  171.     if (rc != 0) {
  172.         free(code->cmds);
  173.         free(code);
  174.         return NULL;
  175.     }
  176.  
  177.     return code;
  178. }
  179.  
  180. /** Unregister task from IRQ notification.
  181.  *
  182.  * @param box       Answerbox associated with the notification.
  183.  * @param inr       IRQ number.
  184.  * @param devno     Device number.
  185.  */
  186. void ipc_irq_unregister(answerbox_t *box, inr_t inr, devno_t devno)
  187. {
  188.     ipl_t ipl;
  189.     irq_t *irq;
  190.  
  191.     ipl = interrupts_disable();
  192.     irq = irq_find_and_lock(inr, devno);
  193.     if (irq) {
  194.         if (irq->notif_cfg.answerbox == box) {
  195.             code_free(irq->notif_cfg.code);
  196.             irq->notif_cfg.notify = false;
  197.             irq->notif_cfg.answerbox = NULL;
  198.             irq->notif_cfg.code = NULL;
  199.             irq->notif_cfg.method = 0;
  200.             irq->notif_cfg.counter = 0;
  201.  
  202.             spinlock_lock(&box->irq_lock);
  203.             list_remove(&irq->notif_cfg.link);
  204.             spinlock_unlock(&box->irq_lock);
  205.            
  206.             spinlock_unlock(&irq->lock);
  207.         }
  208.     }
  209.     interrupts_restore(ipl);
  210. }
  211.  
  212. /** Register an answerbox as a receiving end for IRQ notifications.
  213.  *
  214.  * @param box       Receiving answerbox.
  215.  * @param inr       IRQ number.
  216.  * @param devno     Device number.
  217.  * @param method    Method to be associated with the notification.
  218.  * @param ucode     Uspace pointer to top-half pseudocode.
  219.  *
  220.  * @return      EBADMEM, ENOENT or EEXISTS on failure or 0 on success.
  221.  */
  222. int ipc_irq_register(answerbox_t *box, inr_t inr, devno_t devno,
  223.     unative_t method, irq_code_t *ucode)
  224. {
  225.     ipl_t ipl;
  226.     irq_code_t *code;
  227.     irq_t *irq;
  228.  
  229.     if (ucode) {
  230.         code = code_from_uspace(ucode);
  231.         if (!code)
  232.             return EBADMEM;
  233.     } else {
  234.         code = NULL;
  235.     }
  236.  
  237.     ipl = interrupts_disable();
  238.     irq = irq_find_and_lock(inr, devno);
  239.     if (!irq) {
  240.         interrupts_restore(ipl);
  241.         code_free(code);
  242.         return ENOENT;
  243.     }
  244.    
  245.     if (irq->notif_cfg.answerbox) {
  246.         spinlock_unlock(&irq->lock);
  247.         interrupts_restore(ipl);
  248.         code_free(code);
  249.         return EEXISTS;
  250.     }
  251.    
  252.     irq->notif_cfg.notify = true;
  253.     irq->notif_cfg.answerbox = box;
  254.     irq->notif_cfg.method = method;
  255.     irq->notif_cfg.code = code;
  256.     irq->notif_cfg.counter = 0;
  257.  
  258.     spinlock_lock(&box->irq_lock);
  259.     list_append(&irq->notif_cfg.link, &box->irq_head);
  260.     spinlock_unlock(&box->irq_lock);
  261.  
  262.     spinlock_unlock(&irq->lock);
  263.     interrupts_restore(ipl);
  264.  
  265.     return 0;
  266. }
  267.  
  268. /** Add a call to the proper answerbox queue.
  269.  *
  270.  * Assume irq->lock is locked.
  271.  *
  272.  * @param irq       IRQ structure referencing the target answerbox.
  273.  * @param call      IRQ notification call.
  274.  */
  275. static void send_call(irq_t *irq, call_t *call)
  276. {
  277.     spinlock_lock(&irq->notif_cfg.answerbox->irq_lock);
  278.     list_append(&call->link, &irq->notif_cfg.answerbox->irq_notifs);
  279.     spinlock_unlock(&irq->notif_cfg.answerbox->irq_lock);
  280.        
  281.     waitq_wakeup(&irq->notif_cfg.answerbox->wq, WAKEUP_FIRST);
  282. }
  283.  
  284. /** Send notification message.
  285.  *
  286.  * @param irq       IRQ structure.
  287.  * @param a1        Driver-specific payload argument.
  288.  * @param a2        Driver-specific payload argument.
  289.  * @param a3        Driver-specific payload argument.
  290.  * @param a4        Driver-specific payload argument.
  291.  * @param a5        Driver-specific payload argument.
  292.  */
  293. void ipc_irq_send_msg(irq_t *irq, unative_t a1, unative_t a2, unative_t a3,
  294.     unative_t a4, unative_t a5)
  295. {
  296.     call_t *call;
  297.  
  298.     spinlock_lock(&irq->lock);
  299.  
  300.     if (irq->notif_cfg.answerbox) {
  301.         call = ipc_call_alloc(FRAME_ATOMIC);
  302.         if (!call) {
  303.             spinlock_unlock(&irq->lock);
  304.             return;
  305.         }
  306.         call->flags |= IPC_CALL_NOTIF;
  307.         IPC_SET_METHOD(call->data, irq->notif_cfg.method);
  308.         IPC_SET_ARG1(call->data, a1);
  309.         IPC_SET_ARG2(call->data, a2);
  310.         IPC_SET_ARG3(call->data, a3);
  311.         IPC_SET_ARG4(call->data, a4);
  312.         IPC_SET_ARG5(call->data, a5);
  313.         /* Put a counter to the message */
  314.         call->priv = ++irq->notif_cfg.counter;
  315.        
  316.         send_call(irq, call);
  317.     }
  318.     spinlock_unlock(&irq->lock);
  319. }
  320.  
  321. /** Notify a task that an IRQ had occurred.
  322.  *
  323.  * We expect interrupts to be disabled and the irq->lock already held.
  324.  *
  325.  * @param irq       IRQ structure.
  326.  */
  327. void ipc_irq_send_notif(irq_t *irq)
  328. {
  329.     call_t *call;
  330.  
  331.     ASSERT(irq);
  332.  
  333.     if (irq->notif_cfg.answerbox) {
  334.         call = ipc_call_alloc(FRAME_ATOMIC);
  335.         if (!call) {
  336.             return;
  337.         }
  338.         call->flags |= IPC_CALL_NOTIF;
  339.         /* Put a counter to the message */
  340.         call->priv = ++irq->notif_cfg.counter;
  341.         /* Set up args */
  342.         IPC_SET_METHOD(call->data, irq->notif_cfg.method);
  343.  
  344.         /* Execute code to handle irq */
  345.         code_execute(call, irq->notif_cfg.code);
  346.        
  347.         send_call(irq, call);
  348.     }
  349. }
  350.  
  351. /** Disconnect all IRQ notifications from an answerbox.
  352.  *
  353.  * This function is effective because the answerbox contains
  354.  * list of all irq_t structures that are registered to
  355.  * send notifications to it.
  356.  *
  357.  * @param box       Answerbox for which we want to carry out the cleanup.
  358.  */
  359. void ipc_irq_cleanup(answerbox_t *box)
  360. {
  361.     ipl_t ipl;
  362.    
  363. loop:
  364.     ipl = interrupts_disable();
  365.     spinlock_lock(&box->irq_lock);
  366.    
  367.     while (box->irq_head.next != &box->irq_head) {
  368.         link_t *cur = box->irq_head.next;
  369.         irq_t *irq;
  370.         DEADLOCK_PROBE_INIT(p_irqlock);
  371.        
  372.         irq = list_get_instance(cur, irq_t, notif_cfg.link);
  373.         if (!spinlock_trylock(&irq->lock)) {
  374.             /*
  375.              * Avoid deadlock by trying again.
  376.              */
  377.             spinlock_unlock(&box->irq_lock);
  378.             interrupts_restore(ipl);
  379.             DEADLOCK_PROBE(p_irqlock, DEADLOCK_THRESHOLD);
  380.             goto loop;
  381.         }
  382.        
  383.         ASSERT(irq->notif_cfg.answerbox == box);
  384.        
  385.         list_remove(&irq->notif_cfg.link);
  386.        
  387.         /*
  388.          * Don't forget to free any top-half pseudocode.
  389.          */
  390.         code_free(irq->notif_cfg.code);
  391.        
  392.         irq->notif_cfg.notify = false;
  393.         irq->notif_cfg.answerbox = NULL;
  394.         irq->notif_cfg.code = NULL;
  395.         irq->notif_cfg.method = 0;
  396.         irq->notif_cfg.counter = 0;
  397.  
  398.         spinlock_unlock(&irq->lock);
  399.     }
  400.    
  401.     spinlock_unlock(&box->irq_lock);
  402.     interrupts_restore(ipl);
  403. }
  404.  
  405. /** @}
  406.  */
  407.