2005-04-17 02:20:36 +04:00
/*
* IBM ASM Service Processor Device Driver
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*
* Copyright ( C ) IBM Corporation , 2004
*
* Author : Max Asb <EFBFBD> ck < amax @ us . ibm . com >
*
*/
# include "ibmasm.h"
2005-06-22 04:16:33 +04:00
# include "lowlevel.h"
2005-04-17 02:20:36 +04:00
/*
* ASM service processor event handling routines .
*
* Events are signalled to the device drivers through interrupts .
* They have the format of dot commands , with the type field set to
* sp_event .
* The driver does not interpret the events , it simply stores them in a
* circular buffer .
*/
static void wake_up_event_readers ( struct service_processor * sp )
{
struct event_reader * reader ;
list_for_each_entry ( reader , & sp - > event_buffer - > readers , node )
wake_up_interruptible ( & reader - > wait ) ;
}
/**
* receive_event
* Called by the interrupt handler when a dot command of type sp_event is
* received .
* Store the event in the circular event buffer , wake up any sleeping
* event readers .
* There is no reader marker in the buffer , therefore readers are
* responsible for keeping up with the writer , or they will loose events .
*/
void ibmasm_receive_event ( struct service_processor * sp , void * data , unsigned int data_size )
{
struct event_buffer * buffer = sp - > event_buffer ;
struct ibmasm_event * event ;
unsigned long flags ;
data_size = min ( data_size , IBMASM_EVENT_MAX_SIZE ) ;
spin_lock_irqsave ( & sp - > lock , flags ) ;
/* copy the event into the next slot in the circular buffer */
event = & buffer - > events [ buffer - > next_index ] ;
2005-06-22 04:16:33 +04:00
memcpy_fromio ( event - > data , data , data_size ) ;
2005-04-17 02:20:36 +04:00
event - > data_size = data_size ;
event - > serial_number = buffer - > next_serial_number ;
/* advance indices in the buffer */
buffer - > next_index = ( buffer - > next_index + 1 ) % IBMASM_NUM_EVENTS ;
buffer - > next_serial_number + + ;
spin_unlock_irqrestore ( & sp - > lock , flags ) ;
wake_up_event_readers ( sp ) ;
}
static inline int event_available ( struct event_buffer * b , struct event_reader * r )
{
return ( r - > next_serial_number < b - > next_serial_number ) ;
}
/**
* get_next_event
* Called by event readers ( initiated from user space through the file
* system ) .
* Sleeps until a new event is available .
*/
int ibmasm_get_next_event ( struct service_processor * sp , struct event_reader * reader )
{
struct event_buffer * buffer = sp - > event_buffer ;
struct ibmasm_event * event ;
unsigned int index ;
unsigned long flags ;
2005-06-22 04:16:33 +04:00
reader - > cancelled = 0 ;
if ( wait_event_interruptible ( reader - > wait ,
event_available ( buffer , reader ) | | reader - > cancelled ) )
2005-04-17 02:20:36 +04:00
return - ERESTARTSYS ;
if ( ! event_available ( buffer , reader ) )
return 0 ;
spin_lock_irqsave ( & sp - > lock , flags ) ;
index = buffer - > next_index ;
event = & buffer - > events [ index ] ;
while ( event - > serial_number < reader - > next_serial_number ) {
index = ( index + 1 ) % IBMASM_NUM_EVENTS ;
event = & buffer - > events [ index ] ;
}
memcpy ( reader - > data , event - > data , event - > data_size ) ;
reader - > data_size = event - > data_size ;
reader - > next_serial_number = event - > serial_number + 1 ;
spin_unlock_irqrestore ( & sp - > lock , flags ) ;
return event - > data_size ;
}
2005-06-22 04:16:33 +04:00
void ibmasm_cancel_next_event ( struct event_reader * reader )
{
reader - > cancelled = 1 ;
wake_up_interruptible ( & reader - > wait ) ;
}
2005-04-17 02:20:36 +04:00
void ibmasm_event_reader_register ( struct service_processor * sp , struct event_reader * reader )
{
unsigned long flags ;
reader - > next_serial_number = sp - > event_buffer - > next_serial_number ;
init_waitqueue_head ( & reader - > wait ) ;
spin_lock_irqsave ( & sp - > lock , flags ) ;
list_add ( & reader - > node , & sp - > event_buffer - > readers ) ;
spin_unlock_irqrestore ( & sp - > lock , flags ) ;
}
void ibmasm_event_reader_unregister ( struct service_processor * sp , struct event_reader * reader )
{
unsigned long flags ;
spin_lock_irqsave ( & sp - > lock , flags ) ;
list_del ( & reader - > node ) ;
spin_unlock_irqrestore ( & sp - > lock , flags ) ;
}
int ibmasm_event_buffer_init ( struct service_processor * sp )
{
struct event_buffer * buffer ;
struct ibmasm_event * event ;
int i ;
buffer = kmalloc ( sizeof ( struct event_buffer ) , GFP_KERNEL ) ;
if ( ! buffer )
return 1 ;
buffer - > next_index = 0 ;
buffer - > next_serial_number = 1 ;
event = buffer - > events ;
for ( i = 0 ; i < IBMASM_NUM_EVENTS ; i + + , event + + )
event - > serial_number = 0 ;
INIT_LIST_HEAD ( & buffer - > readers ) ;
sp - > event_buffer = buffer ;
return 0 ;
}
void ibmasm_event_buffer_exit ( struct service_processor * sp )
{
kfree ( sp - > event_buffer ) ;
}