PageRenderTime 39ms CodeModel.GetById 14ms app.highlight 20ms RepoModel.GetById 2ms app.codeStats 0ms

/drivers/misc/ibmasm/event.c

http://github.com/mirrors/linux
C | 163 lines | 97 code | 32 blank | 34 comment | 6 complexity | 366072e165fcafa579695805987a0320 MD5 | raw file
  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}