Chinaunix首页 | 论坛 | 博客
  • 博客访问: 182530
  • 博文数量: 63
  • 博客积分: 1400
  • 博客等级: 上尉
  • 技术积分: 810
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-03 17:24
文章分类

全部博文(63)

文章存档

2011年(1)

2009年(25)

2008年(37)

我的朋友

分类: LINUX

2008-12-02 17:32:21

花了三天时间,终于把这个驱动程序看得懂了个六七分,读到的心得都在文件里注释上。贴上来免得忘记。
 
 
/*
个人感觉驱动的工作过程是这样的,设置s3c2410的touchscreen和ADC的工作方式--当笔在触摸屏上按下的时候触发中断--接收--触发ADc的中断,转换后的数据被读到一个循环链表的结构体中--放入buffer缓冲区--然后驱动的read函数用copy_to_usercopy到用户空间,这样在main函数里打开文件,读取,就可以显示了。
驱动的参考书是周立功的ARM嵌入式linux系统构建与驱动开发范例
#include "config.h"
//laputa append for get bus clk freq
#define  ADC_FREQ 2000000           // 2MHz AD convert freq 030918
static int PreScale_n;         // PCLK / (PreScale_n+1) = ADConversion freq.
//laputa end 030918
#ifdef CONFIG_PM
#include
#endif
/* debug macros */
#undef DEBUG
#ifdef DEBUG
#define DPRINTK( x... ) printk("s3c2410-ts: " ##x)
#else
#define DPRINTK( x... )
#endif
#define PEN_UP         0  
#define PEN_DOWN 1
#define PEN_FLEETING 2
#define MAX_TS_BUF 16 /* how many do we want to buffer */
#undef USE_ASYNC 1
#define DEVICE_NAME "s3c2410-ts"
#define TSRAW_MINOR 1
typedef struct { // 定义液晶屏设备结构体,成员有,笔的状态,缓冲区队列,头尾指针,循环队列,
//自旋锁
 unsigned int penStatus;  /* PEN_UP, PEN_DOWN, PEN_SAMPLE */
 TS_RET buf[MAX_TS_BUF];  /* protect against overrun */
 unsigned int head, tail; /* head and tail for queued events */
 wait_queue_head_t wq;
 spinlock_t lock;
#ifdef USE_ASYNC
 struct fasync_struct *aq;
#endif
#ifdef CONFIG_PM
 struct pm_dev *pm_dev;
#endif
} TS_DEV;
static TS_DEV tsdev;
#define BUF_HEAD (tsdev.buf[tsdev.head])
#define BUF_TAIL (tsdev.buf[tsdev.tail])
#define INCBUF(x,mod)  ((++(x)) & ((mod) - 1))
static int tsMajor = 0;
static void (*tsEvent)(void);
#define HOOK_FOR_DRAG 
#ifdef HOOK_FOR_DRAG
#define TS_TIMER_DELAY  (HZ/100) /* 10 ms */
static struct timer_list ts_timer;
#endif
//设置控制寄存器的相应位
#define wait_down_int() { ADCTSC = DOWN_INT | XP_PULL_UP_EN | \
    XP_AIN | XM_HIZ | YP_AIN | YM_GND | \
    XP_PST(WAIT_INT_MODE); } ///*设置触摸屏处于等待中断模式,笔处于抬起状态,等待笔按下,,理解了,但为什么这样写呢,我觉得 
                                                         这个应该在哪个头文件里有定义的*/
/*找到了这些头文件,在linux的内核中,
#define DOWN_INT  (UD_SEN*0)
#define UP_INT   (UD_SEN*1)
#define YM_SEN   (1 << 7)
#define YM_HIZ   (YM_SEN*0)
#define YM_GND   (YM_SEN*1)
#define YP_SEN   (1 << 6)
#define YP_EXTVLT  (YP_SEN*0)
#define YP_AIN   (YP_SEN*1)
#define XM_SEN   (1 << 5)
#define XM_HIZ   (XM_SEN*0)
#define XM_GND   (XM_SEN*1)
#define XP_SEN   (1 << 4)
#define XP_EXTVLT  (XP_SEN*0)
#define XP_AIN   (XP_SEN*1)
#define XP_PULL_UP  (1 << 3)
#define XP_PULL_UP_EN  (XP_PULL_UP*0)
#define XP_PULL_UP_DIS  (XP_PULL_UP*1)
#define AUTO_PST  (1 << 2)
#define CONVERT_MAN  (AUTO_PST*0)
#define CONVERT_AUTO  (AUTO_PST*1)
#define XP_PST(x)  (x << 0)
*/
/*
/* ADC and Touch Screen Interface */
#define ADC_CTL_BASE  0x58000000
#define bADC_CTL(Nb)  __REG(ADC_CTL_BASE + (Nb))
/* Offset */
#define oADCCON   0x00 /* R/W, ADC control register */
#define oADCTSC   0x04 /* R/W, ADC touch screen ctl reg */
#define oADCDLY   0x08 /* R/W, ADC start or interval delay reg */
#define oADCDAT0  0x0c /* R  , ADC conversion data reg */
#define oADCDAT1  0x10 /* R  , ADC conversion data reg */
/* Registers */
#define ADCCON   bADC_CTL(oADCCON)
#define ADCTSC   bADC_CTL(oADCTSC)
#define ADCDLY   bADC_CTL(oADCDLY)
#define ADCDAT0   bADC_CTL(oADCDAT0)
#define ADCDAT1   bADC_CTL(oADCDAT1)
/* Field */
#define fADCCON_PRSCVL  Fld(8, 6)
#define fADCCON_INPUT  Fld(3, 3)
#define fTSC_XY_PST  Fld(2, 0)
#define fADC_DELAY  Fld(6, 0)
#define fDAT_UPDOWN  Fld(1, 15)
#define fDAT_AUTO_PST  Fld(1, 14)
#define fDAT_XY_PST  Fld(2, 12)
#define fDAT_XPDATA  Fld(10, 0)
#define fDAT_YPDATA  Fld(10, 0)
/* ... */
#define ADC_IN0                 0
#define ADC_IN1                 1
#define ADC_IN2                 2
#define ADC_IN3                 3
#define ADC_IN4                 4
#define ADC_IN5                 5
#define ADC_IN6                 6
#define ADC_IN7                 7
#define ADC_BUSY  1
#define ADC_READY  0
#define NOP_MODE  0
#define X_AXIS_MODE  1
#define Y_AXIS_MODE  2
#define WAIT_INT_MODE  3
/* ... */
#define ADCCON_ECFLG  (1 << 15)
#define PRESCALE_ENDIS  (1 << 14)
#define PRESCALE_DIS  (PRESCALE_ENDIS*0)
#define PRESCALE_EN  (PRESCALE_ENDIS*1)
#if 0
#define PRSCVL(x)  ({ FClrFld(ADCCON, fADCCON_PRSCVL); \
       FInsrt((x), fADCCON_PRSCVL); })
#define ADC_INPUT(x)  ({ FClrFld(ADCCON, fADCCON_INPUT); \
       FInsrt((x), fADCCON_INPUT); })
#endif
#define PRSCVL(x)  (x << 6)
#define ADC_INPUT(x)  (x << 3)
#define ADCCON_STDBM  (1 << 2)        /* 1: standby mode, 0: normal mode */
#define ADC_NORMAL_MODE  FClrBit(ADCCON, ADCCON_STDBM)
#define ADC_STANDBY_MODE (ADCCON_STDBM*1)
#define ADCCON_READ_START (1 << 1)
#define ADC_START_BY_RD_DIS FClrBit(ADCCON, ADCCON_READ_START)
#define ADC_START_BY_RD_EN (ADCCON_READ_START*1)
#define ADC_START  (1 << 0)
*/
#define wait_up_int() { ADCTSC = UP_INT | XP_PULL_UP_EN | XP_AIN | XM_HIZ | \
    YP_AIN | YM_GND | XP_PST(WAIT_INT_MODE); } ///*设置触摸屏处于等待中断模式,笔处于按下状态,等待笔抬起*/
#define mode_x_axis() { ADCTSC = XP_EXTVLT | XM_GND | YP_AIN | YM_HIZ | \
    XP_PULL_UP_DIS | XP_PST(X_AXIS_MODE); } /*对X坐标进行测量*/
#define mode_x_axis_n() { ADCTSC = XP_EXTVLT | XM_GND | YP_AIN | YM_HIZ | \
    XP_PULL_UP_DIS | XP_PST(NOP_MODE); } /*基本配置和上面相同,就是不进行x 坐标的转换*/
