Rumah > Operasi dan penyelenggaraan > operasi dan penyelenggaraan linux > Adakah kernel linux mempunyai fungsi gangguan?

Adakah kernel linux mempunyai fungsi gangguan?

青灯夜游
Lepaskan: 2022-11-11 14:40:54
asal
2454 orang telah melayarinya

Inti Linux mempunyai fungsi gangguan. Dalam kernel Linux, jika anda ingin menggunakan sampukan, anda perlu memohonnya, dan fungsi request_irq() digunakan untuk memohon sampukan Selepas sampukan digunakan, sampukan yang sepadan mesti dikeluarkan melalui free_irq(. ) fungsi; terdapat juga enable_irq() dan disable_irq( ), ia digunakan untuk mendayakan dan melumpuhkan gangguan tertentu.

Adakah kernel linux mempunyai fungsi gangguan?

Persekitaran pengendalian tutorial ini: sistem linux7.3, komputer Dell G3.

1.Linux interrupt

1.1 Linux interrupt API function

request_irq function

Dalam kernel Linux, jika anda ingin menggunakan interupsi, anda perlu memohonnya Fungsi request_irq digunakan untuk memohon gangguan Fungsi request_irq boleh menyebabkan tidur, jadi fungsi request_irq tidak boleh digunakan dalam gangguan konteks atau segmen kod lain yang melarang tidur. Fungsi request_irq akan mengaktifkan (mendayakan) gangguan, jadi kita tidak perlu mendayakan gangguan secara manual Prototaip fungsi request_irq adalah seperti berikut:
Adakah kernel linux mempunyai fungsi gangguan?
irq: Nombor gangguan. untuk memohon gangguan itu.
pengendali: Fungsi pemprosesan sampukan, fungsi pemprosesan sampukan ini akan dilaksanakan apabila sampukan berlaku.
bendera: Bendera gangguan, anda boleh melihat semua bendera gangguan dalam fail include/linux/interrupt.h
Adakah kernel linux mempunyai fungsi gangguan?
nama: Nama gangguan, Selepas menetapkan, anda boleh melihat nama gangguan yang sepadan dalam fail /proc/interrupts.
dev: Jika bendera ditetapkan kepada IRQF_SHARED, dev digunakan untuk membezakan gangguan yang berbeza Secara amnya, dev ditetapkan kepada struktur peranti dan dev akan dihantar ke bahagian kedua fungsi pengendalian gangguan parameter irq_handler_t.
Nilai pulangan: 0 bermakna aplikasi sampukan berjaya, nilai negatif lain bermakna aplikasi sampukan gagal Jika -EBUSY dikembalikan, bermakna sampukan telah dipohon.

free_irq

Apabila menggunakan gangguan, anda perlu memohon melalui fungsi request_irq Selepas penggunaan selesai, gangguan yang sepadan mesti dikeluarkan melalui fungsi free_irq. Jika gangguan tidak dikongsi, free_irq mengalih keluar pengendali gangguan dan melumpuhkan gangguan. Prototaip fungsi free_irq adalah seperti berikut:
Adakah kernel linux mempunyai fungsi gangguan?
Maksud parameter fungsi dan nilai pulangan adalah seperti berikut:
irq: Gangguan yang akan dilepaskan.
dev: Jika gangguan ditetapkan kepada dikongsi (IRQF_SHARED), parameter ini digunakan untuk membezakan gangguan tertentu. Gangguan yang dikongsi hanya akan dilumpuhkan apabila pengendali gangguan terakhir dikeluarkan.
Nilai pulangan: Tiada.

Fungsi pemprosesan sampukan

Apabila menggunakan fungsi request_irq untuk memohon gangguan, anda perlu menetapkan fungsi pemprosesan sampukan Format fungsi pemprosesan sampukan adalah sebagai berikut:
Adakah kernel linux mempunyai fungsi gangguan?
Adakah kernel linux mempunyai fungsi gangguan?

Sampuk daya daya dan lumpuhkan fungsi

