驅動之路四------adc驅動(input裝置)
阿新 • • 發佈:2019-02-06
開發板:smdk6410
開發環境:Linux
突然想起一點,寫這些驅動,核心需要配成支援搶佔才行。
前面的部落格已經將其它的基本知識都解釋了,這裡也就不過多的闡述了,咱就直接寫程式碼吧
這次寫的是adc驅動,將其做為輸入裝置進行使用,
先寫標頭檔案,s3c_adc.h
#ifndef __ADC_H #define __ADC_H #include <linux/device.h> #include <linux/interrupt.h> #include <linux/input.h> struct adc_info { char name[32]; int user; int status; void __iomem *v; struct input_dev *dev; struct clk *clk; struct timer_list timer; int irq; irqreturn_t (*handle)(int no, void *data); }; #define S3C_PA_ADC 0x7e00b000 #define S3C_SZ_ADC SZ_4K #define ADCCON 0x000 #define ADCTSC 0x004 #define ADCDLY 0x008 #define ADCDAT0 0x00C #define ADCDAT1 0x010 #define ADCUPDN 0x014 #define ADCCLRINT 0x018 #define ADCCLRINTPNDNUP 0x020 #define S3C_IRQ_ADC_S IRQ_ADC #define S3C_IRQ_ADC_E IRQ_ADC #endif
標頭檔案主要即使裝置資訊結構體和相關的巨集定義,
現在寫裝置檔案,
#include <linux/init.h> #include <linux/module.h> #include <linux/device.h> #include <linux/platform_device.h> #include "s3c_adc.h" void b_release(struct device *dev) { printk("Device is released\n"); } //資源也是兩類,MEM一類,IRQ一類 struct resource b_res[] = { [0] = { .start = S3C_PA_ADC, .end = S3C_PA_ADC + S3C_SZ_ADC - 1, .flags = IORESOURCE_MEM, }, [1] = { .start = S3C_IRQ_ADC_S, .end = S3C_IRQ_ADC_E, .flags = IORESOURCE_IRQ, } }; struct platform_device dev = { .name = "s3c-my-adc", .id = -1, .num_resources = ARRAY_SIZE(b_res), .resource = b_res, .dev = { .release = b_release, } }; static __init int module_test_init(void) { return platform_device_register(&dev); } static __exit void module_test_exit(void) { platform_device_unregister(&dev); } module_init(module_test_init); module_exit(module_test_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Musesea"); MODULE_VERSION("1.0"); MODULE_DESCRIPTION("Test for module");
看過前幾篇驅動的應該都直到,套路比較固定,只要將其中的很少的東西修改以下就行,就不詳細說每一步的功能了,
下面寫驅動檔案,這才是大頭,
#include <linux/init.h> #include <linux/module.h> #include <asm/uaccess.h> #include <asm/io.h> #include <linux/device.h> #include <linux/slab.h> #include <linux/platform_device.h> #include <linux/input.h> #include <linux/clk.h> #include <linux/clkdev.h> #include <linux/timer.h> #include "s3c_adc.h" void start_adc(struct adc_info *a) { u32 tmp; tmp = readl(a->v + ADCCON); tmp |= 1; writel(tmp, a->v + ADCCON); } //這個函式執行在中斷上下文,函式體內可不能含有可睡眠的函式 void do_timer(unsigned long data) { struct adc_info *a; a = (struct adc_info *)data; //中斷上下文 start_adc(a); mod_timer(&a->timer, jiffies + HZ / 2); } int get_adc(struct adc_info *a) { u32 tmp; tmp = readl(a->v + ADCDAT0); tmp = tmp & 0xfff; return tmp; } irqreturn_t do_adc(int no, void *data) { struct adc_info *a = data; int adc_val; //獲取adc轉換的結果 adc_val = get_adc(a); input_report_abs(a->dev, ABS_X, adc_val); input_sync(a->dev); //清中斷 writel(1, a->v + ADCCLRINT); return IRQ_HANDLED; } void s3c_adc_exit(struct adc_info *a) { printk("Driver is release.\n"); } void s3c_adc_init(struct adc_info *a) { u32 tmp; //tmp = readl(a->v + ADCCON); tmp = (1 << 16) | (1 << 14) | (21 << 6); writel(tmp, a->v + ADCCON); } int b_probe(struct platform_device *pdev) { struct resource *a_res, *irq_res; struct adc_info *adc; int ret; //1.申請資源 a_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if(!a_res || !irq_res) return -EBUSY; //2.分配adc_info adc = kzalloc(sizeof(struct adc_info), GFP_KERNEL); if(!adc) return -ENOMEM; //3.ioremap adc->v = ioremap(a_res->start, a_res->end - a_res->start + 1); if(!adc->v) { ret = -ENOMEM; goto remap_error; } //申請裝置結構體 adc->dev = input_allocate_device(); if(!adc->dev) { ret = -ENOMEM; goto input_allocate_device_error; } adc->dev->name = pdev->name; adc->dev->uniq = "20131113"; adc->dev->phys = "/dev/eventx"; adc->dev->id.bustype = BUS_HOST; adc->dev->id.vendor = 110; adc->dev->id.product = 120; adc->dev->id.version = 119; //4.設定該裝置要支援的事件型別 set_bit(EV_SYN, adc->dev->evbit); set_bit(EV_ABS, adc->dev->evbit); //使該裝置支援絕對的x事件 //寫絕對事件時不要使用set_bit,使用核心給出的下列函式 input_set_abs_params(adc->dev, ABS_X, 0, 4095, 0, 0); //5.註冊input裝置 ret = input_register_device(adc->dev); if(ret) goto input_register_device_error; //將adc儲存到pdev中 platform_set_drvdata(pdev, adc); //6.開啟clock;注意在初始化adc之前一定要寫開啟時鐘;只要使用時鐘的裝置,在初始化之前都要先開啟時鐘 adc->clk = clk_get(NULL, "adc"); clk_enable(adc->clk); //7.初始化adc //sprintf(adc->name, "adc"); adc->user = 0; adc->irq = irq_res->start; adc->handle = do_adc; //涉及時鐘的硬體在初始化之前一定要確認時鐘開啟 s3c_adc_init(adc); //8.申請中斷(adc) ret = request_irq(adc->irq, adc->handle, 0, pdev->name, adc); if(ret){ goto request_irq_error; } //9.建立一個adc->timer_list,定時時間為0.5s,處理函式為do_adc->timer setup_timer(&adc->timer, do_timer, (unsigned long)adc); mod_timer(&adc->timer, jiffies + HZ / 2);//這裡不要用0.5*HZ,核心是不支援浮點數的,也可以寫成(HZ>>1) return 0; free_irq(adc->irq, adc); request_irq_error: input_unregister_device(adc->dev); input_register_device_error: input_free_device(adc->dev); input_allocate_device_error: iounmap(adc->v); remap_error: kfree(adc); return ret; } int b_remove(struct platform_device *pdev) { struct adc_info *adc; adc = platform_get_drvdata(pdev); del_timer_sync(&adc->timer); free_irq(adc->irq, adc); s3c_adc_exit(adc); //clock的反操作 clk_disable(adc->clk); clk_put(adc->clk); input_free_device(adc->dev); iounmap(adc->v); kfree(adc); return 0; } struct platform_driver drv = { .driver = { .name = "s3c-my-adc", }, .probe = b_probe, .remove = b_remove, }; static __init int module_test_init(void) { return platform_driver_register(&drv); } static __exit void module_test_exit(void) { platform_driver_unregister(&drv); } module_init(module_test_init); module_exit(module_test_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Musesea"); MODULE_VERSION("1.0"); MODULE_DESCRIPTION("Test for module");
應該注意的地方在程式碼中做了標註了,至此又一個驅動搞完了。
還是那句話,大家若是發現有什麼問題,一定要告訴我,大家一起學習了。