#define mode_y_axis() { ADCTSC = XP_AIN | XM_HIZ | YP_EXTVLT | YM_GND | \
    XP_PULL_UP_DIS | XP_PST(Y_AXIS_MODE); }/*对Y 坐标开始进行测量*/
    
// laputa modi for 66MHz/(32+1) = 2MHz
// 1/(2MHz/5cycle) = 2.5uS
#define start_adc_x(n) { ADCCON = PRESCALE_EN | PRSCVL(n) | \
    ADC_INPUT(ADC_IN5) | ADC_START_BY_RD_EN | \
    ADC_NORMAL_MODE; \
     ADCDAT0; }
#define start_adc_y(n) { ADCCON = PRESCALE_EN | PRSCVL(n) | \
    ADC_INPUT(ADC_IN7) | ADC_START_BY_RD_EN | \
    ADC_NORMAL_MODE; \
     ADCDAT1; }
/*首先设置ADCCON 寄存器,开始转换x 坐标,因为在ADCCON 寄存器中,已经设置通
 
//过读取数据寄存器来启动转换,所以在执行ADCDAT0 宏时(该宏从ADCDATA0 寄存器中
 
//读取数据),A/D 转换器即开始转换数据。                                  
宏ADCDAT0 定义为:
#define ADCDAT0 bADC_CTL(oADCDAT0)
。。。。。
#define bADC_CTL(Nb) __REG(ADC_CTL_BASE + (Nb))
*/
//laputa end 030917
#define disable_ts_adc() { ADCCON &= ~(ADCCON_READ_START); }
    
static int adc_state = 0;
static int x, y; /* touch screen coorinates */
static void tsEvent_raw(void) //这个函数好像也没头绪,判断出触摸笔的状态,落笔这从缓冲区队
//伍的头部读出坐标,起笔则设置xy坐标为0?
/*当触摸笔按下的时候记录状态,并将其xy坐标值放入缓冲队列,若笔为抬起状态,则记录状态,值xy坐标值
//为0,然后等待循环队列的中断*/
{
 if (tsdev.penStatus == PEN_DOWN) {
  BUF_HEAD.x = x;
  BUF_HEAD.y = y;
  BUF_HEAD.pressure = PEN_DOWN;
#ifdef HOOK_FOR_DRAG
  ts_timer.expires = jiffies + TS_TIMER_DELAY;
  add_timer(&ts_timer);
#endif
 } else {
#ifdef HOOK_FOR_DRAG
  del_timer(&ts_timer);
#endif
  
  BUF_HEAD.x = 0;
  BUF_HEAD.y = 0;
  BUF_HEAD.pressure = PEN_UP;
 }
 tsdev.head = INCBUF(tsdev.head, MAX_TS_BUF);
 wake_up_interruptible(&(tsdev.wq));
#ifdef USE_ASYNC
 if (tsdev.aq)
  kill_fasync(&(tsdev.aq), SIGIO, POLL_IN);
#endif
#ifdef CONFIG_PM
 pm_access(tsdev.pm_dev);
#endif
}
static int tsRead(TS_RET * ts_ret) //液晶屏读函数,这个是从循环队列里读出来
{
 spin_lock_irq(&(tsdev.lock));//先锁上,防止其他的函数再使用此资源
 ts_ret->x = BUF_TAIL.x;//然后读取缓冲队列的坐标值,赋值给ts_ret
 ts_ret->y = BUF_TAIL.y;
 ts_ret->pressure = BUF_TAIL.pressure;
 tsdev.tail = INCBUF(tsdev.tail, MAX_TS_BUF);//队列尾向前移动一个,这个是循环队列的操作
 spin_unlock_irq(&(tsdev.lock));//释放自旋锁资源
 return sizeof(TS_RET);
}

