第一步移植触摸屏驱动:
1.准备源码:
s3c24xx-adc.h
view plain
#ifndef_S3C2410_ADC_H_ #define_S3C2410_ADC_H_ #defineADC_WRITE(ch,prescale)((ch)<<16|(prescale)) #defineADC_WRITE_GETCH(data)(((data)>>16)&0x7) #defineADC_WRITE_GETPRE(data)((data)&0xff) #endif/*_S3C2410_ADC_H_*/
TE2440II_adc.c
view plain
#include<linux/errno.h> #include<linux/kernel.h> #include<linux/module.h> #include<linux/slab.h> #include<linux/input.h> #include<linux/init.h> #include<linux/serio.h> #include<linux/delay.h> #include<linux/clk.h> #include<asm/io.h> #include<asm/irq.h> #include<asm/uaccess.h> #include<mach/regs-clock.h> #include<plat/regs-timer.h> #include<plat/regs-adc.h> #include<mach/regs-gpio.h> #include<linux/cdev.h> #include<linux/miscdevice.h> #include"s3c24xx-adc.h" #undefDEBUG //#defineDEBUG #ifdefDEBUG #defineDPRINTK(x...){printk(__FUNCTION__"(%d):",__LINE__);printk(##x);} #else #defineDPRINTK(x...)(void)(0) #endif #defineDEVICE_NAME"adc" staticvoid__iomem*base_addr; typedefstruct{ wait_queue_head_twait; intchannel; intprescale; }ADC_DEV; DECLARE_MUTEX(ADC_LOCK); staticintOwnADC=0; staticADC_DEVadcdev; staticvolatileintev_adc=0; staticintadc_data; staticstructclk*adc_clock; #defineADCCON(*(volatileunsignedlong*)(base_addr+S3C2410_ADCCON))//ADCcontrol #defineADCTSC(*(volatileunsignedlong*)(base_addr+S3C2410_ADCTSC))//ADCtouchscreencontrol #defineADCDLY(*(volatileunsignedlong*)(base_addr+S3C2410_ADCDLY))//ADCstartorIntervalDelay #defineADCDAT0(*(volatileunsignedlong*)(base_addr+S3C2410_ADCDAT0))//ADCconversiondata0 #defineADCDAT1(*(volatileunsignedlong*)(base_addr+S3C2410_ADCDAT1))//ADCconversiondata1 #defineADCUPDN(*(volatileunsignedlong*)(base_addr+0x14))//StylusUp/Downinterruptstatus #definePRESCALE_DIS(0<<14) #definePRESCALE_EN(1<<14) #definePRSCVL(x)((x)<<6) #defineADC_INPUT(x)((x)<<3) #defineADC_START(1<<0) #defineADC_ENDCVT(1<<15) #defineSTART_ADC_AIN(ch,prescale)/ do{/ ADCCON=PRESCALE_EN|PRSCVL(prescale)|ADC_INPUT((ch));/ ADCCON|=ADC_START;/ }while(0) staticirqreturn_tadcdone_int_handler(intirq,void*dev_id) { if(OwnADC){ adc_data=ADCDAT0&0x3ff; ev_adc=1; wake_up_interruptible(&adcdev.wait); } returnIRQ_HANDLED; } staticssize_ts3c2410_adc_read(structfile*filp,char*buffer,size_tcount,loff_t*ppos) { charstr[20]; intvalue; size_tlen; if(down_trylock(&ADC_LOCK)==0){ OwnADC=1; START_ADC_AIN(adcdev.channel,adcdev.prescale); wait_event_interruptible(adcdev.wait,ev_adc); ev_adc=0; DPRINTK("AIN[%d]=0x%04x,%d/n",adcdev.channel,adc_data,ADCCON&0x80?1:0); value=adc_data; sprintf(str,"%5d",adc_data); copy_to_user(buffer,(char*)&adc_data,sizeof(adc_data)); OwnADC=0; up(&ADC_LOCK); }else{ value=-1; } len=sprintf(str,"%d/n",value); if(count>=len){ intr=copy_to_user(buffer,str,len); returnr?r:len; }else{ return-EINVAL; } } staticints3c2410_adc_open(structinode*inode,structfile*filp) { init_waitqueue_head(&(adcdev.wait)); adcdev.channel=0; adcdev.prescale=0xff; DPRINTK("adcopened/n"); return0; } staticints3c2410_adc_release(structinode*inode,structfile*filp) { DPRINTK("adcclosed/n"); return0; } staticstructfile_operationsdev_fops={ owner:THIS_MODULE, open:s3c2410_adc_open, read:s3c2410_adc_read, release:s3c2410_adc_release, }; staticstructmiscdevicemisc={ .minor=MISC_DYNAMIC_MINOR, .name=DEVICE_NAME, .fops=&dev_fops, }; staticint__initdev_init(void) { intret; base_addr=ioremap(S3C2410_PA_ADC,0x20); if(base_addr==NULL){ printk(KERN_ERR"Failedtoremapregisterblock/n"); return-ENOMEM; } adc_clock=clk_get(NULL,"adc"); if(!adc_clock){ printk(KERN_ERR"failedtogetadcclocksource/n"); return-ENOENT; } clk_enable(adc_clock); /*normalADC*/ ADCTSC=0; ret=request_irq(IRQ_ADC,adcdone_int_handler,IRQF_SHARED,DEVICE_NAME,&adcdev); if(ret){ iounmap(base_addr); returnret; } ret=misc_register(&misc); printk(DEVICE_NAME"/tinitialized/n"); returnret; } staticvoid__exitdev_exit(void) { free_irq(IRQ_ADC,&adcdev); iounmap(base_addr); if(adc_clock){ clk_disable(adc_clock); clk_put(adc_clock); adc_clock=NULL; } misc_deregister(&misc); } EXPORT_SYMBOL(ADC_LOCK); module_init(dev_init); module_exit(dev_exit); MODULE_LICENSE("GPL");
s3c2410_ts.c
view plain
#include<linux/errno.h> #include<linux/kernel.h> #include<linux/module.h> #include<linux/slab.h> #include<linux/input.h> #include<linux/init.h> #include<linux/serio.h> #include<linux/delay.h> #include<linux/platform_device.h> #include<linux/clk.h> #include<asm/io.h> #include<asm/irq.h> #include<plat/regs-adc.h> #include<mach/regs-gpio.h> /*Forts.dev.id.version*/ #defineS3C2410TSVERSION0x0101 #defineWAIT4INT(x)(((x)<<8)|/ S3C2410_ADCTSC_YM_SEN|S3C2410_ADCTSC_YP_SEN|S3C2410_ADCTSC_XP_SEN|/ S3C2410_ADCTSC_XY_PST(3)) #defineAUTOPST(S3C2410_ADCTSC_YM_SEN|S3C2410_ADCTSC_YP_SEN|S3C2410_ADCTSC_XP_SEN|/ S3C2410_ADCTSC_AUTO_PST|S3C2410_ADCTSC_XY_PST(0)) staticchar*s3c2410ts_name="s3c2410TouchScreen"; staticstructinput_dev*dev; staticlongxp; staticlongyp; staticintcount; externstructsemaphoreADC_LOCK; staticintOwnADC=0; staticvoid__iomem*base_addr; staticinlinevoids3c2410_ts_connect(void) { s3c2410_gpio_cfgpin(S3C2410_GPG12,S3C2410_GPG12_XMON); s3c2410_gpio_cfgpin(S3C2410_GPG13,S3C2410_GPG13_nXPON); s3c2410_gpio_cfgpin(S3C2410_GPG14,S3C2410_GPG14_YMON); s3c2410_gpio_cfgpin(S3C2410_GPG15,S3C2410_GPG15_nYPON); } staticvoidtouch_timer_fire(unsignedlongdata) { unsignedlongdata0; unsignedlongdata1; intupdown; data0=ioread32(base_addr+S3C2410_ADCDAT0); data1=ioread32(base_addr+S3C2410_ADCDAT1); updown=(!(data0&S3C2410_ADCDAT0_UPDOWN))&&(!(data1&S3C2410_ADCDAT0_UPDOWN)); if(updown){ if(count!=0){ longtmp; tmp=xp; xp=yp; yp=tmp; xp>>=2; yp>>=2; #ifdefCONFIG_TOUCHSCREEN_MY2440_DEBUG structtimevaltv; do_gettimeofday(&tv); printk(KERN_DEBUG"T:%06d,X:%03ld,Y:%03ld/n",(int)tv.tv_usec,xp,yp); #endif input_report_abs(dev,ABS_X,xp); input_report_abs(dev,ABS_Y,yp); input_report_key(dev,BTN_TOUCH,1); input_report_abs(dev,ABS_PRESSURE,1); input_sync(dev); } xp=0; yp=0; count=0; iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE|AUTOPST,base_addr+S3C2410_ADCTSC); iowrite32(ioread32(base_addr+S3C2410_ADCCON)|S3C2410_ADCCON_ENABLE_START,base_addr+S3C2410_ADCCON); }else{ count=0; input_report_key(dev,BTN_TOUCH,0); input_report_abs(dev,ABS_PRESSURE,0); input_sync(dev); iowrite32(WAIT4INT(0),base_addr+S3C2410_ADCTSC); if(OwnADC){ OwnADC=0; up(&ADC_LOCK); } } } staticstructtimer_listtouch_timer= TIMER_INITIALIZER(touch_timer_fire,0,0); staticirqreturn_tstylus_updown(intirq,void*dev_id) { unsignedlongdata0; unsignedlongdata1; intupdown; if(down_trylock(&ADC_LOCK)==0){ OwnADC=1; data0=ioread32(base_addr+S3C2410_ADCDAT0); data1=ioread32(base_addr+S3C2410_ADCDAT1); updown=(!(data0&S3C2410_ADCDAT0_UPDOWN))&&(!(data1&S3C2410_ADCDAT0_UPDOWN)); if(updown){ touch_timer_fire(0); }else{ OwnADC=0; up(&ADC_LOCK); } } returnIRQ_HANDLED; } staticirqreturn_tstylus_action(intirq,void*dev_id) { unsignedlongdata0; unsignedlongdata1; if(OwnADC){ data0=ioread32(base_addr+S3C2410_ADCDAT0); data1=ioread32(base_addr+S3C2410_ADCDAT1); xp+=data0&S3C2410_ADCDAT0_XPDATA_MASK; yp+=data1&S3C2410_ADCDAT1_YPDATA_MASK; count++; if(count<(1<<2)){ iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE|AUTOPST,base_addr+S3C2410_ADCTSC); iowrite32(ioread32(base_addr+S3C2410_ADCCON)|S3C2410_ADCCON_ENABLE_START,base_addr+S3C2410_ADCCON); }else{ mod_timer(&touch_timer,jiffies+1); iowrite32(WAIT4INT(1),base_addr+S3C2410_ADCTSC); } } returnIRQ_HANDLED; } staticstructclk*adc_clock; staticint__inits3c2410ts_init(void) { structinput_dev*input_dev; adc_clock=clk_get(NULL,"adc"); if(!adc_clock){ printk(KERN_ERR"failedtogetadcclocksource/n"); return-ENOENT; } clk_enable(adc_clock); base_addr=ioremap(S3C2410_PA_ADC,0x20); if(base_addr==NULL){ printk(KERN_ERR"Failedtoremapregisterblock/n"); return-ENOMEM; } /*ConfigureGPIOs*/ s3c2410_ts_connect(); iowrite32(S3C2410_ADCCON_PRSCEN|S3C2410_ADCCON_PRSCVL(0xFF),/ base_addr+S3C2410_ADCCON); iowrite32(0xffff,base_addr+S3C2410_ADCDLY); iowrite32(WAIT4INT(0),base_addr+S3C2410_ADCTSC); /*Initialiseinputstuff*/ input_dev=input_allocate_device(); if(!input_dev){ printk(KERN_ERR"Unabletoallocatetheinputdevice!!/n"); return-ENOMEM; } dev=input_dev; dev->evbit[0]=BIT(EV_SYN)|BIT(EV_KEY)|BIT(EV_ABS); dev->keybit[BITS_TO_LONGS(BTN_TOUCH)]=BIT(BTN_TOUCH); input_set_abs_params(dev,ABS_X,0,0x3FF,0,0); input_set_abs_params(dev,ABS_Y,0,0x3FF,0,0); input_set_abs_params(dev,ABS_PRESSURE,0,1,0,0); dev->name=s3c2410ts_name; dev->id.bustype=BUS_RS232; dev->id.vendor=0xDEAD; dev->id.product=0xBEEF; dev->id.version=S3C2410TSVERSION; /*Getirqs*/ if(request_irq(IRQ_ADC,stylus_action,IRQF_SHARED|IRQF_SAMPLE_RANDOM, "s3c2410_action",dev)){ printk(KERN_ERR"s3c2410_ts.c:CouldnotallocatetsIRQ_ADC!/n"); iounmap(base_addr); return-EIO; } if(request_irq(IRQ_TC,stylus_updown,IRQF_SAMPLE_RANDOM, "s3c2410_action",dev)){ printk(KERN_ERR"s3c2410_ts.c:CouldnotallocatetsIRQ_TC!/n"); iounmap(base_addr); return-EIO; } printk(KERN_INFO"%ssuccessfullyloaded/n",s3c2410ts_name); /*Allwentok,soregistertotheinputsystem*/ input_register_device(dev); return0; } staticvoid__exits3c2410ts_exit(void) { disable_irq(IRQ_ADC); disable_irq(IRQ_TC); free_irq(IRQ_TC,dev); free_irq(IRQ_ADC,dev); if(adc_clock){ clk_disable(adc_clock); clk_put(adc_clock); adc_clock=NULL; } input_unregister_device(dev); iounmap(base_addr); } module_init(s3c2410ts_init); module_exit(s3c2410ts_exit);
2. 部署驱动源代码到内核中:
view plain
#cp-fs3c24xx-adc.hTE2440II_adc.c/linux-2.6.30.4/drivers/char/ #cp-fs3c2410_ts.c/linux-2.6.30.4/drivers/input/touchscreen/
3. 将驱动模块添加到内核配置文件中
view plain
#geditlinux-2.6.30.4/drivers/char/Kconfig//添加如下内容 configTE2440II_ADC bool"ADCdeviceforTE2440II" defaulty
view plain
#geditlinux-2.6.30.4/drivers/char/Makefile//添加如下内容 obj-$(CONFIG_TE2440II_ADC)+=TE2440II_adc.o
view plain
#geditlinux-2.6.30.4/drivers/input/touchscreen/Kconfig//添加如下内容 configTOUCHSCREEN_TE2440II tristate"TE2440IItouchscreensinputdriver" defaulty configTOUCHSCREEN_TE2440II_DEBUG boolean"TE2440IItouchscreensinputdriverdebugmessages" dependsonTOUCHSCREEN_TE2440II help Selectthisifyouwantdebugmessages
view plain
#geditlinux-2.6.30.4/drivers/input/touchscreen/Makefile//添加如下内容 obj-$(CONFIG_TOUCHSCREEN_TE2440II)+=s3c2410_ts.o
4. 修改内核配置选项
view plain
DeviceDrivers---> Inputdevicesupport---> (320)Horizontalscreenresolution (240)Verticalscreenresolution [*]Touchscreens---> <*>TE2440IItouchscreensinputdriver(NEW) [*]TE2440IItouchscreensinputdriverdebugmessages Characterdevices---> [*]ADCdeviceforTE2440II(NEW)
这里说一下,如果打开evbug,可以显示出type,code,value的值,而不只是乱码。
第二步:移植tslib。
1.安装工具:automake,autoconf,libtool。
2.编译:
./autogen.sh
./configure --host=arm-linux --prefix=/opt/tslib
make
make install
可能报错 “ts_test.c:(.text+0x200): undefined reference to `rpl_malloc'”,
原因是在 tslib-1.4/config.h 中有一行定义 “#define malloc rpl_malloc”,
直接注释掉这行定义即可,除非你自己实现了一个 malloc 版本。
3.
安装后, 主机 /opt/tslib 存在如下目录:
bin etc include lib
拷贝主机 /opt/tslib 目录至开发板 /opt/ 目录。
4.
在开发板运行触摸屏校正:
(1) 在运行触摸屏校正之前,需要设置一些环境变量,以下是我的开发板的 /etc/profile 文件的内容:
view plain
#Ashprofile #vim:syntax=sh #Nocorefilesbydefault ulimit-S-c0>/dev/null2>&1 USER="`id-un`" LOGNAME=$USER PS1='[/u@/h/W]/#' PATH=$PATH HOSTNAME=`/bin/hostname` exportUSERLOGNAMEPS1PATH exportTSLIB_ROOT=/opt/tslib exportTSLIB_TSDEVICE=/dev/event0 exportTSLIB_CONFFILE=$TSLIB_ROOT/etc/ts.conf exportTSLIB_PLUGINDIR=$TSLIB_ROOT/lib/ts exportTSLIB_CALIBFILE=/etc/pointercal exportTSLIB_CONSOLEDEVICE=none exportTSLIB_FBDEVICE=/dev/fb0 exportTS_INFO_FILE=/sys/class/input/input0/uevent exportLD_LIBRARY_PATH=lib:/usr/lib:$TSLIB_ROOT/lib:$LD_LIBRARY_PATH
运行触摸屏校正:
# cd /opt/tslib/bin
# ./ts_calibrate
报错 “No raw modules loaded.”, 原因是缺省时,/opt/tslib/etc/ts.conf 中所有的 raw module 都注释掉了,打开第一项 “module_raw input” 即可 。ts.conf文件中的各个设置选项之前不能有空格,否则会出现:Segmentationfault错误。
秀一下效果: