2005-04-17 02:20:36 +04:00
/*
* HIL MLC state machine and serio interface driver
*
* Copyright ( c ) 2001 Brian S . Julin
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions , and the following disclaimer ,
* without modification .
* 2. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission .
*
* Alternatively , this software may be distributed under the terms of the
* GNU General Public License ( " GPL " ) .
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ` ` AS IS ' ' AND
* ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED . IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
* DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION )
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT
* LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY
*
* References :
* HP - HIL Technical Reference Manual . Hewlett Packard Product No . 45918 A
*
*
* Driver theory of operation :
*
2007-03-01 07:51:29 +03:00
* Some access methods and an ISR is defined by the sub - driver
* ( e . g . hp_sdc_mlc . c ) . These methods are expected to provide a
* few bits of logic in addition to raw access to the HIL MLC ,
* specifically , the ISR , which is entirely registered by the
* sub - driver and invoked directly , must check for record
2005-04-17 02:20:36 +04:00
* termination or packet match , at which point a semaphore must
* be cleared and then the hil_mlcs_tasklet must be scheduled .
*
* The hil_mlcs_tasklet processes the state machine for all MLCs
* each time it runs , checking each MLC ' s progress at the current
* node in the state machine , and moving the MLC to subsequent nodes
* in the state machine when appropriate . It will reschedule
* itself if output is pending . ( This rescheduling should be replaced
* at some point with a sub - driver - specific mechanism . )
*
2007-03-01 07:51:29 +03:00
* A timer task prods the tasklet once per second to prevent
2005-04-17 02:20:36 +04:00
* hangups when attached devices do not return expected data
* and to initiate probes of the loop for new devices .
*/
# include <linux/hil_mlc.h>
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/timer.h>
# include <linux/list.h>
MODULE_AUTHOR ( " Brian S. Julin <bri@calyx.com> " ) ;
MODULE_DESCRIPTION ( " HIL MLC serio " ) ;
MODULE_LICENSE ( " Dual BSD/GPL " ) ;
EXPORT_SYMBOL ( hil_mlc_register ) ;
EXPORT_SYMBOL ( hil_mlc_unregister ) ;
# define PREFIX "HIL MLC: "
static LIST_HEAD ( hil_mlcs ) ;
static DEFINE_RWLOCK ( hil_mlcs_lock ) ;
static struct timer_list hil_mlcs_kicker ;
static int hil_mlcs_probe ;
static void hil_mlcs_process ( unsigned long unused ) ;
DECLARE_TASKLET_DISABLED ( hil_mlcs_tasklet , hil_mlcs_process , 0 ) ;
/* #define HIL_MLC_DEBUG */
/********************** Device info/instance management **********************/
2007-03-01 07:51:29 +03:00
static void hil_mlc_clear_di_map ( hil_mlc * mlc , int val )
{
2005-04-17 02:20:36 +04:00
int j ;
2007-03-01 07:51:29 +03:00
for ( j = val ; j < 7 ; j + + )
2005-04-17 02:20:36 +04:00
mlc - > di_map [ j ] = - 1 ;
}
2007-03-01 07:51:29 +03:00
static void hil_mlc_clear_di_scratch ( hil_mlc * mlc )
{
memset ( & mlc - > di_scratch , 0 , sizeof ( mlc - > di_scratch ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-03-01 07:51:29 +03:00
static void hil_mlc_copy_di_scratch ( hil_mlc * mlc , int idx )
{
memcpy ( & mlc - > di [ idx ] , & mlc - > di_scratch , sizeof ( mlc - > di_scratch ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-03-01 07:51:29 +03:00
static int hil_mlc_match_di_scratch ( hil_mlc * mlc )
{
2005-04-17 02:20:36 +04:00
int idx ;
for ( idx = 0 ; idx < HIL_MLC_DEVMEM ; idx + + ) {
2007-03-01 07:51:29 +03:00
int j , found = 0 ;
2005-04-17 02:20:36 +04:00
/* In-use slots are not eligible. */
2007-03-01 07:51:29 +03:00
for ( j = 0 ; j < 7 ; j + + )
if ( mlc - > di_map [ j ] = = idx )
found + + ;
if ( found )
continue ;
if ( ! memcmp ( mlc - > di + idx , & mlc - > di_scratch ,
sizeof ( mlc - > di_scratch ) ) )
break ;
2005-04-17 02:20:36 +04:00
}
2007-03-01 07:51:29 +03:00
return idx > = HIL_MLC_DEVMEM ? - 1 : idx ;
2005-04-17 02:20:36 +04:00
}
2007-03-01 07:51:29 +03:00
static int hil_mlc_find_free_di ( hil_mlc * mlc )
{
2005-04-17 02:20:36 +04:00
int idx ;
2007-03-01 07:51:29 +03:00
/* TODO: Pick all-zero slots first, failing that,
* randomize the slot picked among those eligible .
2005-04-17 02:20:36 +04:00
*/
for ( idx = 0 ; idx < HIL_MLC_DEVMEM ; idx + + ) {
2007-03-01 07:51:29 +03:00
int j , found = 0 ;
for ( j = 0 ; j < 7 ; j + + )
if ( mlc - > di_map [ j ] = = idx )
found + + ;
if ( ! found )
break ;
2005-04-17 02:20:36 +04:00
}
2007-03-01 07:51:29 +03:00
return idx ; /* Note: It is guaranteed at least one above will match */
2005-04-17 02:20:36 +04:00
}
2007-03-01 07:51:29 +03:00
static inline void hil_mlc_clean_serio_map ( hil_mlc * mlc )
{
2005-04-17 02:20:36 +04:00
int idx ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
for ( idx = 0 ; idx < HIL_MLC_DEVMEM ; idx + + ) {
2007-03-01 07:51:29 +03:00
int j , found = 0 ;
for ( j = 0 ; j < 7 ; j + + )
if ( mlc - > di_map [ j ] = = idx )
found + + ;
if ( ! found )
mlc - > serio_map [ idx ] . di_revmap = - 1 ;
2005-04-17 02:20:36 +04:00
}
}
2007-03-01 07:51:29 +03:00
static void hil_mlc_send_polls ( hil_mlc * mlc )
{
2005-04-17 02:20:36 +04:00
int did , i , cnt ;
struct serio * serio ;
struct serio_driver * drv ;
i = cnt = 0 ;
did = ( mlc - > ipacket [ 0 ] & HIL_PKT_ADDR_MASK ) > > 8 ;
serio = did ? mlc - > serio [ mlc - > di_map [ did - 1 ] ] : NULL ;
drv = ( serio ! = NULL ) ? serio - > drv : NULL ;
while ( mlc - > icount < 15 - i ) {
hil_packet p ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
p = mlc - > ipacket [ i ] ;
if ( did ! = ( p & HIL_PKT_ADDR_MASK ) > > 8 ) {
2007-03-01 07:51:29 +03:00
if ( drv & & drv - > interrupt ) {
drv - > interrupt ( serio , 0 , 0 ) ;
drv - > interrupt ( serio , HIL_ERR_INT > > 16 , 0 ) ;
drv - > interrupt ( serio , HIL_PKT_CMD > > 8 , 0 ) ;
drv - > interrupt ( serio , HIL_CMD_POL + cnt , 0 ) ;
}
2005-04-17 02:20:36 +04:00
did = ( p & HIL_PKT_ADDR_MASK ) > > 8 ;
serio = did ? mlc - > serio [ mlc - > di_map [ did - 1 ] ] : NULL ;
drv = ( serio ! = NULL ) ? serio - > drv : NULL ;
cnt = 0 ;
}
2007-03-01 07:51:29 +03:00
cnt + + ;
i + + ;
if ( drv & & drv - > interrupt ) {
drv - > interrupt ( serio , ( p > > 24 ) , 0 ) ;
drv - > interrupt ( serio , ( p > > 16 ) & 0xff , 0 ) ;
drv - > interrupt ( serio , ( p > > 8 ) & ~ HIL_PKT_ADDR_MASK , 0 ) ;
drv - > interrupt ( serio , p & 0xff , 0 ) ;
}
2005-04-17 02:20:36 +04:00
}
}
/*************************** State engine *********************************/
# define HILSEN_SCHED 0x000100 /* Schedule the tasklet */
# define HILSEN_BREAK 0x000200 /* Wait until next pass */
# define HILSEN_UP 0x000400 /* relative node#, decrement */
# define HILSEN_DOWN 0x000800 /* relative node#, increment */
# define HILSEN_FOLLOW 0x001000 /* use retval as next node# */
# define HILSEN_MASK 0x0000ff
# define HILSEN_START 0
# define HILSEN_RESTART 1
# define HILSEN_DHR 9
# define HILSEN_DHR2 10
# define HILSEN_IFC 14
# define HILSEN_HEAL0 16
# define HILSEN_HEAL 18
# define HILSEN_ACF 21
# define HILSEN_ACF2 22
# define HILSEN_DISC0 25
# define HILSEN_DISC 27
# define HILSEN_MATCH 40
# define HILSEN_OPERATE 41
# define HILSEN_PROBE 44
# define HILSEN_DSR 52
# define HILSEN_REPOLL 55
# define HILSEN_IFCACF 58
# define HILSEN_END 60
# define HILSEN_NEXT (HILSEN_DOWN | 1)
# define HILSEN_SAME (HILSEN_DOWN | 0)
# define HILSEN_LAST (HILSEN_UP | 1)
# define HILSEN_DOZE (HILSEN_SAME | HILSEN_SCHED | HILSEN_BREAK)
# define HILSEN_SLEEP (HILSEN_SAME | HILSEN_BREAK)
2007-03-01 07:51:29 +03:00
static int hilse_match ( hil_mlc * mlc , int unused )
{
2005-04-17 02:20:36 +04:00
int rc ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
rc = hil_mlc_match_di_scratch ( mlc ) ;
if ( rc = = - 1 ) {
rc = hil_mlc_find_free_di ( mlc ) ;
2007-03-01 07:51:29 +03:00
if ( rc = = - 1 )
goto err ;
2005-04-17 02:20:36 +04:00
# ifdef HIL_MLC_DEBUG
printk ( KERN_DEBUG PREFIX " new in slot %i \n " , rc ) ;
# endif
hil_mlc_copy_di_scratch ( mlc , rc ) ;
mlc - > di_map [ mlc - > ddi ] = rc ;
mlc - > serio_map [ rc ] . di_revmap = mlc - > ddi ;
hil_mlc_clean_serio_map ( mlc ) ;
serio_rescan ( mlc - > serio [ rc ] ) ;
return - 1 ;
}
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
mlc - > di_map [ mlc - > ddi ] = rc ;
# ifdef HIL_MLC_DEBUG
printk ( KERN_DEBUG PREFIX " same in slot %i \n " , rc ) ;
# endif
mlc - > serio_map [ rc ] . di_revmap = mlc - > ddi ;
hil_mlc_clean_serio_map ( mlc ) ;
return 0 ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
err :
printk ( KERN_ERR PREFIX " Residual device slots exhausted, close some serios! \n " ) ;
return 1 ;
}
/* An LCV used to prevent runaway loops, forces 5 second sleep when reset. */
2007-03-01 07:51:29 +03:00
static int hilse_init_lcv ( hil_mlc * mlc , int unused )
{
2005-04-17 02:20:36 +04:00
struct timeval tv ;
do_gettimeofday ( & tv ) ;
2007-03-01 07:51:29 +03:00
if ( mlc - > lcv & & ( tv . tv_sec - mlc - > lcv_tv . tv_sec ) < 5 )
return - 1 ;
2005-04-17 02:20:36 +04:00
mlc - > lcv_tv = tv ;
mlc - > lcv = 0 ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-03-01 07:51:29 +03:00
static int hilse_inc_lcv ( hil_mlc * mlc , int lim )
{
return mlc - > lcv + + > = lim ? - 1 : 0 ;
2005-04-17 02:20:36 +04:00
}
#if 0
2007-03-01 07:51:29 +03:00
static int hilse_set_lcv ( hil_mlc * mlc , int val )
{
2005-04-17 02:20:36 +04:00
mlc - > lcv = val ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
# endif
/* Management of the discovered device index (zero based, -1 means no devs) */
2007-03-01 07:51:29 +03:00
static int hilse_set_ddi ( hil_mlc * mlc , int val )
{
2005-04-17 02:20:36 +04:00
mlc - > ddi = val ;
hil_mlc_clear_di_map ( mlc , val + 1 ) ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-03-01 07:51:29 +03:00
static int hilse_dec_ddi ( hil_mlc * mlc , int unused )
{
2005-04-17 02:20:36 +04:00
mlc - > ddi - - ;
2007-03-01 07:51:29 +03:00
if ( mlc - > ddi < = - 1 ) {
2005-04-17 02:20:36 +04:00
mlc - > ddi = - 1 ;
hil_mlc_clear_di_map ( mlc , 0 ) ;
return - 1 ;
}
hil_mlc_clear_di_map ( mlc , mlc - > ddi + 1 ) ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-03-01 07:51:29 +03:00
static int hilse_inc_ddi ( hil_mlc * mlc , int unused )
{
BUG_ON ( mlc - > ddi > = 6 ) ;
2005-04-17 02:20:36 +04:00
mlc - > ddi + + ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-03-01 07:51:29 +03:00
static int hilse_take_idd ( hil_mlc * mlc , int unused )
{
2005-04-17 02:20:36 +04:00
int i ;
2007-03-01 07:51:29 +03:00
/* Help the state engine:
* Is this a real IDD response or just an echo ?
2005-04-17 02:20:36 +04:00
*
2007-03-01 07:51:29 +03:00
* Real IDD response does not start with a command .
2005-04-17 02:20:36 +04:00
*/
2007-03-01 07:51:29 +03:00
if ( mlc - > ipacket [ 0 ] & HIL_PKT_CMD )
goto bail ;
2005-04-17 02:20:36 +04:00
/* Should have the command echoed further down. */
for ( i = 1 ; i < 16 ; i + + ) {
2007-03-01 07:51:29 +03:00
if ( ( ( mlc - > ipacket [ i ] & HIL_PKT_ADDR_MASK ) = =
2005-04-17 02:20:36 +04:00
( mlc - > ipacket [ 0 ] & HIL_PKT_ADDR_MASK ) ) & &
2007-03-01 07:51:29 +03:00
( mlc - > ipacket [ i ] & HIL_PKT_CMD ) & &
2005-04-17 02:20:36 +04:00
( ( mlc - > ipacket [ i ] & HIL_PKT_DATA_MASK ) = = HIL_CMD_IDD ) )
break ;
}
2007-03-01 07:51:29 +03:00
if ( i > 15 )
goto bail ;
2005-04-17 02:20:36 +04:00
/* And the rest of the packets should still be clear. */
2007-03-01 07:51:29 +03:00
while ( + + i < 16 )
if ( mlc - > ipacket [ i ] )
break ;
if ( i < 16 )
goto bail ;
for ( i = 0 ; i < 16 ; i + + )
mlc - > di_scratch . idd [ i ] =
2005-04-17 02:20:36 +04:00
mlc - > ipacket [ i ] & HIL_PKT_DATA_MASK ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
/* Next step is to see if RSC supported */
2007-03-01 07:51:29 +03:00
if ( mlc - > di_scratch . idd [ 1 ] & HIL_IDD_HEADER_RSC )
2005-04-17 02:20:36 +04:00
return HILSEN_NEXT ;
2007-03-01 07:51:29 +03:00
if ( mlc - > di_scratch . idd [ 1 ] & HIL_IDD_HEADER_EXD )
2005-04-17 02:20:36 +04:00
return HILSEN_DOWN | 4 ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
return 0 ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
bail :
mlc - > ddi - - ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
return - 1 ; /* This should send us off to ACF */
}
2007-03-01 07:51:29 +03:00
static int hilse_take_rsc ( hil_mlc * mlc , int unused )
{
2005-04-17 02:20:36 +04:00
int i ;
2007-03-01 07:51:29 +03:00
for ( i = 0 ; i < 16 ; i + + )
mlc - > di_scratch . rsc [ i ] =
2005-04-17 02:20:36 +04:00
mlc - > ipacket [ i ] & HIL_PKT_DATA_MASK ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
/* Next step is to see if EXD supported (IDD has already been read) */
2007-03-01 07:51:29 +03:00
if ( mlc - > di_scratch . idd [ 1 ] & HIL_IDD_HEADER_EXD )
2005-04-17 02:20:36 +04:00
return HILSEN_NEXT ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-03-01 07:51:29 +03:00
static int hilse_take_exd ( hil_mlc * mlc , int unused )
{
2005-04-17 02:20:36 +04:00
int i ;
2007-03-01 07:51:29 +03:00
for ( i = 0 ; i < 16 ; i + + )
mlc - > di_scratch . exd [ i ] =
2005-04-17 02:20:36 +04:00
mlc - > ipacket [ i ] & HIL_PKT_DATA_MASK ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
/* Next step is to see if RNM supported. */
2007-03-01 07:51:29 +03:00
if ( mlc - > di_scratch . exd [ 0 ] & HIL_EXD_HEADER_RNM )
2005-04-17 02:20:36 +04:00
return HILSEN_NEXT ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-03-01 07:51:29 +03:00
static int hilse_take_rnm ( hil_mlc * mlc , int unused )
{
2005-04-17 02:20:36 +04:00
int i ;
2007-03-01 07:51:29 +03:00
for ( i = 0 ; i < 16 ; i + + )
mlc - > di_scratch . rnm [ i ] =
2005-04-17 02:20:36 +04:00
mlc - > ipacket [ i ] & HIL_PKT_DATA_MASK ;
2007-03-01 07:51:29 +03:00
printk ( KERN_INFO PREFIX " Device name gotten: %16s \n " ,
mlc - > di_scratch . rnm ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-03-01 07:51:29 +03:00
static int hilse_operate ( hil_mlc * mlc , int repoll )
{
2005-04-17 02:20:36 +04:00
2007-03-01 07:51:29 +03:00
if ( mlc - > opercnt = = 0 )
hil_mlcs_probe = 0 ;
2005-04-17 02:20:36 +04:00
mlc - > opercnt = 1 ;
hil_mlc_send_polls ( mlc ) ;
2007-03-01 07:51:29 +03:00
if ( ! hil_mlcs_probe )
return 0 ;
2005-04-17 02:20:36 +04:00
hil_mlcs_probe = 0 ;
mlc - > opercnt = 0 ;
return 1 ;
}
# define FUNC(funct, funct_arg, zero_rc, neg_rc, pos_rc) \
2006-10-14 19:52:36 +04:00
{ HILSE_FUNC , { . func = funct } , funct_arg , zero_rc , neg_rc , pos_rc } ,
2005-04-17 02:20:36 +04:00
# define OUT(pack) \
2006-10-14 19:52:36 +04:00
{ HILSE_OUT , { . packet = pack } , 0 , HILSEN_NEXT , HILSEN_DOZE , 0 } ,
2005-04-17 02:20:36 +04:00
# define CTS \
2006-10-14 19:52:36 +04:00
{ HILSE_CTS , { . packet = 0 } , 0 , HILSEN_NEXT | HILSEN_SCHED | HILSEN_BREAK , HILSEN_DOZE , 0 } ,
2005-04-17 02:20:36 +04:00
# define EXPECT(comp, to, got, got_wrong, timed_out) \
2006-10-14 19:52:36 +04:00
{ HILSE_EXPECT , { . packet = comp } , to , got , got_wrong , timed_out } ,
2005-04-17 02:20:36 +04:00
# define EXPECT_LAST(comp, to, got, got_wrong, timed_out) \
2006-10-14 19:52:36 +04:00
{ HILSE_EXPECT_LAST , { . packet = comp } , to , got , got_wrong , timed_out } ,
2005-04-17 02:20:36 +04:00
# define EXPECT_DISC(comp, to, got, got_wrong, timed_out) \
2006-10-14 19:52:36 +04:00
{ HILSE_EXPECT_DISC , { . packet = comp } , to , got , got_wrong , timed_out } ,
2005-04-17 02:20:36 +04:00
# define IN(to, got, got_error, timed_out) \
2006-10-14 19:52:36 +04:00
{ HILSE_IN , { . packet = 0 } , to , got , got_error , timed_out } ,
2005-04-17 02:20:36 +04:00
# define OUT_DISC(pack) \
2006-10-14 19:52:36 +04:00
{ HILSE_OUT_DISC , { . packet = pack } , 0 , 0 , 0 , 0 } ,
2005-04-17 02:20:36 +04:00
# define OUT_LAST(pack) \
2006-10-14 19:52:36 +04:00
{ HILSE_OUT_LAST , { . packet = pack } , 0 , 0 , 0 , 0 } ,
2005-04-17 02:20:36 +04:00
2007-03-01 07:51:19 +03:00
const struct hilse_node hil_mlc_se [ HILSEN_END ] = {
2005-04-17 02:20:36 +04:00
/* 0 HILSEN_START */
FUNC ( hilse_init_lcv , 0 , HILSEN_NEXT , HILSEN_SLEEP , 0 )
/* 1 HILSEN_RESTART */
FUNC ( hilse_inc_lcv , 10 , HILSEN_NEXT , HILSEN_START , 0 )
OUT ( HIL_CTRL_ONLY ) /* Disable APE */
CTS
# define TEST_PACKET(x) \
( HIL_PKT_CMD | ( x < < HIL_PKT_ADDR_SHIFT ) | x < < 4 | x )
OUT ( HIL_DO_ALTER_CTRL | HIL_CTRL_TEST | TEST_PACKET ( 0x5 ) )
EXPECT ( HIL_ERR_INT | TEST_PACKET ( 0x5 ) ,
2000 , HILSEN_NEXT , HILSEN_RESTART , HILSEN_RESTART )
OUT ( HIL_DO_ALTER_CTRL | HIL_CTRL_TEST | TEST_PACKET ( 0xa ) )
EXPECT ( HIL_ERR_INT | TEST_PACKET ( 0xa ) ,
2000 , HILSEN_NEXT , HILSEN_RESTART , HILSEN_RESTART )
OUT ( HIL_CTRL_ONLY | 0 ) /* Disable test mode */
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
/* 9 HILSEN_DHR */
FUNC ( hilse_init_lcv , 0 , HILSEN_NEXT , HILSEN_SLEEP , 0 )
/* 10 HILSEN_DHR2 */
FUNC ( hilse_inc_lcv , 10 , HILSEN_NEXT , HILSEN_START , 0 )
FUNC ( hilse_set_ddi , - 1 , HILSEN_NEXT , 0 , 0 )
OUT ( HIL_PKT_CMD | HIL_CMD_DHR )
IN ( 300000 , HILSEN_DHR2 , HILSEN_DHR2 , HILSEN_NEXT )
/* 14 HILSEN_IFC */
2007-03-01 07:51:29 +03:00
OUT ( HIL_PKT_CMD | HIL_CMD_IFC )
2005-04-17 02:20:36 +04:00
EXPECT ( HIL_PKT_CMD | HIL_CMD_IFC | HIL_ERR_INT ,
20000 , HILSEN_DISC , HILSEN_DHR2 , HILSEN_NEXT )
/* If devices are there, they weren't in PUP or other loopback mode.
* We ' re more concerned at this point with restoring operation
* to devices than discovering new ones , so we try to salvage
* the loop configuration by closing off the loop .
*/
/* 16 HILSEN_HEAL0 */
FUNC ( hilse_dec_ddi , 0 , HILSEN_NEXT , HILSEN_ACF , 0 )
FUNC ( hilse_inc_ddi , 0 , HILSEN_NEXT , 0 , 0 )
/* 18 HILSEN_HEAL */
OUT_LAST ( HIL_CMD_ELB )
2007-03-01 07:51:29 +03:00
EXPECT_LAST ( HIL_CMD_ELB | HIL_ERR_INT ,
2005-04-17 02:20:36 +04:00
20000 , HILSEN_REPOLL , HILSEN_DSR , HILSEN_NEXT )
FUNC ( hilse_dec_ddi , 0 , HILSEN_HEAL , HILSEN_NEXT , 0 )
/* 21 HILSEN_ACF */
FUNC ( hilse_init_lcv , 0 , HILSEN_NEXT , HILSEN_DOZE , 0 )
/* 22 HILSEN_ACF2 */
FUNC ( hilse_inc_lcv , 10 , HILSEN_NEXT , HILSEN_START , 0 )
OUT ( HIL_PKT_CMD | HIL_CMD_ACF | 1 )
IN ( 20000 , HILSEN_NEXT , HILSEN_DSR , HILSEN_NEXT )
/* 25 HILSEN_DISC0 */
OUT_DISC ( HIL_PKT_CMD | HIL_CMD_ELB )
EXPECT_DISC ( HIL_PKT_CMD | HIL_CMD_ELB | HIL_ERR_INT ,
20000 , HILSEN_NEXT , HILSEN_DSR , HILSEN_DSR )
/* Only enter here if response just received */
/* 27 HILSEN_DISC */
OUT_DISC ( HIL_PKT_CMD | HIL_CMD_IDD )
EXPECT_DISC ( HIL_PKT_CMD | HIL_CMD_IDD | HIL_ERR_INT ,
20000 , HILSEN_NEXT , HILSEN_DSR , HILSEN_START )
FUNC ( hilse_inc_ddi , 0 , HILSEN_NEXT , HILSEN_START , 0 )
FUNC ( hilse_take_idd , 0 , HILSEN_MATCH , HILSEN_IFCACF , HILSEN_FOLLOW )
OUT_LAST ( HIL_PKT_CMD | HIL_CMD_RSC )
EXPECT_LAST ( HIL_PKT_CMD | HIL_CMD_RSC | HIL_ERR_INT ,
30000 , HILSEN_NEXT , HILSEN_DSR , HILSEN_DSR )
FUNC ( hilse_take_rsc , 0 , HILSEN_MATCH , 0 , HILSEN_FOLLOW )
OUT_LAST ( HIL_PKT_CMD | HIL_CMD_EXD )
EXPECT_LAST ( HIL_PKT_CMD | HIL_CMD_EXD | HIL_ERR_INT ,
30000 , HILSEN_NEXT , HILSEN_DSR , HILSEN_DSR )
FUNC ( hilse_take_exd , 0 , HILSEN_MATCH , 0 , HILSEN_FOLLOW )
OUT_LAST ( HIL_PKT_CMD | HIL_CMD_RNM )
EXPECT_LAST ( HIL_PKT_CMD | HIL_CMD_RNM | HIL_ERR_INT ,
30000 , HILSEN_NEXT , HILSEN_DSR , HILSEN_DSR )
FUNC ( hilse_take_rnm , 0 , HILSEN_MATCH , 0 , 0 )
/* 40 HILSEN_MATCH */
FUNC ( hilse_match , 0 , HILSEN_NEXT , HILSEN_NEXT , /* TODO */ 0 )
/* 41 HILSEN_OPERATE */
OUT ( HIL_PKT_CMD | HIL_CMD_POL )
EXPECT ( HIL_PKT_CMD | HIL_CMD_POL | HIL_ERR_INT ,
20000 , HILSEN_NEXT , HILSEN_DSR , HILSEN_NEXT )
FUNC ( hilse_operate , 0 , HILSEN_OPERATE , HILSEN_IFC , HILSEN_NEXT )
/* 44 HILSEN_PROBE */
OUT_LAST ( HIL_PKT_CMD | HIL_CMD_EPT )
2007-03-01 07:51:29 +03:00
IN ( 10000 , HILSEN_DISC , HILSEN_DSR , HILSEN_NEXT )
2005-04-17 02:20:36 +04:00
OUT_DISC ( HIL_PKT_CMD | HIL_CMD_ELB )
IN ( 10000 , HILSEN_DISC , HILSEN_DSR , HILSEN_NEXT )
OUT ( HIL_PKT_CMD | HIL_CMD_ACF | 1 )
IN ( 10000 , HILSEN_DISC0 , HILSEN_DSR , HILSEN_NEXT )
OUT_LAST ( HIL_PKT_CMD | HIL_CMD_ELB )
IN ( 10000 , HILSEN_OPERATE , HILSEN_DSR , HILSEN_DSR )
/* 52 HILSEN_DSR */
FUNC ( hilse_set_ddi , - 1 , HILSEN_NEXT , 0 , 0 )
OUT ( HIL_PKT_CMD | HIL_CMD_DSR )
2007-03-01 07:51:29 +03:00
IN ( 20000 , HILSEN_DHR , HILSEN_DHR , HILSEN_IFC )
2005-04-17 02:20:36 +04:00
/* 55 HILSEN_REPOLL */
OUT ( HIL_PKT_CMD | HIL_CMD_RPL )
EXPECT ( HIL_PKT_CMD | HIL_CMD_RPL | HIL_ERR_INT ,
20000 , HILSEN_NEXT , HILSEN_DSR , HILSEN_NEXT )
FUNC ( hilse_operate , 1 , HILSEN_OPERATE , HILSEN_IFC , HILSEN_PROBE )
/* 58 HILSEN_IFCACF */
2007-03-01 07:51:29 +03:00
OUT ( HIL_PKT_CMD | HIL_CMD_IFC )
2005-04-17 02:20:36 +04:00
EXPECT ( HIL_PKT_CMD | HIL_CMD_IFC | HIL_ERR_INT ,
20000 , HILSEN_ACF2 , HILSEN_DHR2 , HILSEN_HEAL )
/* 60 HILSEN_END */
} ;
2007-03-01 07:51:29 +03:00
static inline void hilse_setup_input ( hil_mlc * mlc , const struct hilse_node * node )
{
2005-04-17 02:20:36 +04:00
switch ( node - > act ) {
case HILSE_EXPECT_DISC :
mlc - > imatch = node - > object . packet ;
mlc - > imatch | = ( ( mlc - > ddi + 2 ) < < HIL_PKT_ADDR_SHIFT ) ;
break ;
case HILSE_EXPECT_LAST :
mlc - > imatch = node - > object . packet ;
mlc - > imatch | = ( ( mlc - > ddi + 1 ) < < HIL_PKT_ADDR_SHIFT ) ;
break ;
case HILSE_EXPECT :
mlc - > imatch = node - > object . packet ;
break ;
case HILSE_IN :
mlc - > imatch = 0 ;
break ;
default :
BUG ( ) ;
}
mlc - > istarted = 1 ;
mlc - > intimeout = node - > arg ;
do_gettimeofday ( & ( mlc - > instart ) ) ;
mlc - > icount = 15 ;
memset ( mlc - > ipacket , 0 , 16 * sizeof ( hil_packet ) ) ;
2007-03-01 07:51:29 +03:00
BUG_ON ( down_trylock ( & mlc - > isem ) ) ;
2005-04-17 02:20:36 +04:00
}
# ifdef HIL_MLC_DEBUG
2007-03-01 07:51:29 +03:00
static int doze ;
2005-04-17 02:20:36 +04:00
static int seidx ; /* For debug */
# endif
2007-03-01 07:51:29 +03:00
static int hilse_donode ( hil_mlc * mlc )
{
2007-03-01 07:51:19 +03:00
const struct hilse_node * node ;
2005-04-17 02:20:36 +04:00
int nextidx = 0 ;
int sched_long = 0 ;
unsigned long flags ;
# ifdef HIL_MLC_DEBUG
2007-03-01 07:51:29 +03:00
if ( mlc - > seidx & & mlc - > seidx ! = seidx & &
mlc - > seidx ! = 41 & & mlc - > seidx ! = 42 & & mlc - > seidx ! = 43 ) {
printk ( KERN_DEBUG PREFIX " z%i \n {%i} " , doze , mlc - > seidx ) ;
2005-04-17 02:20:36 +04:00
doze = 0 ;
}
seidx = mlc - > seidx ;
# endif
node = hil_mlc_se + mlc - > seidx ;
switch ( node - > act ) {
int rc ;
hil_packet pack ;
case HILSE_FUNC :
2007-03-01 07:51:19 +03:00
BUG_ON ( node - > object . func = = NULL ) ;
2005-04-17 02:20:36 +04:00
rc = node - > object . func ( mlc , node - > arg ) ;
2007-03-01 07:51:29 +03:00
nextidx = ( rc > 0 ) ? node - > ugly :
2005-04-17 02:20:36 +04:00
( ( rc < 0 ) ? node - > bad : node - > good ) ;
2007-03-01 07:51:29 +03:00
if ( nextidx = = HILSEN_FOLLOW )
nextidx = rc ;
2005-04-17 02:20:36 +04:00
break ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
case HILSE_EXPECT_LAST :
case HILSE_EXPECT_DISC :
case HILSE_EXPECT :
case HILSE_IN :
/* Already set up from previous HILSE_OUT_* */
2007-03-01 07:51:29 +03:00
write_lock_irqsave ( & mlc - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
rc = mlc - > in ( mlc , node - > arg ) ;
if ( rc = = 2 ) {
nextidx = HILSEN_DOZE ;
sched_long = 1 ;
2007-03-01 07:51:29 +03:00
write_unlock_irqrestore ( & mlc - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
break ;
}
2007-03-01 07:51:29 +03:00
if ( rc = = 1 )
nextidx = node - > ugly ;
else if ( rc = = 0 )
nextidx = node - > good ;
else
nextidx = node - > bad ;
2005-04-17 02:20:36 +04:00
mlc - > istarted = 0 ;
2007-03-01 07:51:29 +03:00
write_unlock_irqrestore ( & mlc - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
break ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
case HILSE_OUT_LAST :
2007-03-01 07:51:29 +03:00
write_lock_irqsave ( & mlc - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
pack = node - > object . packet ;
pack | = ( ( mlc - > ddi + 1 ) < < HIL_PKT_ADDR_SHIFT ) ;
goto out ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
case HILSE_OUT_DISC :
2007-03-01 07:51:29 +03:00
write_lock_irqsave ( & mlc - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
pack = node - > object . packet ;
pack | = ( ( mlc - > ddi + 2 ) < < HIL_PKT_ADDR_SHIFT ) ;
goto out ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
case HILSE_OUT :
2007-03-01 07:51:29 +03:00
write_lock_irqsave ( & mlc - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
pack = node - > object . packet ;
out :
2007-03-01 07:51:29 +03:00
if ( mlc - > istarted )
goto out2 ;
2005-04-17 02:20:36 +04:00
/* Prepare to receive input */
if ( ( node + 1 ) - > act & HILSE_IN )
hilse_setup_input ( mlc , node + 1 ) ;
out2 :
2007-03-01 07:51:29 +03:00
write_unlock_irqrestore ( & mlc - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
if ( down_trylock ( & mlc - > osem ) ) {
nextidx = HILSEN_DOZE ;
break ;
}
up ( & mlc - > osem ) ;
2007-03-01 07:51:29 +03:00
write_lock_irqsave ( & mlc - > lock , flags ) ;
if ( ! mlc - > ostarted ) {
2005-04-17 02:20:36 +04:00
mlc - > ostarted = 1 ;
mlc - > opacket = pack ;
mlc - > out ( mlc ) ;
nextidx = HILSEN_DOZE ;
2007-03-01 07:51:29 +03:00
write_unlock_irqrestore ( & mlc - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
break ;
}
mlc - > ostarted = 0 ;
do_gettimeofday ( & ( mlc - > instart ) ) ;
2007-03-01 07:51:29 +03:00
write_unlock_irqrestore ( & mlc - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
nextidx = HILSEN_NEXT ;
break ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
case HILSE_CTS :
2007-03-16 07:59:29 +03:00
write_lock_irqsave ( & mlc - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
nextidx = mlc - > cts ( mlc ) ? node - > bad : node - > good ;
2007-03-16 07:59:29 +03:00
write_unlock_irqrestore ( & mlc - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
break ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
default :
BUG ( ) ;
}
# ifdef HIL_MLC_DEBUG
2007-03-01 07:51:29 +03:00
if ( nextidx = = HILSEN_DOZE )
doze + + ;
2005-04-17 02:20:36 +04:00
# endif
while ( nextidx & HILSEN_SCHED ) {
struct timeval tv ;
2007-03-01 07:51:29 +03:00
if ( ! sched_long )
goto sched ;
2005-04-17 02:20:36 +04:00
do_gettimeofday ( & tv ) ;
2007-03-01 07:51:19 +03:00
tv . tv_usec + = USEC_PER_SEC * ( tv . tv_sec - mlc - > instart . tv_sec ) ;
2005-04-17 02:20:36 +04:00
tv . tv_usec - = mlc - > instart . tv_usec ;
if ( tv . tv_usec > = mlc - > intimeout ) goto sched ;
2007-03-01 07:51:19 +03:00
tv . tv_usec = ( mlc - > intimeout - tv . tv_usec ) * HZ / USEC_PER_SEC ;
2005-04-17 02:20:36 +04:00
if ( ! tv . tv_usec ) goto sched ;
mod_timer ( & hil_mlcs_kicker , jiffies + tv . tv_usec ) ;
break ;
sched :
tasklet_schedule ( & hil_mlcs_tasklet ) ;
break ;
2007-03-01 07:51:29 +03:00
}
if ( nextidx & HILSEN_DOWN )
mlc - > seidx + = nextidx & HILSEN_MASK ;
else if ( nextidx & HILSEN_UP )
mlc - > seidx - = nextidx & HILSEN_MASK ;
else
mlc - > seidx = nextidx & HILSEN_MASK ;
if ( nextidx & HILSEN_BREAK )
return 1 ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/******************** tasklet context functions **************************/
2007-03-01 07:51:29 +03:00
static void hil_mlcs_process ( unsigned long unused )
{
2005-04-17 02:20:36 +04:00
struct list_head * tmp ;
read_lock ( & hil_mlcs_lock ) ;
list_for_each ( tmp , & hil_mlcs ) {
struct hil_mlc * mlc = list_entry ( tmp , hil_mlc , list ) ;
while ( hilse_donode ( mlc ) = = 0 ) {
# ifdef HIL_MLC_DEBUG
2007-03-01 07:51:29 +03:00
if ( mlc - > seidx ! = 41 & &
mlc - > seidx ! = 42 & &
mlc - > seidx ! = 43 )
printk ( KERN_DEBUG PREFIX " + " ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-03-01 07:51:29 +03:00
}
2005-04-17 02:20:36 +04:00
}
read_unlock ( & hil_mlcs_lock ) ;
}
/************************* Keepalive timer task *********************/
2007-03-01 07:51:29 +03:00
void hil_mlcs_timer ( unsigned long data )
{
2005-04-17 02:20:36 +04:00
hil_mlcs_probe = 1 ;
tasklet_schedule ( & hil_mlcs_tasklet ) ;
/* Re-insert the periodic task. */
if ( ! timer_pending ( & hil_mlcs_kicker ) )
mod_timer ( & hil_mlcs_kicker , jiffies + HZ ) ;
}
/******************** user/kernel context functions **********************/
2007-03-01 07:51:29 +03:00
static int hil_mlc_serio_write ( struct serio * serio , unsigned char c )
{
2005-04-17 02:20:36 +04:00
struct hil_mlc_serio_map * map ;
struct hil_mlc * mlc ;
struct serio_driver * drv ;
uint8_t * idx , * last ;
map = serio - > port_data ;
2007-03-01 07:51:29 +03:00
BUG_ON ( map = = NULL ) ;
2005-04-17 02:20:36 +04:00
mlc = map - > mlc ;
2007-03-01 07:51:29 +03:00
BUG_ON ( mlc = = NULL ) ;
mlc - > serio_opacket [ map - > didx ] | =
2005-04-17 02:20:36 +04:00
( ( hil_packet ) c ) < < ( 8 * ( 3 - mlc - > serio_oidx [ map - > didx ] ) ) ;
if ( mlc - > serio_oidx [ map - > didx ] > = 3 ) {
/* for now only commands */
2007-03-01 07:51:29 +03:00
if ( ! ( mlc - > serio_opacket [ map - > didx ] & HIL_PKT_CMD ) )
2005-04-17 02:20:36 +04:00
return - EIO ;
switch ( mlc - > serio_opacket [ map - > didx ] & HIL_PKT_DATA_MASK ) {
case HIL_CMD_IDD :
idx = mlc - > di [ map - > didx ] . idd ;
goto emu ;
case HIL_CMD_RSC :
idx = mlc - > di [ map - > didx ] . rsc ;
goto emu ;
case HIL_CMD_EXD :
idx = mlc - > di [ map - > didx ] . exd ;
goto emu ;
case HIL_CMD_RNM :
idx = mlc - > di [ map - > didx ] . rnm ;
goto emu ;
default :
break ;
}
mlc - > serio_oidx [ map - > didx ] = 0 ;
mlc - > serio_opacket [ map - > didx ] = 0 ;
}
mlc - > serio_oidx [ map - > didx ] + + ;
return - EIO ;
emu :
drv = serio - > drv ;
2007-03-01 07:51:29 +03:00
BUG_ON ( drv = = NULL ) ;
2005-04-17 02:20:36 +04:00
last = idx + 15 ;
2007-03-01 07:51:29 +03:00
while ( ( last ! = idx ) & & ( * last = = 0 ) )
last - - ;
2005-04-17 02:20:36 +04:00
while ( idx ! = last ) {
2006-10-07 06:47:23 +04:00
drv - > interrupt ( serio , 0 , 0 ) ;
drv - > interrupt ( serio , HIL_ERR_INT > > 16 , 0 ) ;
drv - > interrupt ( serio , 0 , 0 ) ;
drv - > interrupt ( serio , * idx , 0 ) ;
2005-04-17 02:20:36 +04:00
idx + + ;
}
2006-10-07 06:47:23 +04:00
drv - > interrupt ( serio , 0 , 0 ) ;
drv - > interrupt ( serio , HIL_ERR_INT > > 16 , 0 ) ;
drv - > interrupt ( serio , HIL_PKT_CMD > > 8 , 0 ) ;
drv - > interrupt ( serio , * idx , 0 ) ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
mlc - > serio_oidx [ map - > didx ] = 0 ;
mlc - > serio_opacket [ map - > didx ] = 0 ;
return 0 ;
}
2007-03-01 07:51:29 +03:00
static int hil_mlc_serio_open ( struct serio * serio )
{
2005-04-17 02:20:36 +04:00
struct hil_mlc_serio_map * map ;
struct hil_mlc * mlc ;
2005-10-22 06:58:51 +04:00
if ( serio_get_drvdata ( serio ) ! = NULL )
return - EBUSY ;
2005-04-17 02:20:36 +04:00
map = serio - > port_data ;
2007-03-01 07:51:29 +03:00
BUG_ON ( map = = NULL ) ;
2005-04-17 02:20:36 +04:00
mlc = map - > mlc ;
2007-03-01 07:51:29 +03:00
BUG_ON ( mlc = = NULL ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-03-01 07:51:29 +03:00
static void hil_mlc_serio_close ( struct serio * serio )
{
2005-04-17 02:20:36 +04:00
struct hil_mlc_serio_map * map ;
struct hil_mlc * mlc ;
map = serio - > port_data ;
2007-03-01 07:51:29 +03:00
BUG_ON ( map = = NULL ) ;
2005-04-17 02:20:36 +04:00
mlc = map - > mlc ;
2007-03-01 07:51:29 +03:00
BUG_ON ( mlc = = NULL ) ;
2005-04-17 02:20:36 +04:00
2005-10-22 06:58:51 +04:00
serio_set_drvdata ( serio , NULL ) ;
2005-04-17 02:20:36 +04:00
serio - > drv = NULL ;
/* TODO wake up interruptable */
}
2007-03-01 07:51:19 +03:00
static const struct serio_device_id hil_mlc_serio_id = {
2005-10-22 06:58:51 +04:00
. type = SERIO_HIL_MLC ,
. proto = SERIO_HIL ,
. extra = SERIO_ANY ,
. id = SERIO_ANY ,
} ;
2007-03-01 07:51:29 +03:00
int hil_mlc_register ( hil_mlc * mlc )
{
2005-04-17 02:20:36 +04:00
int i ;
2007-03-01 07:51:29 +03:00
unsigned long flags ;
2005-04-17 02:20:36 +04:00
2007-03-01 07:51:29 +03:00
BUG_ON ( mlc = = NULL ) ;
2005-04-17 02:20:36 +04:00
mlc - > istarted = 0 ;
2007-03-01 07:51:29 +03:00
mlc - > ostarted = 0 ;
2005-04-17 02:20:36 +04:00
2007-03-01 07:51:29 +03:00
rwlock_init ( & mlc - > lock ) ;
init_MUTEX ( & mlc - > osem ) ;
2005-04-17 02:20:36 +04:00
2007-03-01 07:51:29 +03:00
init_MUTEX ( & mlc - > isem ) ;
mlc - > icount = - 1 ;
mlc - > imatch = 0 ;
2005-04-17 02:20:36 +04:00
mlc - > opercnt = 0 ;
2007-03-01 07:51:29 +03:00
init_MUTEX_LOCKED ( & ( mlc - > csem ) ) ;
2005-04-17 02:20:36 +04:00
hil_mlc_clear_di_scratch ( mlc ) ;
hil_mlc_clear_di_map ( mlc , 0 ) ;
for ( i = 0 ; i < HIL_MLC_DEVMEM ; i + + ) {
struct serio * mlc_serio ;
hil_mlc_copy_di_scratch ( mlc , i ) ;
2006-03-14 08:09:16 +03:00
mlc_serio = kzalloc ( sizeof ( * mlc_serio ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
mlc - > serio [ i ] = mlc_serio ;
2007-03-01 07:51:19 +03:00
snprintf ( mlc_serio - > name , sizeof ( mlc_serio - > name ) - 1 , " HIL_SERIO%d " , i ) ;
snprintf ( mlc_serio - > phys , sizeof ( mlc_serio - > phys ) - 1 , " HIL%d " , i ) ;
2005-10-22 06:58:51 +04:00
mlc_serio - > id = hil_mlc_serio_id ;
2005-04-17 02:20:36 +04:00
mlc_serio - > write = hil_mlc_serio_write ;
mlc_serio - > open = hil_mlc_serio_open ;
mlc_serio - > close = hil_mlc_serio_close ;
mlc_serio - > port_data = & ( mlc - > serio_map [ i ] ) ;
mlc - > serio_map [ i ] . mlc = mlc ;
mlc - > serio_map [ i ] . didx = i ;
mlc - > serio_map [ i ] . di_revmap = - 1 ;
mlc - > serio_opacket [ i ] = 0 ;
mlc - > serio_oidx [ i ] = 0 ;
serio_register_port ( mlc_serio ) ;
}
mlc - > tasklet = & hil_mlcs_tasklet ;
write_lock_irqsave ( & hil_mlcs_lock , flags ) ;
list_add_tail ( & mlc - > list , & hil_mlcs ) ;
mlc - > seidx = HILSEN_START ;
write_unlock_irqrestore ( & hil_mlcs_lock , flags ) ;
tasklet_schedule ( & hil_mlcs_tasklet ) ;
return 0 ;
}
2007-03-01 07:51:29 +03:00
int hil_mlc_unregister ( hil_mlc * mlc )
{
2005-04-17 02:20:36 +04:00
struct list_head * tmp ;
2007-03-01 07:51:29 +03:00
unsigned long flags ;
2005-04-17 02:20:36 +04:00
int i ;
2007-03-01 07:51:29 +03:00
BUG_ON ( mlc = = NULL ) ;
2005-04-17 02:20:36 +04:00
write_lock_irqsave ( & hil_mlcs_lock , flags ) ;
2007-03-01 07:51:29 +03:00
list_for_each ( tmp , & hil_mlcs )
2005-04-17 02:20:36 +04:00
if ( list_entry ( tmp , hil_mlc , list ) = = mlc )
goto found ;
/* not found in list */
write_unlock_irqrestore ( & hil_mlcs_lock , flags ) ;
tasklet_schedule ( & hil_mlcs_tasklet ) ;
return - ENODEV ;
found :
list_del ( tmp ) ;
2007-03-01 07:51:29 +03:00
write_unlock_irqrestore ( & hil_mlcs_lock , flags ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < HIL_MLC_DEVMEM ; i + + ) {
serio_unregister_port ( mlc - > serio [ i ] ) ;
mlc - > serio [ i ] = NULL ;
}
tasklet_schedule ( & hil_mlcs_tasklet ) ;
return 0 ;
}
/**************************** Module interface *************************/
static int __init hil_mlc_init ( void )
{
init_timer ( & hil_mlcs_kicker ) ;
hil_mlcs_kicker . expires = jiffies + HZ ;
hil_mlcs_kicker . function = & hil_mlcs_timer ;
add_timer ( & hil_mlcs_kicker ) ;
tasklet_enable ( & hil_mlcs_tasklet ) ;
return 0 ;
}
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
static void __exit hil_mlc_exit ( void )
{
del_timer ( & hil_mlcs_kicker ) ;
tasklet_disable ( & hil_mlcs_tasklet ) ;
tasklet_kill ( & hil_mlcs_tasklet ) ;
}
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
module_init ( hil_mlc_init ) ;
module_exit ( hil_mlc_exit ) ;