static ssize_t s3c2410_ts_read(struct file *filp, char *buffer, size_t count, loff_t *ppos) //这个和上个有什么不同?这个是从系统空间读到用户空间
{
 TS_RET ts_ret;
retry:
 if (tsdev.head != tsdev.tail) {//当循环队列中的数据不为空的时候
  int count;
  count = tsRead(&ts_ret);
  if (count) copy_to_user(buffer, (char *)&ts_ret, count);//copy to user这个函数是整
//个大函数的关键句子,是主要的函数
  return count;
 } else {
  if (filp->f_flags & O_NONBLOCK)
   return -EAGAIN;
  interruptible_sleep_on(&(tsdev.wq));
  if (signal_pending(current))
   return -ERESTARTSYS;
  goto retry;//若为空,则反复读取,程序在retry这里循环
 }
 return sizeof(TS_RET);
}
#ifdef USE_ASYNC
static int s3c2410_ts_fasync(int fd, struct file *filp, int mode) //这个是什么?同
//步???
{
 return fasync_helper(fd, filp, mode, &(tsdev.aq));
}
#endif
static unsigned int s3c2410_ts_poll(struct file *filp, struct poll_table_struct *wait)
//这个poll也是文件操作的一个函数,具体什么判断缓冲队列是否为空?
{
 poll_wait(filp, &(tsdev.wq), wait);
 return (tsdev.head == tsdev.tail) ? 0 : (POLLIN | POLLRDNORM);
}
static inline void start_ts_adc(void) //开始adc转换?
{
 adc_state = 0;
 mode_x_axis();
 start_adc_x(PreScale_n);  //+- modify for ADC freq 030918
}
static inline void s3c2410_get_XY(void) //从转换好的ADCdata中读取数据
{
 
 if (adc_state == 0) {
  adc_state = 1;
  disable_ts_adc();
  y = (ADCDAT0 & 0x3ff);
  mode_y_axis();
  start_adc_y(PreScale_n); //+- modify for ADC freq 030918
 } else if (adc_state == 1) {
  adc_state = 0; 
  disable_ts_adc();
  x = (ADCDAT1 & 0x3ff);
  tsdev.penStatus = PEN_DOWN;
  DPRINTK("PEN DOWN: x: %08d, y: %08d\n", x, y);
  wait_up_int();
  tsEvent();
 }
}
static void s3c2410_isr_adc(int irq, void *dev_id, struct pt_regs *reg) //adc 中断服
//务函数,读取xy坐标值,也是先锁再读解锁
{
#if 0
 DPRINTK("Occured Touch Screen Interrupt\n");
 DPRINTK("SUBSRCPND = 0x%08lx\n", SUBSRCPND);
#endif
 spin_lock_irq(&(tsdev.lock));
 if (tsdev.penStatus == PEN_UP)
   s3c2410_get_XY();
#ifdef HOOK_FOR_DRAG
 else
   s3c2410_get_XY();
#endif
 
 spin_unlock_irq(&(tsdev.lock));
}
static void s3c2410_isr_tc(int irq, void *dev_id, struct pt_regs *reg) //液晶屏幕中
//断服务函数,先锁再读再解锁
{
 spin_lock_irq(&(tsdev.lock));
 if (tsdev.penStatus == PEN_UP) {
   start_ts_adc();
 } else {
   tsdev.penStatus = PEN_UP;
   DPRINTK("PEN UP: x: %08d, y: %08d\n", x, y);
   wait_down_int();
   tsEvent();
 }
 spin_unlock_irq(&(tsdev.lock));
}
#ifdef HOOK_FOR_DRAG
static void ts_timer_handler(unsigned long data)
{
 spin_lock_irq(&(tsdev.lock));
 if (tsdev.penStatus == PEN_DOWN) {
  start_ts_adc();
 }
 spin_unlock_irq(&(tsdev.lock));
}
#endif
static int s3c2410_ts_open(struct inode *inode, struct file *filp) //open()函数,查
//看设备的就绪状态
{
 tsdev.head = tsdev.tail = 0;
 tsdev.penStatus = PEN_UP;
#ifdef HOOK_FOR_DRAG
 init_timer(&ts_timer);
 ts_timer.function = ts_timer_handler;
#endif
 tsEvent = tsEvent_raw;
 init_waitqueue_head(&(tsdev.wq));
 MOD_INC_USE_COUNT;
 return 0;
}
static int s3c2410_ts_release(struct inode *inode, struct file *filp)//release()函
//数,卸载设备的时候执行
{
#ifdef HOOK_FOR_DRAG
 del_timer(&ts_timer);
#endif
 MOD_DEC_USE_COUNT; //设备驱动程序的使用次数计数。卸载设备的时候减一个
 return 0;
}
static struct file_operations s3c2410_fops = {//file oprerations struct
 owner: THIS_MODULE,
 open: s3c2410_ts_open,
 read: s3c2410_ts_read, 
 release: s3c2410_ts_release,
#ifdef USE_ASYNC
 fasync: s3c2410_ts_fasync,
#endif
 poll: s3c2410_ts_poll,
};
void tsEvent_dummy(void) {}
#ifdef CONFIG_PM //pm是电源管理模块
static int s3c2410_ts_pm_callback(struct pm_dev *pm_dev, pm_request_t req,void *data) //这个Pm是什么东东,还不知道
{
    switch (req) {
  case PM_SUSPEND:
   tsEvent = tsEvent_dummy;//dummy是什么都不干,void()
   break;
  case PM_RESUME:
   tsEvent = tsEvent_raw;//raw是读取坐标值
   wait_down_int();
   break;
    }
    return 0;
}
#endif
#ifdef CONFIG_DEVFS_FS
static devfs_handle_t devfs_ts_dir, devfs_tsraw;
#endif
static int __init s3c2410_ts_init(void)  //初始化驱动程序模块,linux内核在初始化的时候加
//驱动程序初始化模块
{
 int ret;
 tsEvent = tsEvent_dummy;
 
// laputa for touchscreen adc delay time setting
 ADCDLY  = 20000;     // it is got from test
 //laputa append for AD converting freq calculate
 ret = s3c2410_get_bus_clk(GET_PCLK);
 PreScale_n = (int)( ret / ADC_FREQ ) - 1 ;
 //laputa end 030918
 
 ret = register_chrdev(0, DEVICE_NAME, &s3c2410_fops);
  //这是这个函数最重要的一句,向内核注册驱动程序。第一个参数是主设备号,第二个是设备名称,第三个
//是文件操作的结构提地址
 if (ret < 0) {
   printk(DEVICE_NAME " can't get major number\n");
   return ret;
 }
 tsMajor = ret;
 /* set gpio to XP, YM, YP and  YM */
 set_gpio_ctrl(GPIO_YPON);
 set_gpio_ctrl(GPIO_YMON);
 set_gpio_ctrl(GPIO_XPON);
 set_gpio_ctrl(GPIO_XMON);
 /* Enable touch interrupt */
 ret = request_irq(IRQ_ADC_DONE, s3c2410_isr_adc, SA_INTERRUPT,
     DEVICE_NAME, s3c2410_isr_adc);
 if (ret) goto adc_failed;
 ret = request_irq(IRQ_TC, s3c2410_isr_tc, SA_INTERRUPT,
     DEVICE_NAME, s3c2410_isr_tc);
 if (ret) goto tc_failed;
 /* Wait for touch screen interrupts */
 wait_down_int();
#ifdef CONFIG_DEVFS_FS
 devfs_ts_dir = devfs_mk_dir(NULL, "touchscreen", NULL);
 devfs_tsraw = devfs_register(devfs_ts_dir, "0", DEVFS_FL_DEFAULT,     // +- 031127 dev/touchscreen/0 for standard interface  
   tsMajor, TSRAW_MINOR, S_IFCHR | S_IRUSR | S_IWUSR,
   &s3c2410_fops, NULL);
#endif
#ifdef CONFIG_PM
 tsdev.pm_dev = pm_register(PM_DEBUG_DEV, PM_USER_INPUT,
       s3c2410_ts_pm_callback);
#endif
 printk(DEVICE_NAME " initialized\n");
 return 0;
 tc_failed:
 free_irq(IRQ_ADC_DONE, s3c2410_isr_adc);
 adc_failed:
 return ret;
}
static void __exit s3c2410_ts_exit(void) //从内核中卸载驱动程序,
{
#ifdef CONFIG_DEVFS_FS 
 devfs_unregister(devfs_tsraw);
 devfs_unregister(devfs_ts_dir);
#endif 
 unregister_chrdev(tsMajor, DEVICE_NAME); //与向内核中注册驱动程序类似,这个是这个函数
//最重要的一句
#ifdef CONFIG_PM
 pm_unregister(tsdev.pm_dev);
#endif
 free_irq(IRQ_ADC_DONE, s3c2410_isr_adc);
 free_irq(IRQ_TC, s3c2410_isr_tc);
}
module_init(s3c2410_ts_init); /×这两句告诉内核在卸载驱动程序的时候执行哪些函数,这两个函数就是我们上面所实现的。×/
module_exit(s3c2410_ts_exit);
阅读(1384) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~