Subversion Repositories HelenOS

Rev

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