Fungsi penggunaan sampukan dan lumpuhkan yang biasa digunakan adalah seperti berikut:
Adakah kernel linux mempunyai fungsi gangguan?
enable_irq dan disable_irq digunakan untuk mendayakan dan melumpuhkan sampukan yang ditentukan, irq ialah nombor sampukan yang akan dilumpuhkan. Fungsi disable_irq tidak akan kembali sehingga fungsi pengendalian gangguan yang sedang dilaksanakan selesai Oleh itu, pengguna perlu memastikan tiada gangguan baharu akan dijana dan semua pengendali gangguan yang telah mula melaksanakan telah keluar. Dalam kes ini, anda boleh menggunakan fungsi melumpuhkan sampukan yang lain:
Adakah kernel linux mempunyai fungsi gangguan?
disable_irq_nosync Fungsi kembali serta-merta selepas dipanggil dan tidak menunggu pengendali gangguan semasa menyelesaikan pelaksanaan. Tiga fungsi di atas semuanya mendayakan atau melumpuhkan gangguan tertentu Kadangkala kita perlu mematikan keseluruhan sistem gangguan pemproses semasa, yang sering dikatakan mematikan gangguan global semasa mempelajari STM32 fungsi:
Adakah kernel linux mempunyai fungsi gangguan?
local_irq_enable digunakan untuk mendayakan sistem gangguan pemproses semasa, dan local_irq_disable digunakan untuk melumpuhkan sistem gangguan pemproses semasa. Jika tugasan A memanggil local_irq_disable untuk mematikan gangguan global untuk 10S, tugasan B mula berjalan apabila ia dimatikan untuk 2S Tugasan B juga memanggil local_irq_disable untuk mematikan gangguan global selama 3S, tugasan B memanggil fungsi local_irq_enable untuk menghidupkan gangguan global. Pada masa ini, 2 3 = 5 saat telah berlalu, dan kemudian gangguan global dihidupkan Pada masa ini, keinginan tugas A untuk mematikan gangguan global selama 10 saat hancur, dan kemudian tugas A menjadi "marah". Hasilnya sangat serius Mungkin sistem Mereka semua akan dimusnahkan oleh Tugas A. Untuk menyelesaikan masalah ini, tugas B tidak boleh secara mudah dan kasar menghidupkan gangguan global melalui fungsi local_irq_enable, tetapi memulihkan status gangguan kepada keadaan sebelumnya Dengan mengambil kira perasaan tugas lain, dua fungsi berikut mesti digunakan pada masa ini. separuh bawah dan separuh bawah bermaksud perkara yang sama. Fungsi perkhidmatan sampukan yang kami daftarkan semasa menggunakan request_irq untuk memohon sampukan tergolong dalam bahagian atas pemprosesan sampukan Selagi sampukan dicetuskan, fungsi pemprosesan sampukan akan dilaksanakan. Kita semua tahu bahawa fungsi pemprosesan gangguan mesti dilaksanakan secepat mungkin, dan lebih pendek lebih baik, tetapi realitinya selalunya kejam Sesetengah proses pemprosesan gangguan lebih memakan masa, dan kita mesti memprosesnya untuk mengurangkan masa pelaksanaan fungsi pemprosesan gangguan. Sebagai contoh, skrin sentuh kapasitif memberitahu SOC bahawa peristiwa sentuhan berlaku melalui gangguan SOC bertindak balas kepada gangguan, kemudian membaca nilai koordinat sentuhan melalui antara muka IIC dan melaporkannya kepada sistem. Tetapi kita semua tahu bahawa kelajuan maksimum IIC hanya 400Kbit/S, jadi membaca data melalui IIC semasa gangguan akan membuang masa. Kami boleh melaksanakan sementara operasi membaca data sentuhan melalui IIC, dan fungsi pemprosesan gangguan hanya bertindak balas kepada gangguan, dan kemudian mengosongkan bit bendera gangguan. Pada masa ini, proses pemprosesan gangguan terbahagi kepada dua bahagian:
Separuh atas: Separuh atas adalah fungsi pemprosesan sampukan yang lebih cepat dan tidak mengambil masa yang lama boleh diselesaikan di bahagian atas. Adakah kernel linux mempunyai fungsi gangguan?Bahagian bawah: Jika proses pemprosesan gangguan memakan masa, maka kod yang memakan masa ini akan dikeluarkan dan diserahkan kepada bahagian bawah untuk dilaksanakan, supaya fungsi pemprosesan gangguan akan pantas masuk dan keluar.

Oleh itu, tujuan utama kernel Linux membahagikan sampukan kepada bahagian atas dan bawah adalah untuk mencapai cepat masuk dan keluar daripada fungsi pemprosesan sampukan tersebut yang sensitif masa dan pantas boleh diletakkan dalam fungsi pemprosesan sampukan Itu adalah bahagian atas. Semua kerja yang tinggal boleh dilaksanakan di bahagian bawah Sebagai contoh, data disalin ke memori di bahagian atas, dan pemprosesan khusus data boleh dilaksanakan di bahagian bawah. Bagi kod yang tergolong dalam bahagian atas dan kod mana yang tergolong dalam bahagian bawah, tiada peraturan yang jelas Semua pertimbangan adalah berdasarkan penggunaan sebenar. Ini akan menguji kemahiran penulis pemandu. Berikut adalah beberapa titik rujukan yang boleh digunakan untuk rujukan:

① Jika kandungan yang akan diproses tidak mahu diganggu oleh gangguan lain, maka ia boleh diletakkan di bahagian atas. ② Jika tugasan yang perlu diproses adalah sensitif masa, anda boleh meletakkannya di bahagian atas. ③. Jika tugasan yang akan diproses berkaitan dengan perkakasan, ia boleh diletakkan di bahagian atas

④ Untuk tugasan selain daripada tiga mata di atas, keutamaan harus diletakkan di bahagian bawah.

Mekanisme separuh bawah:


Gangguan lembut

Pada mulanya, kernel Linux menyediakan mekanisme "separuh bawah" untuk melaksanakan bahagian bawah, dirujuk sebagai "BH". Kemudian, gangguan lembut dan tasklet diperkenalkan untuk menggantikan mekanisme "BH" dan tasklet boleh digunakan untuk menggantikan BH Bermula dari versi 2.5 kernel Linux, BH telah ditinggalkan. Kernel Linux menggunakan struktur softirq_action untuk mewakili gangguan lembut Struktur softirq_action ditakrifkan dalam fail include/linux/interrupt.h Kandungannya adalah seperti berikut:
Adakah kernel linux mempunyai fungsi gangguan?
Sebanyak 10 ditakrifkan dalam. fail kernel/softirq.c Gangguan lembut, seperti ditunjukkan di bawah:
Adakah kernel linux mempunyai fungsi gangguan?
NR_SOFTIRQS ialah jenis penghitungan, ditakrifkan dalam fail include/linux/interrupt.h, dan ditakrifkan seperti berikut:
Adakah kernel linux mempunyai fungsi gangguan?
Seperti yang dapat dilihat, Terdapat 10 softirqs kesemuanya, jadi NR_SOFTIRQS ialah 10, jadi array softirq_vec mempunyai 10 elemen. Pembolehubah ahli tindakan dalam struktur softirq_action ialah fungsi perkhidmatan gangguan lembut Tatasusunan softirq_vec ialah tatasusunan global, jadi semua CPU (untuk sistem SMP) boleh mengaksesnya Setiap CPU mempunyai mekanisme pencetus dan kawalannya sendiri sampukan lembut yang dicetuskan dengan sendirinya. Walau bagaimanapun, fungsi perkhidmatan gangguan lembut yang dilaksanakan oleh setiap CPU sememangnya sama, dan semuanya adalah fungsi tindakan yang ditakrifkan dalam tatasusunan softirq_vec. Untuk menggunakan gangguan lembut, anda mesti menggunakan fungsi open_softirq terlebih dahulu untuk mendaftarkan fungsi pemprosesan gangguan lembut yang sepadan Prototaip fungsi open_softirq adalah seperti berikut:
Adakah kernel linux mempunyai fungsi gangguan?
nr: The soft. sampukan untuk didayakan adalah dalam kod sampel 51.1 Pilih satu daripada .2.3.
tindakan: Fungsi pemprosesan sepadan dengan gangguan lembut.
Nilai pulangan: Tiada nilai pulangan.
Selepas mendaftarkan gangguan lembut, ia perlu dicetuskan melalui fungsi raise_softirq Prototaip fungsi raise_softirq adalah seperti berikut:
Adakah kernel linux mempunyai fungsi gangguan?
Gangguan lembut mesti didaftarkan secara statik semasa penyusunan! Kernel Linux menggunakan fungsi softirq_init untuk memulakan softirqs Fungsi softirq_init ditakrifkan dalam fail kernel/softirq.c Kandungan fungsi adalah seperti berikut:
Adakah kernel linux mempunyai fungsi gangguan?

tasklet

Tasklet ialah satu lagi mekanisme separuh bawah yang dilaksanakan menggunakan sampukan lembut Antara sampukan lembut dan tasklet, anda disyorkan untuk menggunakan tasklet. Fungsi func pada baris 489 struktur kernel Linux


Adakah kernel linux mempunyai fungsi gangguan? ialah fungsi pemprosesan yang akan dilaksanakan oleh tasklet Kandungan fungsi yang ditentukan pengguna adalah setara dengan fungsi pemprosesan gangguan. Jika anda ingin menggunakan tasklet, anda mesti mentakrifkan tasklet terlebih dahulu, dan kemudian gunakan fungsi tasklet_init untuk memulakan tasklet Prototaip fungsi taskled_init adalah seperti berikut:

Adakah kernel linux mempunyai fungsi gangguan?
Maksud parameter fungsi dan nilai pulangan adalah seperti berikut: Adakah kernel linux mempunyai fungsi gangguan?
t
: Tasklet yang akan dimulakanfunc
: Fungsi pemprosesan tasklet. data
: Parameter untuk dihantar ke fungsi fungsi Nilai pulangan
: Tiada nilai pulangan. Anda juga boleh menggunakan DECLARE_TASKLET makro untuk melengkapkan takrifan dan permulaan tugasan sekaligus DECLARE_TASKLET ditakrifkan dalam fail include/linux/interrupt.h dan ditakrifkan seperti berikut:

di mana. name ialah nama tasklet yang akan ditakrifkan , nama ini ialah pembolehubah masa jenis tasklet_struct, func ialah fungsi pemprosesan tasklet, dan data ialah parameter yang dihantar ke fungsi func. Adakah kernel linux mempunyai fungsi gangguan?Di bahagian atas, iaitu memanggil fungsi tasklet_schedule dalam fungsi pemprosesan gangguan boleh membuat tasklet berjalan pada masa yang sesuai Prototaip fungsi tasklet_schedule adalah seperti berikut:


The. contoh penggunaan rujukan bagi tasklet adalah seperti berikut :Adakah kernel linux mempunyai fungsi gangguan?

Adakah kernel linux mempunyai fungsi gangguan?
Adakah kernel linux mempunyai fungsi gangguan?

Baris Beratur Kerja

Baris gilir kerja ialah satu lagi kaedah pelaksanaan separuh bawah Baris gilir kerja dilaksanakan dalam konteks proses konteks, baris gilir kerja membenarkan tidur atau penjadualan semula. Oleh itu, jika kerja yang anda ingin tangguhkan boleh tidur, maka anda boleh memilih baris gilir kerja, jika tidak, anda hanya boleh memilih gangguan lembut atau tasklet.
Inti Linux menggunakan struktur work_struct untuk mewakili kerja, kandungannya adalah seperti berikut (kompilasi bersyarat diabaikan):
Adakah kernel linux mempunyai fungsi gangguan?
Kerja ini disusun dalam baris gilir kerja dan baris gilir kerja diwakili oleh struktur workqueue_struct, kandungannya adalah seperti berikut (diabaikan) Kompilasi bersyarat):
Adakah kernel linux mempunyai fungsi gangguan?
Inti Linux menggunakan benang pekerja untuk memproses setiap kerja dalam baris gilir kerja Inti Linux menggunakan struktur pekerja untuk mewakili benang pekerja. Kandungan struktur pekerja adalah seperti berikut:
Adakah kernel linux mempunyai fungsi gangguan?
Seperti yang dapat dilihat dari kod sampel 51.1.2.10, setiap pekerja mempunyai baris gilir kerja, dan benang pekerja memproses semua kerja dalam barisan kerjanya sendiri. Dalam pembangunan pemandu sebenar, kita hanya perlu mentakrifkan kerja (work_struct), dan pada asasnya kita tidak perlu risau tentang baris gilir kerja dan benang pekerja. Hanya mencipta kerja adalah sangat mudah, hanya tentukan pembolehubah struktur work_struct, dan kemudian gunakan makro INIT_WORK untuk memulakan kerja Makro INIT_WORK ditakrifkan seperti berikut:
Adakah kernel linux mempunyai fungsi gangguan?
Adakah kernel linux mempunyai fungsi gangguan?
<.>Adakah kernel linux mempunyai fungsi gangguan?

1.3 Nod maklumat sampukan pokok peranti

Jika anda menggunakan pepohon peranti, anda perlu menetapkan maklumat atribut sampukan dalam pepohon peranti Kernel Linux membaca maklumat atribut sampukan pokok peranti untuk mengkonfigurasi gangguan. Untuk pengawal gangguan, rujuk dokumen Documentation/devicetree/bindings/arm/gic.txt untuk maklumat pengikatan pokok peranti. Buka fail imx6ull.dtsi Nod intc ialah nod pengawal gangguan I.MX6ULL Kandungan nod adalah seperti berikut:
Adakah kernel linux mempunyai fungsi gangguan?
Baris 2, nilai atribut yang serasi ialah "arm, cortex-a7. - gic" dan cari "arm,cortex-a7-gic" dalam kod sumber kernel Linux untuk mencari fail pemacu pengawal gangguan GIC.
Baris 3, #selinterrupt-sel adalah sama dengan #sel-alamat, #sel-saiz. Menunjukkan saiz sel peranti di bawah pengawal gangguan ini Untuk peranti, atribut interrupts digunakan untuk menerangkan maklumat interrupt-cells menerangkan saiz sel atribut interrupts, iaitu, berapa banyak sel yang ada untuk satu. mesej. Setiap sel ialah nilai integer 32-bit Untuk GIC yang diproses oleh ARM, terdapat 3 sel secara keseluruhannya. gangguan PPI.
Sel kedua: nombor sampukan untuk sampukan SPI, julat nombor sampukan ialah 0~987 untuk sampukan PPI, julat nombor sampukan ialah 0~15.
Sel ketiga: bendera, bit[3:0] menunjukkan jenis pencetus sampukan Apabila ia adalah 1, ia menunjukkan pencetus tepi meningkat Apabila ia adalah 2, ia menunjukkan pencetus tepi jatuh Apabila ia adalah 4. ia menunjukkan pencetus tahap tinggi Apabila 8, ia bermakna pencetus tahap rendah. bit[15:8] ialah topeng CPU bagi gangguan PPI.
Dalam baris 4, nod pengawal sampukan kosong, menunjukkan bahawa nod semasa ialah pengawal sampukan.
Untuk gpio, nod gpio juga boleh digunakan sebagai pengawal gangguan Contohnya, kandungan nod gpio5 dalam fail imx6ull.dtsi adalah seperti berikut:

