linux觸摸屏驅動開發中的s3c_ts_probe()函數的分析

linux觸摸屏驅動開發中的s3c_ts_probe()函數的分析


static int __init s3c_ts_probe(struct platform_device *pdev)
{
struct resource *res;
struct device *dev;
struct input_dev *input_dev;
struct s3c_ts_mach_info * s3c_ts_cfg;
int ret, size;

dev = &pdev->dev;

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {

dev_err(dev,"no memory resource specified\n");
return -ENOENT;
}



size = (res->end - res->start) + 1;
ts_mem = request_mem_region(res->start, size, pdev->name);

if (ts_mem == NULL) {
dev_err(dev, "failed to get memory region\n");
ret = -ENOENT;
goto err_req;
}



ts_base = ioremap(res->start, size);
if (ts_base == NULL) {

dev_err(dev, "failed to ioremap() region\n");
ret = -EINVAL;
goto err_map;
}


ts_clock = clk_get(&pdev->dev, "adc");

if (IS_ERR(ts_clock)) {
dev_err(dev, "failed to find watchdog clock source\n");
ret = PTR_ERR(ts_clock);
goto err_clk;
}



clk_enable(ts_clock);


s3c_ts_cfg = s3c_ts_get_platdata(&pdev->dev);

if ((s3c_ts_cfg->presc&0xff) > 0)

writel(S3C_ADCCON_PRSCEN | S3C_ADCCON_PRSCVL(s3c_ts_cfg->presc&0xFF),\
ts_base+S3C_ADCCON);

else

writel(0, ts_base+S3C_ADCCON);


/* Initialise registers */
if ((s3c_ts_cfg->delay&0xffff) > 0)
writel(s3c_ts_cfg->delay & 0xffff, ts_base+S3C_ADCDLY);


if (s3c_ts_cfg->resol_bit==12) {

switch(s3c_ts_cfg->s3c_adc_con) {
case ADC_TYPE_2:
writel(readl(ts_base+S3C_ADCCON)|S3C_ADCCON_RESSEL_12BIT, ts_base+S3C_ADCCON);
break;


case ADC_TYPE_1:
writel(readl(ts_base+S3C_ADCCON)|S3C_ADCCON_RESSEL_12BIT_1, ts_base+S3C_ADCCON);
break;

default:
dev_err(dev, "Touchscreen over this type of AP isn't supported !\n");
break;
}
}


writel(WAIT4INT(0), ts_base+S3C_ADCTSC);


ts = kzalloc(sizeof(struct s3c_ts_info), GFP_KERNEL);
input_dev = input_allocate_device();

if (!input_dev) {

ret = -ENOMEM;
goto err_alloc;
}


ts->dev = input_dev;


ts->dev->evbit[0] = ts->dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
ts->dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);


if (s3c_ts_cfg->resol_bit==12) {

input_set_abs_params(ts->dev, ABS_X, 0, 0xFFF, 0, 0);
input_set_abs_params(ts->dev, ABS_Y, 0, 0xFFF, 0, 0);
}

else {

input_set_abs_params(ts->dev, ABS_X, 0, 0x3FF, 0, 0);
input_set_abs_params(ts->dev, ABS_Y, 0, 0x3FF, 0, 0);
}


input_set_abs_params(ts->dev, ABS_PRESSURE, 0, 1, 0, 0);

sprintf(ts->phys, "input(ts)");

ts->dev->name = s3c_ts_name;
ts->dev->phys = ts->phys;
ts->dev->id.bustype = BUS_RS232;
ts->dev->id.vendor = 0xDEAD;
ts->dev->id.product = 0xBEEF;
ts->dev->id.version = S3C_TSVERSION;


ts->shift = s3c_ts_cfg->oversampling_shift;
ts->resol_bit = s3c_ts_cfg->resol_bit;
ts->s3c_adc_con = s3c_ts_cfg->s3c_adc_con;

/* For IRQ_PENDUP */
ts_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (ts_irq == NULL) {

dev_err(dev, "no irq resource specified\n");
ret = -ENOENT;
goto err_irq;
}



ret = request_irq(ts_irq->start, stylus_updown, IRQF_SAMPLE_RANDOM, "s3c_updown", ts);
if (ret != 0) {

dev_err(dev,"s3c_ts.c: Could not allocate ts IRQ_PENDN !\n");
ret = -EIO;
goto err_irq;
}



/* For IRQ_ADC */
ts_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
if (ts_irq == NULL) {

dev_err(dev, "no irq resource specified\n");
ret = -ENOENT;
goto err_irq;
}


ret = request_irq(ts_irq->start, stylus_action, IRQF_SAMPLE_RANDOM, "s3c_action", ts);
if (ret != 0) {

dev_err(dev, "s3c_ts.c: Could not allocate ts IRQ_ADC !\n");
ret =  -EIO;
goto err_irq;
}


printk(KERN_INFO "%s got loaded successfully : %d bits\n", s3c_ts_name, s3c_ts_cfg->resol_bit);

/* All went ok, so register to the input system */
ret = input_register_device(ts->dev);

if(ret) {

dev_err(dev, "s3c_ts.c: Could not register input device(touchscreen)!\n");
ret = -EIO;
goto fail;
}


return 0;

fail:

free_irq(ts_irq->start, ts->dev);
free_irq(ts_irq->end, ts->dev);


err_irq:

input_free_device(input_dev);
kfree(ts);


err_alloc:

clk_disable(ts_clock);
clk_put(ts_clock);


err_clk:

iounmap(ts_base);

err_map:

release_resource(ts_mem);
kfree(ts_mem);

err_req:

return ret;

}


首先向說一下函數中的前幾個變量:
1.第一個局部變量是struct resource類型的指針res變量,這個變量使用來描述設備的資源結構體(include\linux\ioport.h)
struct resource 
{

resource_size_t start;
resource_size_t end;
const char *name;
unsigned long flags;
struct resource *parent, *sibling, *child;

};
其中的flags位表示的是資源的類型,start和end分別表示資源的起始地址和結束地址
2.device這裏就不細講了,大家知道它是對於設備的一個封裝就行了

3.struct input_dev類型指針變量input_dev   

 input_dev 結構體成員註釋:

 struct input_dev — represents an input device//說明:下面出現的bitmap是指“位映像”
struct input_dev 

