linux/drivers/misc/ibmasm/event.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2
   3/*
   4 * IBM ASM Service Processor Device Driver
   5 *
   6 * Copyright (C) IBM Corporation, 2004
   7 *
   8 * Author: Max Asböck <amax@us.ibm.com>
   9 */
  10
  11#include <linux/sched.h>
  12#include <linux/slab.h>
  13#include "ibmasm.h"
  14#include "lowlevel.h"
  15
  16/*
  17 * ASM service processor event handling routines.
  18 *
  19 * Events are signalled to the device drivers through interrupts.
  20 * They have the format of dot commands, with the type field set to
  21 * sp_event.
  22 * The driver does not interpret the events, it simply stores them in a
  23 * circular buffer.
  24 */
  25
  26static void wake_up_event_readers(struct service_processor *sp)
  27{
  28        struct event_reader *reader;
  29
  30        list_for_each_entry(reader, &sp->event_buffer->readers, node)
  31                wake_up_interruptible(&reader->wait);
  32}
  33
  34/*
  35 * receive_event
  36 * Called by the interrupt handler when a dot command of type sp_event is
  37 * received.
  38 * Store the event in the circular event buffer, wake up any sleeping
  39 * event readers.
  40 * There is no reader marker in the buffer, therefore readers are
  41 * responsible for keeping up with the writer, or they will lose events.
  42 */
  43void ibmasm_receive_event(struct service_processor *sp, void *data, unsigned int data_size)
  44{
  45        struct event_buffer *buffer = sp->event_buffer;
  46        struct ibmasm_event *event;
  47        unsigned long flags;
  48
  49        data_size = min(data_size, IBMASM_EVENT_MAX_SIZE);
  50
  51        spin_lock_irqsave(&sp->lock, flags);
  52        /* copy the event into the next slot in the circular buffer */
  53        event = &buffer->events[buffer->next_index];
  54        memcpy_fromio(event->data, data, data_size);
  55        event->data_size = data_size;
  56        event->serial_number = buffer->next_serial_number;
  57
  58        /* advance indices in the buffer */
  59        buffer->next_index = (buffer->next_index + 1) % IBMASM_NUM_EVENTS;
  60        buffer->next_serial_number++;
  61        spin_unlock_irqrestore(&sp->lock, flags);
  62
  63        wake_up_event_readers(sp);
  64}
  65
  66static inline int event_available(struct event_buffer *b, struct event_reader *r)
  67{
  68        return (r->next_serial_number < b->next_serial_number);
  69}
  70
  71/*
  72 * get_next_event
  73 * Called by event readers (initiated from user space through the file
  74 * system).
  75 * Sleeps until a new event is available.
  76 */
  77int ibmasm_get_next_event(struct service_processor *sp, struct event_reader *reader)
  78{
  79        struct event_buffer *buffer = sp->event_buffer;
  80        struct ibmasm_event *event;
  81        unsigned int index;
  82        unsigned long flags;
  83
  84        reader->cancelled = 0;
  85
  86        if (wait_event_interruptible(reader->wait,
  87                        event_available(buffer, reader) || reader->cancelled))
  88                return -ERESTARTSYS;
  89
  90        if (!event_available(buffer, reader))
  91                return 0;
  92
  93        spin_lock_irqsave(&sp->lock, flags);
  94
  95        index = buffer->next_index;
  96        event = &buffer->events[index];
  97        while (event->serial_number < reader->next_serial_number) {
  98                index = (index + 1) % IBMASM_NUM_EVENTS;
  99                event = &buffer->events[index];
 100        }
 101        memcpy(reader->data, event->data, event->data_size);
 102        reader->data_size = event->data_size;
 103        reader->next_serial_number = event->serial_number + 1;
 104
 105        spin_unlock_irqrestore(&sp->lock, flags);
 106
 107        return event->data_size;
 108}
 109
 110void ibmasm_cancel_next_event(struct event_reader *reader)
 111{
 112        reader->cancelled = 1;
 113        wake_up_interruptible(&reader->wait);
 114}
 115
 116void ibmasm_event_reader_register(struct service_processor *sp, struct event_reader *reader)
 117{
 118        unsigned long flags;
 119
 120        reader->next_serial_number = sp->event_buffer->next_serial_number;
 121        init_waitqueue_head(&reader->wait);
 122        spin_lock_irqsave(&sp->lock, flags);
 123        list_add(&reader->node, &sp->event_buffer->readers);
 124        spin_unlock_irqrestore(&sp->lock, flags);
 125}
 126
 127void ibmasm_event_reader_unregister(struct service_processor *sp, struct event_reader *reader)
 128{
 129        unsigned long flags;
 130
 131        spin_lock_irqsave(&sp->lock, flags);
 132        list_del(&reader->node);
 133        spin_unlock_irqrestore(&sp->lock, flags);
 134}
 135
 136int ibmasm_event_buffer_init(struct service_processor *sp)
 137{
 138        struct event_buffer *buffer;
 139        struct ibmasm_event *event;
 140        int i;
 141
 142        buffer = kmalloc(sizeof(struct event_buffer), GFP_KERNEL);
 143        if (!buffer)
 144                return -ENOMEM;
 145
 146        buffer->next_index = 0;
 147        buffer->next_serial_number = 1;
 148
 149        event = buffer->events;
 150        for (i=0; i<IBMASM_NUM_EVENTS; i++, event++)
 151                event->serial_number = 0;
 152
 153        INIT_LIST_HEAD(&buffer->readers);
 154
 155        sp->event_buffer = buffer;
 156
 157        return 0;
 158}
 159
 160void ibmasm_event_buffer_exit(struct service_processor *sp)
 161{
 162        kfree(sp->event_buffer);
 163}
 164