Adakah kernel linux mempunyai fungsi gangguan?Barisan 4, mencelah. menerangkan maklumat sumber sampukan, Untuk gpio5, terdapat dua jenis sampukan ialah kedua-dua SPI dan tahap pencetus ialah IRQ_TYPE_LEVEL_HIGH. Perbezaannya terletak pada sumber gangguan, satu ialah 74 dan satu lagi ialah 75. Buka bab "Bab 3 Gangguan dan Peristiwa DMA" bagi "Manual Rujukan IMX6ULL" dan cari Jadual 3-1, seperti yang ditunjukkan dalam Rajah 50.1.3.1:

Adakah kernel linux mempunyai fungsi gangguan?Seperti yang dapat dilihat daripada Rajah 50.1.3.1, GPIO5 menggunakan sejumlah 2 nombor interupsi, satu ialah 74 dan satu lagi ialah 75. Antaranya, 74 sepadan dengan 16 IO yang lebih rendah daripada GPIO5_IO00~GPIO5_IO15, dan 75 sepadan dengan 16 IO yang lebih tinggi daripada GPIO5_IO16~GPIOI5_IO31. Baris 8, pengawal gangguan menunjukkan bahawa nod gpio5 juga merupakan pengawal gangguan, digunakan untuk mengawal gangguan semua IO
gpio5.
Baris 9, tukar #sel gangguan kepada 2.
Buka fail imx6ull-alientek-emmc.dts dan cari kandungan berikut:

Adakah kernel linux mempunyai fungsi gangguan?
Adakah kernel linux mempunyai fungsi gangguan?
Adakah kernel linux mempunyai fungsi gangguan?

1.4 Dapatkan gangguan nombor

Apabila menulis pemandu, anda perlu menggunakan nombor sampukan Kami menggunakan nombor sampukan telah ditulis ke dalam pepohon peranti, jadi nombor peranti yang sepadan boleh diekstrak daripada mengganggu atribut melalui fungsi irq_of_parse_and_map Prototaip fungsi adalah seperti berikut:


Adakah kernel linux mempunyai fungsi gangguan?Maksud parameter fungsi dan nilai pulangan adalah seperti berikut:

dev: nod peranti.
indeks: nombor indeks Atribut sampukan mungkin mengandungi berbilang maklumat sampukan.
Nilai pulangan: nombor gangguan. Jika anda menggunakan GPIO, anda boleh menggunakan fungsi gpio_to_irq untuk mendapatkan nombor gangguan yang sepadan dengan gpio Prototaip fungsi adalah seperti berikut:

Adakah kernel linux mempunyai fungsi gangguan?

2 🎜>

#include <linux/types.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/ide.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/errno.h> 
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <asm/mach/map.h>
#include <linux/timer.h>
#include <linux/jiffies.h>

#define IMX6UIRQ_CNT            1               /* 设备号个数 */
#define IMX6UIRQ_NAME           "irqDev"        /* 名字 */
#define KEY0VALUE               0X01            /* KEY0 按键值 */
#define INVAKEY                 0XFF            /* 无效的按键值 */
#define KEY_NUM                 1               /* 按键数量 */

/* 可能会有好多按键,通过结构体数组来描述按键 */
/* 中断 IO 描述结构体 */
struct irq_keydesc {
    int gpio;                               /* gpio */
    int irqnum;                             /* 中断号 */
    unsigned char value;                    /* 按键对应的键值 */
    char name[10];                          /* 名字 */
    irqreturn_t (*handler)(int, void *);    /* 中断服务函数 */
};