const char * name; //設備名
const char * phys; //系統層次架構中設備的物理路徑 
const char * uniq; //設備的唯一標示碼(如果設備有的話) 
struct input_id id; //設備的id (struct input_id) 
unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; //設備所支持的事件類型的bitmap (EV_KEY, EV_REL, etc.) 
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; //此設備所擁有的keys或者buttons的bitmap 
unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; //此設備所擁有的相對軸的bitmap 
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; //此設別所擁有的絕對軸的bitmap 
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)]; //設備所支持的雜項事件的bitmap 
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)]; //設備上的led的bitmap 
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)]; //設備所支持的聲音效果的bitmap 
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; //設備所支持的強制反饋效果的bitmap(force feedback) 
unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; //設備上的開關的bitmap 
unsigned int keycodemax; //鍵碼錶的大小(keycode table) 
unsigned int keycodesize; //鍵碼錶元素的大小 
void * keycode; //此設備的掃描碼到鍵碼的映射 
/*修改現有鍵映射的可選方法, 曾用來實現稀疏鍵映射. 若沒有提供則使用缺省的 */ 
int (* setkeycode) (struct input_dev *dev, int scancode, int keycode); 
/*獲取當前鍵映射的可選方法. 若未提供則使用缺省的*/ 
int (* getkeycode) (struct input_dev *dev, int scancode, int *keycode); struct ff_device * ff; 
//設備關聯的強制反饋結構體(如果設備支持強制反饋效果的話) 
unsigned int repeat_key; //存儲上個按下的鍵的鍵碼; 用於實現軟件自動重複 
struct timer_list timer; //軟件自動重複的定時器 
int sync; //自上一次EV_SYNC後再沒有新的事件時設置爲1 
int abs[ABS_MAX + 1]; //要上報的絕對座標下的當前值 
int rep[REP_MAX + 1]; //自動重複參數的當前值 (delay, rate) 
unsigned long key[BITS_TO_LONGS(KEY_CNT)]; //反映設備的keys/buttons的當前狀態 
unsigned long led[BITS_TO_LONGS(LED_CNT)]; //反映設備LED的當前狀態 
unsigned long snd[BITS_TO_LONGS(SND_CNT)]; //反映聲音效果的當前狀態 
unsigned long sw[BITS_TO_LONGS(SW_CNT)]; //反映設備開關的當前狀態 
int absmax[ABS_MAX + 1]; //來自絕對軸事件的最大值 
int absmin[ABS_MAX + 1]; //來自絕對軸事件的最小值 
int absfuzz[ABS_MAX + 1]; //描述軸的噪聲 
int absflat[ABS_MAX + 1]; //中央平坦位置的大小 (used by joydev) 
/*這個方法在第一個用戶調用input_event_device時調用。驅動必須讓設備準備好開始產生事件 (開始輪詢線程,請求一個IRQ,提交URB,等等) */ 
int (* open) (struct input_dev *dev); 
void (* close) (struct input_dev *dev); //這個方法在最後一個用戶調用input_close_device時被調用.
/*清空設備. 常用於清空失連的(disconnect)加載進設備的強制反饋效果 */ 
int (* flush) (struct input_dev *dev, struct file *file);
/*事件處理方法, 如 EV_LED 或者 EV_SND. 設備將執行一些要求的動作 (開啓 LED, 放聲音, 等等.) 此調用由event_lock保護並且不能夠休眠 */ int (* event) (struct input_dev *dev, unsigned int type, unsigned int code, int value);
/*當前佔有設備的輸入句柄 (via EVIOCGRAB ioctl). 當設備對應一個句柄,這個句柄就是來自此設備所有輸入事件的唯一接受者*/ 
struct input_handle * grab;/*當輸入核接受處理設備的心事件時此spinlock鎖定 (in input_event). */ 
spinlock_t event_lock; struct mutex mutex; //序列化對open、close、flush方法的調用
/* 存儲打開此設備的用戶數量(input handlers). 通過input_open_device和input_close_device來確保dev->open只能在被第一個用戶打開設備時調用並且確保dev->close只能在最後一個用戶關閉設備時被調用*/ 
unsigned int users; 
int going_away; //標示設備屬於未註冊的一類並且會導致input_open_device*()返回-ENODEV錯誤. 
struct device dev; //設備的驅動模型視圖 
struct list_head h_list; //和設備關聯的輸入句柄鏈表。當讀取鏈表時需要鎖定dev->mutex。 
struct list_head node; //用來把設備放入input_dev_list

}; 
//AutoRepeat:用來配置對按住某鍵不放的處理,格式爲:
//AutoRepeat 毫秒數 次數
//如:AutoRepeat 500 5,表示當按住某鍵500毫秒(0.5秒)後,開始自動送出該按鍵信號,每秒5次。
4.struct s3c_ts_mach_info指針變量s3c_ts_cfg(arch\arm\plat-s3c\include\plat\ts.h)
struct s3c_ts_mach_info 
{

int delay;//延遲時間
int presc;//預分頻值
int oversampling_shift;//轉化次數
int resol_bit;//分辨率
enum s3c_adc_type s3c_adc_con;//adc類型,跟板子的型號有關,見下面

};
其中有
enum s3c_adc_type 
{

ADC_TYPE_0,ADC_TYPE_1, /* S3C2416, S3C2450 */
ADC_TYPE_2,/* S3C64XX, S5PC1XX */

};
以上是幾個局部變量的聲明。
接下來用struct platform_device結構體參數的dev來初始化剛剛聲明的dev變量
struct platform_device 
{

const char * name;
int id;
struct device dev;
u32 num_resources;
struct resource * resource;

};
然後初始化res變量對應的平臺設備資源:在Dev-ts.c (linux2.6.28\arch\arm\plat-s3c)文件中
/* Touch srcreen */
static struct resource s3c_ts_resource[] = 
{

[0] = {
.start = S3C_PA_ADC, I/O端口
.end = S3C_PA_ADC + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_PENDN, 中斷
.end = IRQ_PENDN,
.flags = IORESOURCE_IRQ,
},
[2] = {
.start = IRQ_ADC, 中斷
.end = IRQ_ADC,
.flags = IORESOURCE_IRQ,
}
};
struct platform_device s3c_device_ts = 
{
.name = "s3c-ts",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_ts_resource),
.resource = s3c_ts_resource,

};
再爲驅動申請內存。ioremap是把物理地址重映射爲虛擬地址,便於操作系統操作。
接着申請時鐘,時鐘使能。獲取s3c6410 touchscreen machine infomation,s3c_ts_mach_info結構體(delay、presc、oversampling_shift、resol_bit)設置預分頻接口寄存器、設置delay接口寄存器、設置resol_bit接口寄存器
writel(WAIT4INT(0), ts_base+S3C_ADCTSC);設置爲等待中斷模式
ts = kzalloc(sizeof(struct s3c_ts_info), GFP_KERNEL);//申請內存
input_dev = input_allocate_device();//申請input device
struct s3c_ts_info {

struct input_dev *dev;
long xp;
long yp;
int count;
int shift;
char phys[32];
int resol_bit;
enum s3c_adc_type s3c_adc_con;

};
這就是ts的結構體,結構體的第一個元素就是input_dev結構體接着初始化input device結構體、初始化ts結構體註冊中斷函數


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章