2005-04-17 02:20:36 +04:00
/*
* Device driver for the Apple Desktop Bus
* and the / dev / adb device on macintoshes .
*
* Copyright ( C ) 1996 Paul Mackerras .
*
* Modified to declare controllers as structures , added
* client notification of bus reset and handles PowerBook
* sleep , by Benjamin Herrenschmidt .
*
* To do :
*
* - / sys / bus / adb to list the devices and infos
* - more / dev / adb to allow userland to receive the
* flow of auto - polling datas from a given device .
* - move bus probe to a kernel thread
*/
# include <linux/config.h>
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/module.h>
# include <linux/fs.h>
# include <linux/mm.h>
# include <linux/sched.h>
# include <linux/smp_lock.h>
# include <linux/adb.h>
# include <linux/cuda.h>
# include <linux/pmu.h>
# include <linux/notifier.h>
# include <linux/wait.h>
# include <linux/init.h>
# include <linux/delay.h>
# include <linux/spinlock.h>
# include <linux/completion.h>
# include <linux/device.h>
# include <linux/devfs_fs_kernel.h>
# include <asm/uaccess.h>
# include <asm/semaphore.h>
# ifdef CONFIG_PPC
# include <asm/prom.h>
# endif
EXPORT_SYMBOL ( adb_controller ) ;
EXPORT_SYMBOL ( adb_client_list ) ;
extern struct adb_driver via_macii_driver ;
extern struct adb_driver via_maciisi_driver ;
extern struct adb_driver via_cuda_driver ;
extern struct adb_driver adb_iop_driver ;
extern struct adb_driver via_pmu_driver ;
extern struct adb_driver macio_adb_driver ;
static struct adb_driver * adb_driver_list [ ] = {
# ifdef CONFIG_ADB_MACII
& via_macii_driver ,
# endif
# ifdef CONFIG_ADB_MACIISI
& via_maciisi_driver ,
# endif
# ifdef CONFIG_ADB_CUDA
& via_cuda_driver ,
# endif
# ifdef CONFIG_ADB_IOP
& adb_iop_driver ,
# endif
# if defined(CONFIG_ADB_PMU) || defined(CONFIG_ADB_PMU68K)
& via_pmu_driver ,
# endif
# ifdef CONFIG_ADB_MACIO
& macio_adb_driver ,
# endif
NULL
} ;
2005-03-23 21:01:41 +03:00
static struct class * adb_dev_class ;
2005-04-17 02:20:36 +04:00
struct adb_driver * adb_controller ;
struct notifier_block * adb_client_list = NULL ;
static int adb_got_sleep ;
static int adb_inited ;
static pid_t adb_probe_task_pid ;
static DECLARE_MUTEX ( adb_probe_mutex ) ;
static struct completion adb_probe_task_comp ;
static int sleepy_trackpad ;
static int autopoll_devs ;
int __adb_probe_sync ;
2005-06-28 01:36:34 +04:00
# ifdef CONFIG_PM
2005-04-17 02:20:36 +04:00
static int adb_notify_sleep ( struct pmu_sleep_notifier * self , int when ) ;
static struct pmu_sleep_notifier adb_sleep_notifier = {
adb_notify_sleep ,
SLEEP_LEVEL_ADB ,
} ;
# endif
static int adb_scan_bus ( void ) ;
static int do_adb_reset_bus ( void ) ;
static void adbdev_init ( void ) ;
static int try_handler_change ( int , int ) ;
static struct adb_handler {
void ( * handler ) ( unsigned char * , int , struct pt_regs * , int ) ;
int original_address ;
int handler_id ;
int busy ;
} adb_handler [ 16 ] ;
/*
* The adb_handler_sem mutex protects all accesses to the original_address
* and handler_id fields of adb_handler [ i ] for all i , and changes to the
* handler field .
* Accesses to the handler field are protected by the adb_handler_lock
* rwlock . It is held across all calls to any handler , so that by the
* time adb_unregister returns , we know that the old handler isn ' t being
* called .
*/
static DECLARE_MUTEX ( adb_handler_sem ) ;
static DEFINE_RWLOCK ( adb_handler_lock ) ;
#if 0
static void printADBreply ( struct adb_request * req )
{
int i ;
printk ( " adb reply (%d) " , req - > reply_len ) ;
for ( i = 0 ; i < req - > reply_len ; i + + )
printk ( " %x " , req - > reply [ i ] ) ;
printk ( " \n " ) ;
}
# endif
static __inline__ void adb_wait_ms ( unsigned int ms )
{
if ( current - > pid & & adb_probe_task_pid & &
adb_probe_task_pid = = current - > pid )
msleep ( ms ) ;
else
mdelay ( ms ) ;
}
static int adb_scan_bus ( void )
{
int i , highFree = 0 , noMovement ;
int devmask = 0 ;
struct adb_request req ;
/* assumes adb_handler[] is all zeroes at this point */
for ( i = 1 ; i < 16 ; i + + ) {
/* see if there is anything at address i */
adb_request ( & req , NULL , ADBREQ_SYNC | ADBREQ_REPLY , 1 ,
( i < < 4 ) | 0xf ) ;
if ( req . reply_len > 1 )
/* one or more devices at this address */
adb_handler [ i ] . original_address = i ;
else if ( i > highFree )
highFree = i ;
}
/* Note we reset noMovement to 0 each time we move a device */
for ( noMovement = 1 ; noMovement < 2 & & highFree > 0 ; noMovement + + ) {
for ( i = 1 ; i < 16 ; i + + ) {
if ( adb_handler [ i ] . original_address = = 0 )
continue ;
/*
* Send a " talk register 3 " command to address i
* to provoke a collision if there is more than
* one device at this address .
*/
adb_request ( & req , NULL , ADBREQ_SYNC | ADBREQ_REPLY , 1 ,
( i < < 4 ) | 0xf ) ;
/*
* Move the device ( s ) which didn ' t detect a
* collision to address ` highFree ' . Hopefully
* this only moves one device .
*/
adb_request ( & req , NULL , ADBREQ_SYNC , 3 ,
( i < < 4 ) | 0xb , ( highFree | 0x60 ) , 0xfe ) ;
/*
* See if anybody actually moved . This is suggested
* by HW TechNote 01 :
*
* http : //developer.apple.com/technotes/hw/hw_01.html
*/
adb_request ( & req , NULL , ADBREQ_SYNC | ADBREQ_REPLY , 1 ,
( highFree < < 4 ) | 0xf ) ;
if ( req . reply_len < = 1 ) continue ;
/*
* Test whether there are any device ( s ) left
* at address i .
*/
adb_request ( & req , NULL , ADBREQ_SYNC | ADBREQ_REPLY , 1 ,
( i < < 4 ) | 0xf ) ;
if ( req . reply_len > 1 ) {
/*
* There are still one or more devices
* left at address i . Register the one ( s )
* we moved to ` highFree ' , and find a new
* value for highFree .
*/
adb_handler [ highFree ] . original_address =
adb_handler [ i ] . original_address ;
while ( highFree > 0 & &
adb_handler [ highFree ] . original_address )
highFree - - ;
if ( highFree < = 0 )
break ;
noMovement = 0 ;
}
else {
/*
* No devices left at address i ; move the
* one ( s ) we moved to ` highFree ' back to i .
*/
adb_request ( & req , NULL , ADBREQ_SYNC , 3 ,
( highFree < < 4 ) | 0xb ,
( i | 0x60 ) , 0xfe ) ;
}
}
}
/* Now fill in the handler_id field of the adb_handler entries. */
printk ( KERN_DEBUG " adb devices: " ) ;
for ( i = 1 ; i < 16 ; i + + ) {
if ( adb_handler [ i ] . original_address = = 0 )
continue ;
adb_request ( & req , NULL , ADBREQ_SYNC | ADBREQ_REPLY , 1 ,
( i < < 4 ) | 0xf ) ;
adb_handler [ i ] . handler_id = req . reply [ 2 ] ;
printk ( " [%d]: %d %x " , i , adb_handler [ i ] . original_address ,
adb_handler [ i ] . handler_id ) ;
devmask | = 1 < < i ;
}
printk ( " \n " ) ;
return devmask ;
}
/*
* This kernel task handles ADB probing . It dies once probing is
* completed .
*/
static int
adb_probe_task ( void * x )
{
sigset_t blocked ;
strcpy ( current - > comm , " kadbprobe " ) ;
sigfillset ( & blocked ) ;
sigprocmask ( SIG_BLOCK , & blocked , NULL ) ;
flush_signals ( current ) ;
printk ( KERN_INFO " adb: starting probe task... \n " ) ;
do_adb_reset_bus ( ) ;
printk ( KERN_INFO " adb: finished probe task... \n " ) ;
adb_probe_task_pid = 0 ;
up ( & adb_probe_mutex ) ;
return 0 ;
}
static void
__adb_probe_task ( void * data )
{
adb_probe_task_pid = kernel_thread ( adb_probe_task , NULL , SIGCHLD | CLONE_KERNEL ) ;
}
static DECLARE_WORK ( adb_reset_work , __adb_probe_task , NULL ) ;
int
adb_reset_bus ( void )
{
if ( __adb_probe_sync ) {
do_adb_reset_bus ( ) ;
return 0 ;
}
down ( & adb_probe_mutex ) ;
schedule_work ( & adb_reset_work ) ;
return 0 ;
}
int __init adb_init ( void )
{
struct adb_driver * driver ;
int i ;
# ifdef CONFIG_PPC32
if ( ( _machine ! = _MACH_chrp ) & & ( _machine ! = _MACH_Pmac ) )
return 0 ;
# endif
# ifdef CONFIG_MAC
if ( ! MACH_IS_MAC )
return 0 ;
# endif
/* xmon may do early-init */
if ( adb_inited )
return 0 ;
adb_inited = 1 ;
adb_controller = NULL ;
i = 0 ;
while ( ( driver = adb_driver_list [ i + + ] ) ! = NULL ) {
if ( ! driver - > probe ( ) ) {
adb_controller = driver ;
break ;
}
}
if ( ( adb_controller = = NULL ) | | adb_controller - > init ( ) ) {
printk ( KERN_WARNING " Warning: no ADB interface detected \n " ) ;
adb_controller = NULL ;
} else {
2005-06-28 01:36:34 +04:00
# ifdef CONFIG_PM
2005-04-17 02:20:36 +04:00
pmu_register_sleep_notifier ( & adb_sleep_notifier ) ;
2005-06-28 01:36:34 +04:00
# endif /* CONFIG_PM */
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_PPC
if ( machine_is_compatible ( " AAPL,PowerBook1998 " ) | |
machine_is_compatible ( " PowerBook1,1 " ) )
sleepy_trackpad = 1 ;
# endif /* CONFIG_PPC */
init_completion ( & adb_probe_task_comp ) ;
adbdev_init ( ) ;
adb_reset_bus ( ) ;
}
return 0 ;
}
__initcall ( adb_init ) ;
2005-06-28 01:36:34 +04:00
# ifdef CONFIG_PM
2005-04-17 02:20:36 +04:00
/*
* notify clients before sleep and reset bus afterwards
*/
int
adb_notify_sleep ( struct pmu_sleep_notifier * self , int when )
{
int ret ;
switch ( when ) {
case PBOOK_SLEEP_REQUEST :
adb_got_sleep = 1 ;
/* We need to get a lock on the probe thread */
down ( & adb_probe_mutex ) ;
/* Stop autopoll */
if ( adb_controller - > autopoll )
adb_controller - > autopoll ( 0 ) ;
ret = notifier_call_chain ( & adb_client_list , ADB_MSG_POWERDOWN , NULL ) ;
if ( ret & NOTIFY_STOP_MASK ) {
up ( & adb_probe_mutex ) ;
return PBOOK_SLEEP_REFUSE ;
}
break ;
case PBOOK_SLEEP_REJECT :
if ( adb_got_sleep ) {
adb_got_sleep = 0 ;
up ( & adb_probe_mutex ) ;
adb_reset_bus ( ) ;
}
break ;
case PBOOK_SLEEP_NOW :
break ;
case PBOOK_WAKE :
adb_got_sleep = 0 ;
up ( & adb_probe_mutex ) ;
adb_reset_bus ( ) ;
break ;
}
return PBOOK_SLEEP_OK ;
}
2005-06-28 01:36:34 +04:00
# endif /* CONFIG_PM */
2005-04-17 02:20:36 +04:00
static int
do_adb_reset_bus ( void )
{
int ret , nret ;
if ( adb_controller = = NULL )
return - ENXIO ;
if ( adb_controller - > autopoll )
adb_controller - > autopoll ( 0 ) ;
nret = notifier_call_chain ( & adb_client_list , ADB_MSG_PRE_RESET , NULL ) ;
if ( nret & NOTIFY_STOP_MASK ) {
if ( adb_controller - > autopoll )
adb_controller - > autopoll ( autopoll_devs ) ;
return - EBUSY ;
}
if ( sleepy_trackpad ) {
/* Let the trackpad settle down */
adb_wait_ms ( 500 ) ;
}
down ( & adb_handler_sem ) ;
write_lock_irq ( & adb_handler_lock ) ;
memset ( adb_handler , 0 , sizeof ( adb_handler ) ) ;
write_unlock_irq ( & adb_handler_lock ) ;
/* That one is still a bit synchronous, oh well... */
if ( adb_controller - > reset_bus )
ret = adb_controller - > reset_bus ( ) ;
else
ret = 0 ;
if ( sleepy_trackpad ) {
/* Let the trackpad settle down */
adb_wait_ms ( 1500 ) ;
}
if ( ! ret ) {
autopoll_devs = adb_scan_bus ( ) ;
if ( adb_controller - > autopoll )
adb_controller - > autopoll ( autopoll_devs ) ;
}
up ( & adb_handler_sem ) ;
nret = notifier_call_chain ( & adb_client_list , ADB_MSG_POST_RESET , NULL ) ;
if ( nret & NOTIFY_STOP_MASK )
return - EBUSY ;
return ret ;
}
void
adb_poll ( void )
{
if ( ( adb_controller = = NULL ) | | ( adb_controller - > poll = = NULL ) )
return ;
adb_controller - > poll ( ) ;
}
static void
adb_probe_wakeup ( struct adb_request * req )
{
complete ( & adb_probe_task_comp ) ;
}
/* Static request used during probe */
static struct adb_request adb_sreq ;
static unsigned long adb_sreq_lock ; // Use semaphore ! */
int
adb_request ( struct adb_request * req , void ( * done ) ( struct adb_request * ) ,
int flags , int nbytes , . . . )
{
va_list list ;
int i , use_sreq ;
int rc ;
if ( ( adb_controller = = NULL ) | | ( adb_controller - > send_request = = NULL ) )
return - ENXIO ;
if ( nbytes < 1 )
return - EINVAL ;
if ( req = = NULL & & ( flags & ADBREQ_NOSEND ) )
return - EINVAL ;
if ( req = = NULL ) {
if ( test_and_set_bit ( 0 , & adb_sreq_lock ) ) {
printk ( " adb.c: Warning: contention on static request ! \n " ) ;
return - EPERM ;
}
req = & adb_sreq ;
flags | = ADBREQ_SYNC ;
use_sreq = 1 ;
} else
use_sreq = 0 ;
req - > nbytes = nbytes + 1 ;
req - > done = done ;
req - > reply_expected = flags & ADBREQ_REPLY ;
req - > data [ 0 ] = ADB_PACKET ;
va_start ( list , nbytes ) ;
for ( i = 0 ; i < nbytes ; + + i )
req - > data [ i + 1 ] = va_arg ( list , int ) ;
va_end ( list ) ;
if ( flags & ADBREQ_NOSEND )
return 0 ;
/* Synchronous requests send from the probe thread cause it to
* block . Beware that the " done " callback will be overriden !
*/
if ( ( flags & ADBREQ_SYNC ) & &
( current - > pid & & adb_probe_task_pid & &
adb_probe_task_pid = = current - > pid ) ) {
req - > done = adb_probe_wakeup ;
rc = adb_controller - > send_request ( req , 0 ) ;
if ( rc | | req - > complete )
goto bail ;
wait_for_completion ( & adb_probe_task_comp ) ;
rc = 0 ;
goto bail ;
}
rc = adb_controller - > send_request ( req , flags & ADBREQ_SYNC ) ;
bail :
if ( use_sreq )
clear_bit ( 0 , & adb_sreq_lock ) ;
return rc ;
}
/* Ultimately this should return the number of devices with
the given default id .
And it does it now ! Note : changed behaviour : This function
will now register if default_id _and_ handler_id both match
but handler_id can be left to 0 to match with default_id only .
When handler_id is set , this function will try to adjust
the handler_id id it doesn ' t match . */
int
adb_register ( int default_id , int handler_id , struct adb_ids * ids ,
void ( * handler ) ( unsigned char * , int , struct pt_regs * , int ) )
{
int i ;
down ( & adb_handler_sem ) ;
ids - > nids = 0 ;
for ( i = 1 ; i < 16 ; i + + ) {
if ( ( adb_handler [ i ] . original_address = = default_id ) & &
( ! handler_id | | ( handler_id = = adb_handler [ i ] . handler_id ) | |
try_handler_change ( i , handler_id ) ) ) {
if ( adb_handler [ i ] . handler ! = 0 ) {
printk ( KERN_ERR
" Two handlers for ADB device %d \n " ,
default_id ) ;
continue ;
}
write_lock_irq ( & adb_handler_lock ) ;
adb_handler [ i ] . handler = handler ;
write_unlock_irq ( & adb_handler_lock ) ;
ids - > id [ ids - > nids + + ] = i ;
}
}
up ( & adb_handler_sem ) ;
return ids - > nids ;
}
int
adb_unregister ( int index )
{
int ret = - ENODEV ;
down ( & adb_handler_sem ) ;
write_lock_irq ( & adb_handler_lock ) ;
if ( adb_handler [ index ] . handler ) {
while ( adb_handler [ index ] . busy ) {
write_unlock_irq ( & adb_handler_lock ) ;
yield ( ) ;
write_lock_irq ( & adb_handler_lock ) ;
}
ret = 0 ;
adb_handler [ index ] . handler = NULL ;
}
write_unlock_irq ( & adb_handler_lock ) ;
up ( & adb_handler_sem ) ;
return ret ;
}
void
adb_input ( unsigned char * buf , int nb , struct pt_regs * regs , int autopoll )
{
int i , id ;
static int dump_adb_input = 0 ;
unsigned long flags ;
void ( * handler ) ( unsigned char * , int , struct pt_regs * , int ) ;
/* We skip keystrokes and mouse moves when the sleep process
* has been started . We stop autopoll , but this is another security
*/
if ( adb_got_sleep )
return ;
id = buf [ 0 ] > > 4 ;
if ( dump_adb_input ) {
printk ( KERN_INFO " adb packet: " ) ;
for ( i = 0 ; i < nb ; + + i )
printk ( " %x " , buf [ i ] ) ;
printk ( " , id = %d \n " , id ) ;
}
write_lock_irqsave ( & adb_handler_lock , flags ) ;
handler = adb_handler [ id ] . handler ;
if ( handler ! = NULL )
adb_handler [ id ] . busy = 1 ;
write_unlock_irqrestore ( & adb_handler_lock , flags ) ;
if ( handler ! = NULL ) {
( * handler ) ( buf , nb , regs , autopoll ) ;
wmb ( ) ;
adb_handler [ id ] . busy = 0 ;
}
}
/* Try to change handler to new_id. Will return 1 if successful. */
static int try_handler_change ( int address , int new_id )
{
struct adb_request req ;
if ( adb_handler [ address ] . handler_id = = new_id )
return 1 ;
adb_request ( & req , NULL , ADBREQ_SYNC , 3 ,
ADB_WRITEREG ( address , 3 ) , address | 0x20 , new_id ) ;
adb_request ( & req , NULL , ADBREQ_SYNC | ADBREQ_REPLY , 1 ,
ADB_READREG ( address , 3 ) ) ;
if ( req . reply_len < 2 )
return 0 ;
if ( req . reply [ 2 ] ! = new_id )
return 0 ;
adb_handler [ address ] . handler_id = req . reply [ 2 ] ;
return 1 ;
}
int
adb_try_handler_change ( int address , int new_id )
{
int ret ;
down ( & adb_handler_sem ) ;
ret = try_handler_change ( address , new_id ) ;
up ( & adb_handler_sem ) ;
return ret ;
}
int
adb_get_infos ( int address , int * original_address , int * handler_id )
{
down ( & adb_handler_sem ) ;
* original_address = adb_handler [ address ] . original_address ;
* handler_id = adb_handler [ address ] . handler_id ;
up ( & adb_handler_sem ) ;
return ( * original_address ! = 0 ) ;
}
/*
* / dev / adb device driver .
*/
# define ADB_MAJOR 56 /* major number for /dev/adb */
struct adbdev_state {
spinlock_t lock ;
atomic_t n_pending ;
struct adb_request * completed ;
wait_queue_head_t wait_queue ;
int inuse ;
} ;
static void adb_write_done ( struct adb_request * req )
{
struct adbdev_state * state = ( struct adbdev_state * ) req - > arg ;
unsigned long flags ;
if ( ! req - > complete ) {
req - > reply_len = 0 ;
req - > complete = 1 ;
}
spin_lock_irqsave ( & state - > lock , flags ) ;
atomic_dec ( & state - > n_pending ) ;
if ( ! state - > inuse ) {
kfree ( req ) ;
if ( atomic_read ( & state - > n_pending ) = = 0 ) {
spin_unlock_irqrestore ( & state - > lock , flags ) ;
kfree ( state ) ;
return ;
}
} else {
struct adb_request * * ap = & state - > completed ;
while ( * ap ! = NULL )
ap = & ( * ap ) - > next ;
req - > next = NULL ;
* ap = req ;
wake_up_interruptible ( & state - > wait_queue ) ;
}
spin_unlock_irqrestore ( & state - > lock , flags ) ;
}
static int
do_adb_query ( struct adb_request * req )
{
int ret = - EINVAL ;
switch ( req - > data [ 1 ] )
{
case ADB_QUERY_GETDEVINFO :
if ( req - > nbytes < 3 )
break ;
down ( & adb_handler_sem ) ;
req - > reply [ 0 ] = adb_handler [ req - > data [ 2 ] ] . original_address ;
req - > reply [ 1 ] = adb_handler [ req - > data [ 2 ] ] . handler_id ;
up ( & adb_handler_sem ) ;
req - > complete = 1 ;
req - > reply_len = 2 ;
adb_write_done ( req ) ;
ret = 0 ;
break ;
}
return ret ;
}
static int adb_open ( struct inode * inode , struct file * file )
{
struct adbdev_state * state ;
if ( iminor ( inode ) > 0 | | adb_controller = = NULL )
return - ENXIO ;
state = kmalloc ( sizeof ( struct adbdev_state ) , GFP_KERNEL ) ;
if ( state = = 0 )
return - ENOMEM ;
file - > private_data = state ;
spin_lock_init ( & state - > lock ) ;
atomic_set ( & state - > n_pending , 0 ) ;
state - > completed = NULL ;
init_waitqueue_head ( & state - > wait_queue ) ;
state - > inuse = 1 ;
return 0 ;
}
static int adb_release ( struct inode * inode , struct file * file )
{
struct adbdev_state * state = file - > private_data ;
unsigned long flags ;
lock_kernel ( ) ;
if ( state ) {
file - > private_data = NULL ;
spin_lock_irqsave ( & state - > lock , flags ) ;
if ( atomic_read ( & state - > n_pending ) = = 0
& & state - > completed = = NULL ) {
spin_unlock_irqrestore ( & state - > lock , flags ) ;
kfree ( state ) ;
} else {
state - > inuse = 0 ;
spin_unlock_irqrestore ( & state - > lock , flags ) ;
}
}
unlock_kernel ( ) ;
return 0 ;
}
static ssize_t adb_read ( struct file * file , char __user * buf ,
size_t count , loff_t * ppos )
{
int ret = 0 ;
struct adbdev_state * state = file - > private_data ;
struct adb_request * req ;
wait_queue_t wait = __WAITQUEUE_INITIALIZER ( wait , current ) ;
unsigned long flags ;
if ( count < 2 )
return - EINVAL ;
if ( count > sizeof ( req - > reply ) )
count = sizeof ( req - > reply ) ;
if ( ! access_ok ( VERIFY_WRITE , buf , count ) )
return - EFAULT ;
req = NULL ;
spin_lock_irqsave ( & state - > lock , flags ) ;
add_wait_queue ( & state - > wait_queue , & wait ) ;
current - > state = TASK_INTERRUPTIBLE ;
for ( ; ; ) {
req = state - > completed ;
if ( req ! = NULL )
state - > completed = req - > next ;
else if ( atomic_read ( & state - > n_pending ) = = 0 )
ret = - EIO ;
if ( req ! = NULL | | ret ! = 0 )
break ;
if ( file - > f_flags & O_NONBLOCK ) {
ret = - EAGAIN ;
break ;
}
if ( signal_pending ( current ) ) {
ret = - ERESTARTSYS ;
break ;
}
spin_unlock_irqrestore ( & state - > lock , flags ) ;
schedule ( ) ;
spin_lock_irqsave ( & state - > lock , flags ) ;
}
current - > state = TASK_RUNNING ;
remove_wait_queue ( & state - > wait_queue , & wait ) ;
spin_unlock_irqrestore ( & state - > lock , flags ) ;
if ( ret )
return ret ;
ret = req - > reply_len ;
if ( ret > count )
ret = count ;
if ( ret > 0 & & copy_to_user ( buf , req - > reply , ret ) )
ret = - EFAULT ;
kfree ( req ) ;
return ret ;
}
static ssize_t adb_write ( struct file * file , const char __user * buf ,
size_t count , loff_t * ppos )
{
int ret /*, i*/ ;
struct adbdev_state * state = file - > private_data ;
struct adb_request * req ;
if ( count < 2 | | count > sizeof ( req - > data ) )
return - EINVAL ;
if ( adb_controller = = NULL )
return - ENXIO ;
if ( ! access_ok ( VERIFY_READ , buf , count ) )
return - EFAULT ;
req = ( struct adb_request * ) kmalloc ( sizeof ( struct adb_request ) ,
GFP_KERNEL ) ;
if ( req = = NULL )
return - ENOMEM ;
req - > nbytes = count ;
req - > done = adb_write_done ;
req - > arg = ( void * ) state ;
req - > complete = 0 ;
ret = - EFAULT ;
if ( copy_from_user ( req - > data , buf , count ) )
goto out ;
atomic_inc ( & state - > n_pending ) ;
/* If a probe is in progress or we are sleeping, wait for it to complete */
down ( & adb_probe_mutex ) ;
/* Queries are special requests sent to the ADB driver itself */
if ( req - > data [ 0 ] = = ADB_QUERY ) {
if ( count > 1 )
ret = do_adb_query ( req ) ;
else
ret = - EINVAL ;
up ( & adb_probe_mutex ) ;
}
/* Special case for ADB_BUSRESET request, all others are sent to
the controller */
else if ( ( req - > data [ 0 ] = = ADB_PACKET ) & & ( count > 1 )
& & ( req - > data [ 1 ] = = ADB_BUSRESET ) ) {
ret = do_adb_reset_bus ( ) ;
up ( & adb_probe_mutex ) ;
atomic_dec ( & state - > n_pending ) ;
if ( ret = = 0 )
ret = count ;
goto out ;
} else {
req - > reply_expected = ( ( req - > data [ 1 ] & 0xc ) = = 0xc ) ;
if ( adb_controller & & adb_controller - > send_request )
ret = adb_controller - > send_request ( req , 0 ) ;
else
ret = - ENXIO ;
up ( & adb_probe_mutex ) ;
}
if ( ret ! = 0 ) {
atomic_dec ( & state - > n_pending ) ;
goto out ;
}
return count ;
out :
kfree ( req ) ;
return ret ;
}
static struct file_operations adb_fops = {
. owner = THIS_MODULE ,
. llseek = no_llseek ,
. read = adb_read ,
. write = adb_write ,
. open = adb_open ,
. release = adb_release ,
} ;
static void
adbdev_init ( void )
{
if ( register_chrdev ( ADB_MAJOR , " adb " , & adb_fops ) ) {
printk ( KERN_ERR " adb: unable to get major %d \n " , ADB_MAJOR ) ;
return ;
}
devfs_mk_cdev ( MKDEV ( ADB_MAJOR , 0 ) , S_IFCHR | S_IRUSR | S_IWUSR , " adb " ) ;
2005-03-23 21:01:41 +03:00
adb_dev_class = class_create ( THIS_MODULE , " adb " ) ;
if ( IS_ERR ( adb_dev_class ) )
2005-04-17 02:20:36 +04:00
return ;
2005-10-28 09:25:43 +04:00
class_device_create ( adb_dev_class , NULL , MKDEV ( ADB_MAJOR , 0 ) , NULL , " adb " ) ;
2005-04-17 02:20:36 +04:00
}