/* irq设备结构体 */
struct imx6uirq_dev {
    dev_t               devid;          /* 设备号 */
    struct cdev         cdev;           /* 字符设备 */
    struct class        *class;         /* 类 */
    struct device       *device;        /* 设备 */
    int                 major;          /* 注设备号 */
    int                 minor;          /* 次设备号 */
    struct device_node  *nd;            /* 设备节点 */

    atomic_t            keyvalue;       /* 有效的按键键值 */
    atomic_t            releasekey;     /* 标记是否完成一次完成的按键*/
    struct timer_list   timer;          /* 定义一个定时器*/
    struct irq_keydesc  irqkeydesc[KEY_NUM]; /* 按键描述数组 */
    unsigned char       curkeynum;      /* 当前的按键号 */
};

struct imx6uirq_dev  irqDev; /* 定义LED结构体 */

/* @description : 中断服务函数,开启定时器,延时 10ms,
* 定时器用于按键消抖。
* 两个参数是中断处理函数的必须写法
* @param - irq : 中断号
* @param - dev_id : 设备结构。
* @return : 中断执行结果
*/
static irqreturn_t key0_handler(int irq, void *dev_id)
{
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *)dev_id;

    /* 采用定时器削抖,如果再定时器时间内还是这个值,说明是真的按下了,在定时器中断中处理 */
    /* 这里设置为0是简易处理,因为只有一个按键 */
    /* 有其他按键要再建一个中断处理函数,并把curkeynum改成相应的按键值 */
    /* 注意不能所有按键用一个中断函数,第一是一起按的时候会出错 */
    /* 第二,无法用curkeynum判断使用的是第几个按键 */
    dev->curkeynum = 0;
    /* 传递给定时器的参数,注意要强转,在中断处理函数里面再转回来 */
    dev->timer.data = (volatile long)dev_id;
    /* mod_timer会启动定时器,第二个参数是要修改的超时时间 */
    mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10));
    return IRQ_RETVAL(IRQ_HANDLED);
}

 /* @description : 定时器服务函数,用于按键消抖,定时器到了以后
* 再次读取按键值,如果按键还是处于按下状态就表示按键有效。
* @param – arg : 设备结构变量
* @return : 无
*/
void timer_function(unsigned long arg)
{
    unsigned char value;
    unsigned char num;
    struct irq_keydesc *keydesc;
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *)arg; 

    /* 因为只有一个按键,这里是0 */
    num = dev->curkeynum;
    keydesc = &dev->irqkeydesc[num]; 
    value = gpio_get_value(keydesc->gpio); /* 读取 IO 值 */

    if(value == 0){ /* 按下按键 */
        atomic_set(&dev->keyvalue, keydesc->value);
    }
    else{ /* 按键松开 */
        /* 这种情况是按下再松开的松开,使用keyValue加上releaseKey */
        /* 没按下的话, releasekey一直为0*/
        atomic_set(&dev->keyvalue, 0x80 | keydesc->value);
        atomic_set(&dev->releasekey, 1); /* 标记松开按键 */ 
    } 
}

/*
* @description : 按键 IO 初始化
* @param : 无
* @return : 无
*/
static int keyio_init(void)
{
    unsigned char i = 0;

    int ret = 0;

    /* 1.获取key节点 */
    irqDev.nd = of_find_node_by_path("/key");
    if (irqDev.nd== NULL){
        printk("key node not find!\r\n");
        return -EINVAL;
    }
    /* 对每个按键都提取 GPIO */
    for (i = 0; i < KEY_NUM; i++) {
        irqDev.irqkeydesc[i].gpio = of_get_named_gpio(irqDev.nd, "key-gpios", i);
        if (irqDev.irqkeydesc[i].gpio < 0) {
            printk("can&#39;t get key%d\r\n", i);
        }
    }

    /* 初始化 key 所使用的 IO,并且设置成中断模式 */
    for (i = 0; i < KEY_NUM; i++) {
        /* 先对每一个IO命名 */
        /* 先对命名清0 */
        memset(irqDev.irqkeydesc[i].name, 0, sizeof(irqDev.irqkeydesc[i].name)); 
        /* 给IO命名 */
        sprintf(irqDev.irqkeydesc[i].name, "KEY%d", i); 
        /* 请求GPIO */
        gpio_request(irqDev.irqkeydesc[i].gpio, irqDev.irqkeydesc[i].name);
        
        /* 设置GPIO为输入 */
        gpio_direction_input(irqDev.irqkeydesc[i].gpio); 

        /* 获取中断号,以下为两个方法,都可以获取到 */
        /* 从interrupts属性里面获取 */
        /* 注意i是根据设备树里面设置了多少个就是多少个,都会获取到 */
        /* 下面的方法是通用的获取中断号的函数 */
        irqDev.irqkeydesc[i].irqnum = irq_of_parse_and_map(irqDev.nd, i);
#if 0
        /* 此方法是gpio获取中断号的方法 */
        irqDev.irqkeydesc[i].irqnum = gpio_to_irq(irqDev.irqkeydesc[i].gpio);
#endif
        printk("key%d:gpio=%d, irqnum=%d\r\n", i, irqDev.irqkeydesc[i].gpio,
                                                  irqDev.irqkeydesc[i].irqnum);
    }

    /* 2. 按键中断初始化 */
    /* 设置中断处理函数和按键初始值 */
    /* 因为只有一个key0.,所以这里也没用循环 */
    irqDev.irqkeydesc[0].handler = key0_handler;
    irqDev.irqkeydesc[0].value = KEY0VALUE;
     /* 申请中断 */
    for (i = 0; i < KEY_NUM; i++) {
        /* request_irq参数
         * 中断号,中断函数,中断触发类型,中断名字,传递给中断处理函数的参数(第二个),这里传的结构体
         * */
        ret = request_irq(irqDev.irqkeydesc[i].irqnum, irqDev.irqkeydesc[i].handler,
                        IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
                        irqDev.irqkeydesc[i].name, &irqDev);
        if(ret < 0){
            printk("irq %d request failed!\r\n", irqDev.irqkeydesc[i].irqnum);
            return -EFAULT;
        }
    }

    /* 3. 创建定时器 */
    init_timer(&irqDev.timer);
    irqDev.timer.function = timer_function;
    /* 注意下面不能让定时器运行,因为要按下按键之后再运行 */
    /* 启动定时器通过mod_timer启动,通常在初始化阶段的定时器用的是add_timer */
    return 0;
}


