2007-12-19 03:27:18 +03:00
/*
* Copyright 2007 , Mattias Nissler < mattias . nissler @ gmx . de >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/spinlock.h>
# include <linux/poll.h>
# include <linux/netdevice.h>
# include <linux/types.h>
# include <linux/skbuff.h>
# include <net/mac80211.h>
2008-04-08 23:14:40 +04:00
# include "rate.h"
2007-12-19 03:27:18 +03:00
# include "rc80211_pid.h"
static void rate_control_pid_event ( struct rc_pid_event_buffer * buf ,
enum rc_pid_event_type type ,
union rc_pid_event_data * data )
{
struct rc_pid_event * ev ;
unsigned long status ;
spin_lock_irqsave ( & buf - > lock , status ) ;
ev = & ( buf - > ring [ buf - > next_entry ] ) ;
buf - > next_entry = ( buf - > next_entry + 1 ) % RC_PID_EVENT_RING_SIZE ;
ev - > timestamp = jiffies ;
ev - > id = buf - > ev_count + + ;
ev - > type = type ;
ev - > data = * data ;
spin_unlock_irqrestore ( & buf - > lock , status ) ;
wake_up_all ( & buf - > waitqueue ) ;
}
void rate_control_pid_event_tx_status ( struct rc_pid_event_buffer * buf ,
2008-05-15 14:55:29 +04:00
struct ieee80211_tx_info * stat )
2007-12-19 03:27:18 +03:00
{
union rc_pid_event_data evd ;
2008-10-21 14:40:02 +04:00
evd . flags = stat - > flags ;
2008-05-15 14:55:29 +04:00
memcpy ( & evd . tx_status , stat , sizeof ( struct ieee80211_tx_info ) ) ;
2007-12-19 03:27:18 +03:00
rate_control_pid_event ( buf , RC_PID_EVENT_TYPE_TX_STATUS , & evd ) ;
}
void rate_control_pid_event_rate_change ( struct rc_pid_event_buffer * buf ,
int index , int rate )
{
union rc_pid_event_data evd ;
evd . index = index ;
evd . rate = rate ;
rate_control_pid_event ( buf , RC_PID_EVENT_TYPE_RATE_CHANGE , & evd ) ;
}
void rate_control_pid_event_tx_rate ( struct rc_pid_event_buffer * buf ,
int index , int rate )
{
union rc_pid_event_data evd ;
evd . index = index ;
evd . rate = rate ;
rate_control_pid_event ( buf , RC_PID_EVENT_TYPE_TX_RATE , & evd ) ;
}
void rate_control_pid_event_pf_sample ( struct rc_pid_event_buffer * buf ,
s32 pf_sample , s32 prop_err ,
s32 int_err , s32 der_err )
{
union rc_pid_event_data evd ;
evd . pf_sample = pf_sample ;
evd . prop_err = prop_err ;
evd . int_err = int_err ;
evd . der_err = der_err ;
rate_control_pid_event ( buf , RC_PID_EVENT_TYPE_PF_SAMPLE , & evd ) ;
}
static int rate_control_pid_events_open ( struct inode * inode , struct file * file )
{
struct rc_pid_sta_info * sinfo = inode - > i_private ;
struct rc_pid_event_buffer * events = & sinfo - > events ;
struct rc_pid_events_file_info * file_info ;
2008-05-07 07:42:28 +04:00
unsigned long status ;
2007-12-19 03:27:18 +03:00
/* Allocate a state struct */
file_info = kmalloc ( sizeof ( * file_info ) , GFP_KERNEL ) ;
if ( file_info = = NULL )
return - ENOMEM ;
spin_lock_irqsave ( & events - > lock , status ) ;
file_info - > next_entry = events - > next_entry ;
file_info - > events = events ;
spin_unlock_irqrestore ( & events - > lock , status ) ;
file - > private_data = file_info ;
return 0 ;
}
static int rate_control_pid_events_release ( struct inode * inode ,
struct file * file )
{
struct rc_pid_events_file_info * file_info = file - > private_data ;
kfree ( file_info ) ;
return 0 ;
}
static unsigned int rate_control_pid_events_poll ( struct file * file ,
poll_table * wait )
{
struct rc_pid_events_file_info * file_info = file - > private_data ;
poll_wait ( file , & file_info - > events - > waitqueue , wait ) ;
return POLLIN | POLLRDNORM ;
}
# define RC_PID_PRINT_BUF_SIZE 64
static ssize_t rate_control_pid_events_read ( struct file * file , char __user * buf ,
size_t length , loff_t * offset )
{
struct rc_pid_events_file_info * file_info = file - > private_data ;
struct rc_pid_event_buffer * events = file_info - > events ;
struct rc_pid_event * ev ;
char pb [ RC_PID_PRINT_BUF_SIZE ] ;
int ret ;
int p ;
2008-05-07 07:42:28 +04:00
unsigned long status ;
2007-12-19 03:27:18 +03:00
/* Check if there is something to read. */
if ( events - > next_entry = = file_info - > next_entry ) {
if ( file - > f_flags & O_NONBLOCK )
return - EAGAIN ;
/* Wait */
ret = wait_event_interruptible ( events - > waitqueue ,
events - > next_entry ! = file_info - > next_entry ) ;
if ( ret )
return ret ;
}
/* Write out one event per call. I don't care whether it's a little
* inefficient , this is debugging code anyway . */
spin_lock_irqsave ( & events - > lock , status ) ;
/* Get an event */
ev = & ( events - > ring [ file_info - > next_entry ] ) ;
file_info - > next_entry = ( file_info - > next_entry + 1 ) %
RC_PID_EVENT_RING_SIZE ;
/* Print information about the event. Note that userpace needs to
* provide large enough buffers . */
length = length < RC_PID_PRINT_BUF_SIZE ?
length : RC_PID_PRINT_BUF_SIZE ;
p = snprintf ( pb , length , " %u %lu " , ev - > id , ev - > timestamp ) ;
switch ( ev - > type ) {
case RC_PID_EVENT_TYPE_TX_STATUS :
p + = snprintf ( pb + p , length - p , " tx_status %u %u " ,
2008-10-21 14:40:02 +04:00
! ( ev - > data . flags & IEEE80211_TX_STAT_ACK ) ,
ev - > data . tx_status . status . rates [ 0 ] . idx ) ;
2007-12-19 03:27:18 +03:00
break ;
case RC_PID_EVENT_TYPE_RATE_CHANGE :
p + = snprintf ( pb + p , length - p , " rate_change %d %d " ,
ev - > data . index , ev - > data . rate ) ;
break ;
case RC_PID_EVENT_TYPE_TX_RATE :
p + = snprintf ( pb + p , length - p , " tx_rate %d %d " ,
ev - > data . index , ev - > data . rate ) ;
break ;
case RC_PID_EVENT_TYPE_PF_SAMPLE :
p + = snprintf ( pb + p , length - p ,
" pf_sample %d %d %d %d " ,
ev - > data . pf_sample , ev - > data . prop_err ,
ev - > data . int_err , ev - > data . der_err ) ;
break ;
}
p + = snprintf ( pb + p , length - p , " \n " ) ;
spin_unlock_irqrestore ( & events - > lock , status ) ;
if ( copy_to_user ( buf , pb , p ) )
return - EFAULT ;
return p ;
}
# undef RC_PID_PRINT_BUF_SIZE
2007-12-24 00:05:25 +03:00
static struct file_operations rc_pid_fop_events = {
2007-12-19 03:27:18 +03:00
. owner = THIS_MODULE ,
. read = rate_control_pid_events_read ,
. poll = rate_control_pid_events_poll ,
. open = rate_control_pid_events_open ,
. release = rate_control_pid_events_release ,
} ;
void rate_control_pid_add_sta_debugfs ( void * priv , void * priv_sta ,
struct dentry * dir )
{
struct rc_pid_sta_info * spinfo = priv_sta ;
spinfo - > events_entry = debugfs_create_file ( " rc_pid_events " , S_IRUGO ,
dir , spinfo ,
& rc_pid_fop_events ) ;
}
void rate_control_pid_remove_sta_debugfs ( void * priv , void * priv_sta )
{
struct rc_pid_sta_info * spinfo = priv_sta ;
debugfs_remove ( spinfo - > events_entry ) ;
}