//ts.c
#include "ypublic.h" //import the public header file
#define YDRIVER_NAME "TS_TEST" //device name
#define YMAJOR 100 //device major
MODULE_LICENSE("Dual BSD/GPL"); //license
void adc_task(ulong p); //adc sub irq routine
DECLARE_TASKLET(_adc_task,adc_task,0); //the tasklet for adc irq
#define ST_ADC_TEST 1 //a state for test stylus is up or down
#define ST_ADC_AUTOXY 2 //a state for measure x and y position
typedef struct _t_adc_dev{ //my device strtuct
//struct cdev dev; //my device,if you want add open\close...,need initialize this member
t_adcregs_p padcreg; //adc is register pointer
t_clockreg_p pclkreg; //clock and power are register pointer
struct timer_list timer; //timer
uint counter; //counter
t_fifo fifo; //an FIFO buffer
uint x; //current X
uint y; //current y
uint adc_state; //adc state
}t_adc_dev;
t_adc_dev *_padcdev; //a global device pointer
void adc_task(ulong p) //adc sub irq routine
{
uint x,y;
x=fifo_pop(&_padcdev->fifo); //get x from FIFO buffer
y=fifo_pop(&_padcdev->fifo); //get y from FIFO buffer
if(abs(_padcdev->x-x)>5||abs(_padcdev->y-y)>5) //
{
if(_padcdev->x==0xfff) //first pointer whit stylus down
printk(KERN_INFO"have stylus down\n");
_padcdev->x=x; //update current x
_padcdev->y=y; //update current y
printk(KERN_INFO"%u : ( %u , %u )\n",
_padcdev->counter++,x,y);
}
_padcdev->timer.data=1; //timer for measure x and y position
_padcdev->timer.expires=MS(10); //delay 10 ms
add_timer(&_padcdev->timer); //add timer
}
irqreturn_t on_adc(int irq,void *id) //adc irq routine
{
uint x,y;
if(_padcdev->adc_state==ST_ADC_AUTOXY) //for measure x and y position
{
x=_padcdev->padcreg->ADCDAT0&0x3ff; //get X
y=_padcdev->padcreg->ADCDAT1&0x3ff; //get Y
if(x!=0&&y!=0) //stylus was down or not
{ //stylus was down
fifo_push(&_padcdev->fifo,x); //push X value to FIFO buffer
fifo_push(&_padcdev->fifo,y); //push Y value to FIFO buffer
_padcdev->timer.data=2; //timer for test stylus was down or not
_padcdev->timer.expires=MS(1); //delay 1 ms
add_timer(&_padcdev->timer); //add timer
}
else{ //stylus isn't down
if(_padcdev->x!=0xfff) //stylus was up or not
printk(KERN_INFO"stylus up\n");
_padcdev->padcreg->ADCTSC=0xd3; //Waiting for Interrupt Mode
enable_irq(IRQ_TC); //enable touch screen interrupt
}
}
else if(_padcdev->adc_state==ST_ADC_TEST) //for test stylus is up or down
{
if((_padcdev->padcreg->ADCDAT0&0x3ff)<1000)
{ //stylus was not down
fifo_flush(&_padcdev->fifo,2); //clear the dataes of pushed on current
if(_padcdev->x!=0xfff) //stylus was up
printk(KERN_INFO"stylus up\n");
_padcdev->padcreg->ADCTSC=0xd3; //Waiting for Interrupt Mode
enable_irq(IRQ_TC); //enable touch screen interrupt
}
else
{
tasklet_schedule(&_adc_task); //run sub irq routine on free time
}
}
return IRQ_HANDLED;
}
irqreturn_t on_tc(int irq,void* id)
{
disable_irq_nosync(irq); //disable touch screen interrupt
_padcdev->y=0xfff; //initialize X andY for a impossible value
_padcdev->x=_padcdev->y; //X and Y region was (0-0x3ff)
_padcdev->padcreg->ADCTSC&=0xffc; //clear INT_TC bit by change the mode to no operation mode
_padcdev->timer.data=1; //timer for measure x and y position
_padcdev->timer.expires=MS(25); //delay 25 ms
add_timer(&_padcdev->timer); //add timer
return IRQ_HANDLED; //irq return
}
void on_timer(ulong d)
{
switch(d){
case 1:
{
_padcdev->padcreg->ADCTSC=0x90|SETB(3)|SETB(2); // XP pull-up disable and auto sequential measurement of X-position, Y-position
_padcdev->padcreg->ADCCON|=0x1; //adc conversion start
_padcdev->adc_state=ST_ADC_AUTOXY; //adc conversion for measure x and y
break;
}
case 2:
{
_padcdev->adc_state=ST_ADC_TEST; //adc conversion for test stylus was down or not
_padcdev->padcreg->ADCTSC=SETB(4); //YP output heigh level,XP was used as analog input, and no operation mode
_padcdev->padcreg->ADCCON|=setNB(7,_padcdev->padcreg->ADCCON,3,5)|0x1;//start adc conversion from XP
break;
}
}
}
int __init init_adc(void)
{
int rs=0; //result
dev_t dev=MKDEV(YMAJOR,0); //device ID
_padcdev=kmalloc(sizeof(t_adc_dev),GFP_KERNEL);//malloc space for device
if(_padcdev==NULL)
{
rs=-ENOMEM; //no memery
goto fail1;
}
fifo_init(&_padcdev->fifo); //initialize FIFO buffer struct
_padcdev->pclkreg=(t_clockreg_p)yioremap(CLOCKBASE,sizeof(t_clockreg),"clkio"); //remap clock and power registers base address
if(_padcdev->pclkreg==NULL){
rs=-EBUSY; //clock register was used by other device
goto fail2;
}
else{
_padcdev->pclkreg->CLKCON|=SETB(15); //enable adc clk
yiounmap(CLOCKBASE,_padcdev->pclkreg,sizeof(t_clockreg)); //release the right that uding clock register
}
_padcdev->padcreg=(t_adcregs_p)yioremap(ADCBASE,sizeof(t_adcregs),"adcio"); //remap adc registers base address
if(_padcdev->padcreg==NULL){
rs=-EBUSY; //adc register was used by other device
goto fail2;
}
rs=register_chrdev_region(dev,1,YDRIVER_NAME);//register char device
if(rs<0){
rs=-EBUSY; //register char device failed
goto fail3;
}
rs=request_irq(IRQ_ADC,on_adc,IRQF_DISABLED,"adc_int",NULL); //request ADC interruput
if(rs)goto fail4; //request ADC interrupt failed
rs=request_irq(IRQ_TC,on_tc,IRQF_DISABLED,"tc_int",NULL); //request touch screen interrupt
if(rs)goto fail5; //request touch screen interrupt failed
_padcdev->counter=0; //initialize the tounter to zero
init_timer(&_padcdev->timer); //initialize timer
_padcdev->timer.function=&on_timer; //specify the timer is routine
printk(KERN_INFO"TS driver installed\n"); //tip driver install success
_padcdev->padcreg->ADCCON=(0x1<<14)|(0x13<<6); //ADC clock=PCLK/(0x13+1)
_padcdev->padcreg->ADCTSC=0xd3; //Waiting for Interrupt Mode
return rs;
fail5:
free_irq(IRQ_ADC,NULL); //free adc irq
fail4:
unregister_chrdev_region(MKDEV(YMAJOR,0),1); //unregister the char device
fail3:
yiounmap(ADCBASE,_padcdev->padcreg,sizeof(t_adcregs)); //unremap ADC registeres
fail2:
kfree(_padcdev); //free device space
fail1:
return rs;
}
void __exit exit_adc(void)
{
free_irq(IRQ_TC,NULL); //free touch screen irq
free_irq(IRQ_ADC,NULL); //free adc irq
del_timer(&_padcdev->timer); //delete timer
yiounmap(ADCBASE,_padcdev->padcreg,sizeof(t_adcregs)); //unremap ADC registeres
kfree(_padcdev); //free device space
unregister_chrdev_region(MKDEV(YMAJOR,0),1); //unregister the char device
}
module_init(init_adc); //register the module for initialize driver
module_exit(exit_adc); //register the module for release driver
MODULE_AUTHOR("MrY"); //driver's author
MODULE_VERSION("0.1"); //driver's version
|