static int imx6uirq_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &irqDev;
    return 0;
}

static int imx6uirq_release(struct inode *inode, struct file *filp)
{
    //struct imx6uirq_dev  *dev = (struct imx6uirq_dev  *)filp->private_data;
    return 0;
}

 /*
* @description : 从设备读取数据
* @param – filp : 要打开的设备文件(文件描述符)
* @param – buf : 返回给用户空间的数据缓冲区
* @param - cnt : 要读取的数据长度
* @param – offt : 相对于文件首地址的偏移
* @return : 读取的字节数,如果为负值,表示读取失败
*/
static ssize_t imx6uirq_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    int ret = 0;
    unsigned char keyvalue = 0;     /* 按键值 */
    unsigned char releasekey = 0;   /* 标记是否一次完成 */
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;

    keyvalue = atomic_read(&dev->keyvalue);
    releasekey = atomic_read(&dev->releasekey);

    if (releasekey) { /* 有按键按下 */
        if (keyvalue & 0x80) {
            keyvalue &= ~0x80; /* 因为中断中或了一个0x80,这里面去掉0x80 */
            ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue));
        } else {
            goto data_error;
        }
        atomic_set(&dev->releasekey, 0); /* 按下标志清零 */
    } else { /* 没有按下 */
        goto data_error;
    }
    return 0;

data_error:
    return -EINVAL;
}

/* 字符设备操作集 */
static const struct file_operations imx6uirq_fops = {
    .owner = THIS_MODULE,
    .open = imx6uirq_open,
    .release = imx6uirq_release,
    .read = imx6uirq_read
};

/* 模块入口函数 */
static int __init imx6uirq_init(void)
{
    /* 定义一些所需变量 */
    int ret = 0;

    /* 1. 注册字符设备驱动 */
    irqDev.major = 0;
    if(irqDev.major) {
        irqDev.devid = MKDEV(irqDev.major, 0);
        ret = register_chrdev_region(irqDev.devid, IMX6UIRQ_CNT, IMX6UIRQ_NAME );  
    } else {
        alloc_chrdev_region(&irqDev.devid, 0, IMX6UIRQ_CNT, IMX6UIRQ_NAME );
        irqDev.major = MAJOR(irqDev.devid);
        irqDev.minor = MINOR(irqDev.devid);
    }
    if(ret < 0){
        goto fail_devid;
    }
    printk("Make devid success! \r\n");
    printk("major = %d, minor = %d \r\n", irqDev.major, irqDev.minor);

    /* 2. 初始化cdev */
    irqDev.cdev.owner = THIS_MODULE;
    cdev_init(&irqDev.cdev, &imx6uirq_fops);
    ret = cdev_add(&irqDev.cdev, irqDev.devid, IMX6UIRQ_CNT);
    if (ret < 0){
        goto fail_cdev;
    } else {
        printk("Cdev add sucess! \r\n");
    }

    /* 3. 自动创建设备节点 */
    irqDev.class = class_create(THIS_MODULE, IMX6UIRQ_NAME );
    if(IS_ERR(irqDev.class)) {
        ret = PTR_ERR(irqDev.class);
        goto fail_class;
    } else {
        printk("Class create sucess! \r\n");
    }

    irqDev.device = device_create(irqDev.class, NULL, irqDev.devid, NULL, IMX6UIRQ_NAME );
    if(IS_ERR(irqDev.device)) {
        ret = PTR_ERR(irqDev.device);
        goto fail_device;
    } else {
        printk("Device create sucess! \r\n");
    }

    /* 4.初始化按键 */
    atomic_set(&irqDev.keyvalue, INVAKEY);
    atomic_set(&irqDev.releasekey, 0);
    keyio_init();

    printk("irqDev init! \r\n");
    return 0;

/* 错误处理 */
fail_device:
    class_destroy(irqDev.class);
fail_class:
    cdev_del(&irqDev.cdev);
fail_cdev:
    unregister_chrdev_region(irqDev.devid, IMX6UIRQ_CNT);
fail_devid:
    return ret;
}

/* 模块出口函数 */
static void __exit imx6uirq_exit(void)
{
    unsigned int i = 0;
    /* 删除定时器 */
    del_timer_sync(&irqDev.timer); 

    /* 释放中断 */
    for (i = 0; i < KEY_NUM; i++) {
        free_irq(irqDev.irqkeydesc[i].irqnum, &irqDev);
    }

    /* 1. 释放设备号 */
    cdev_del(&irqDev.cdev);
    /* 2. 注销设备号 */
    unregister_chrdev_region(irqDev.devid, IMX6UIRQ_CNT);
    /* 3. 摧毁设备 */
    device_destroy(irqDev.class, irqDev.devid);
    /* 4.摧毁类 */
    class_destroy(irqDev.class);


    printk("irqDev exit! \r\n");
}

/* 模块入口和出口注册 */
module_init(imx6uirq_init);
module_exit(imx6uirq_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Shao Zheming");
Salin selepas log masuk
3. Kod aplikasi

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "linux/ioctl.h"

/* 
 * argc: 应用程序参数个数
 * argv[]: 参数是什么,具体的参数,说明参数是字符串的形式
 * .chrdevbaseApp <filename> <0:1> 0表示关灯,1表示开灯
 * .chrdevbaseApp /dev/led 0 关灯
 * .chrdevbaseApp /dev/led 1 开灯
 * */


int main(int argc, char *argv[])
{
    if(argc != 2)
    {
        printf("Error Usage!\r\n");
        return -1;
    }
    
    int fd, ret;
    char *filename;
    unsigned char data;

    filename = argv[1];
    fd = open(filename, O_RDWR);
    if(fd < 0)
    {
        printf("file %s open failed! \r\n", filename);
        return -1;
    }

    while (1) {
        ret = read(fd, &data, sizeof(data));
        if (ret < 0) { /* 数据读取错误或者无效 */
        
        } else { /* 数据读取正确 */
            if (data) /* 读取到数据 */
                printf("key value = %#X\r\n", data);
        }
    }

    close(fd);
    return 0;
}
Salin selepas log masuk
Salin selepas log masuk
4 >

5 Bahagian bawah pemprosesan baris gilir kerja

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "linux/ioctl.h"

/* 
 * argc: 应用程序参数个数
 * argv[]: 参数是什么,具体的参数,说明参数是字符串的形式
 * .chrdevbaseApp <filename> <0:1> 0表示关灯,1表示开灯
 * .chrdevbaseApp /dev/led 0 关灯
 * .chrdevbaseApp /dev/led 1 开灯
 * */


int main(int argc, char *argv[])
{
    if(argc != 2)
    {
        printf("Error Usage!\r\n");
        return -1;
    }
    
    int fd, ret;
    char *filename;
    unsigned char data;

    filename = argv[1];
    fd = open(filename, O_RDWR);
    if(fd < 0)
    {
        printf("file %s open failed! \r\n", filename);
        return -1;
    }

    while (1) {
        ret = read(fd, &data, sizeof(data));
        if (ret < 0) { /* 数据读取错误或者无效 */
        
        } else { /* 数据读取正确 */
            if (data) /* 读取到数据 */
                printf("key value = %#X\r\n", data);
        }
    }

    close(fd);
    return 0;
}
Salin selepas log masuk
Salin selepas log masuk

Kaedah pembangunan adalah sama dengan taskletPerhatikan bahawa kerja boleh menyimpulkan pembangun peranti struktur, jadi secara amnya letakkan kerja dalam struktur pembangun


Cadangan berkaitan: "Tutorial Video Linux"

Atas ialah kandungan terperinci Adakah kernel linux mempunyai fungsi gangguan?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Label berkaitan:
sumber:php.cn
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan