linux驅動編寫(字元裝置編寫架構)

來源:互聯網
上載者:User

【 聲明:著作權,歡迎轉載,請勿用於商業用途。  聯絡信箱:feixiaoxing @163.com】


    上次我們編寫了一個簡單的字元裝置,但是涉及的內容比較少,只有open和read兩個函數。今天,我們打算在此基礎上擴充一下內容。基本的思路是這樣的:(1)編寫字元裝置下需要處理的各個函數,包括open、release、read、write、ioctl、lseek函數;(2)編寫一個使用者側的程式來驗證我們編寫的驅動函數是否正確。當然,我們編寫的代碼部分參考了宋寶華先生的《linux裝置驅動開發詳解》一書,在此說明一下。
    在開始今天的內容之前,其實有一些題外話可以和大家分享一下。自從工作以來,我個人一直都有一個觀點。那就怎麼樣利用簡單的代碼來說明開發中的問題,或者是解釋軟體中的原理,這是一個很高的學問。有些道理看上去雲裡霧裡說不清楚,其實都可以通過編寫代碼來驗證的。os可以、cpu可以、cache可以、編譯器可以、網路通訊協定也可以,很多很多的內容完全可以通過幾行代碼就可以表達得非常清楚,但是事實上我們並沒有這麼做。我想原因無非是這麼幾條,一來授業者對相關知識的學習也是停留在概念上而已,二來我們的學習過於死板和教條、太關注知識、不求實踐,三就是學習者自身缺少思考的能力、缺少自我反省的能力、對很多東西不求甚解。對於簡單的linux裝置,我們完全可以通過這幾行代碼說清楚問題,免得大家還要苦苦追尋,百思而不得入門。
    好了,說了這麼多,我們看看現在的驅動代碼是怎麼修改的把。
#include <linux/module.h>#include <linux/types.h>#include <linux/fs.h>#include <linux/errno.h>#include <linux/mm.h>#include <linux/sched.h>#include <linux/init.h>#include <linux/cdev.h>#include <asm/io.h>#include <asm/system.h>#include <asm/uaccess.h>#define CHRMEM_SIZE 0x1000#define MEM_CLEAR   0x1static int chr_major;struct chr_dev{struct cdev cdev;unsigned char mem[CHRMEM_SIZE];};struct chr_dev* char_devp;int chr_open(struct inode* inode, struct file* filp){filp->private_data = char_devp;return 0;}int chr_release(struct inode* inode, struct file* filp){return  0;}static int chr_ioctl(struct inode* inode, struct file* filp, unsigned int cmd, unsigned long arg){struct chr_dev* dev = filp->private_data;switch(cmd){case MEM_CLEAR:memset(dev->mem, 0, CHRMEM_SIZE);break;default:return -EINVAL;}return 0;}static ssize_t chr_read(struct file* filp, char __user* buf, size_t size, loff_t* ppos){unsigned long p = *ppos;unsigned int count = size;int ret = 0;struct chr_dev* dev = filp->private_data;if(p >= CHRMEM_SIZE){return 0;}if(count > CHRMEM_SIZE - p){return 0;}if(copy_to_user(buf, (void*)(dev->mem + p), count)){return -EINVAL;}else{*ppos += count;ret = count;}return ret;}static ssize_t chr_write(struct file* filp, const char __user* buf, ssize_t size, loff_t *ppos){unsigned long p = *ppos;unsigned int count = size;int ret = 0;struct chr_dev* dev = filp->private_data;if(p >= CHRMEM_SIZE){return 0;}if(count > CHRMEM_SIZE - p){count = CHRMEM_SIZE - p;}if(copy_from_user(dev->mem + p, buf, count)){ret = -EINVAL;}else{*ppos += count;ret = count;}return ret;}static loff_t chr_llseek(struct file* filp, loff_t offset, int orig){loff_t ret = 0;/* orig can be SEEK_SET, SEEK_CUR, SEEK_END */switch(orig){case 0:if(offset < 0){ret = -EINVAL;break;}if((unsigned int) offset > CHRMEM_SIZE){ret = -EINVAL;break;}filp->f_pos = (unsigned int) offset;ret = filp->f_pos;break;case 1:if((filp->f_pos + offset) > CHRMEM_SIZE){ret = -EINVAL;break;}if((filp->f_pos + offset) < 0){ret = -EINVAL;break;}filp->f_pos += offset;ret = filp->f_pos;break;default:ret = - EINVAL;break;}return ret;}static const struct file_operations chr_ops = {.owner    = THIS_MODULE,.llseek   = chr_llseek,.read     = chr_read,.write    = chr_write,.ioctl    = chr_ioctl,.open     = chr_open,.release  = chr_release};static void chr_setup_cdev(struct chr_dev* dev, int index){int err;int devno = MKDEV(chr_major, index);cdev_init(&dev->cdev, &chr_ops);dev->cdev.owner = THIS_MODULE;err = cdev_add(&dev->cdev, devno, 1);if(err){printk(KERN_NOTICE "Error happend!\n");}}int chr_init(void){int result;dev_t ndev;result = alloc_chrdev_region(&ndev, 0, 1, "chr_dev");      if(result < 0 )      {          return result;      } printk("chr_init(): major = %d, minor = %d\n", MAJOR(ndev), MINOR(ndev));    chr_major = MAJOR(ndev);char_devp = kmalloc(sizeof(struct chr_dev), GFP_KERNEL);if(!char_devp){result = -ENOMEM;goto final;}memset(char_devp, 0, sizeof(struct chr_dev));chr_setup_cdev(char_devp, 0);return 0;final:unregister_chrdev_region(ndev, 1);return 0;}void chr_exit(){cdev_del(&char_devp->cdev);kfree(char_devp);unregister_chrdev_region(MKDEV(chr_major, 0), 1);}module_init(chr_init);module_exit(chr_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("feixiaoxing!163.com");MODULE_DESCRIPTION("A simple device example!");

    不可否認,我們的代碼出現了更多的內容,但是基本架構還是一致的。要是說區別,無非就是我們在原來的基礎上添加了新的處理函數而已。說起來,我們對於裝置的主要操作也就是這麼幾種,大家如果對此的概念已經非常成熟了,那麼後面的學習就會輕鬆很多。當然和之前的驅動一樣,我們也需要make &  insmod char.ko & mknod /dev/chr_dev c 249 0。接下來,為了驗證上述的內容是否正確,編寫一段簡單的測試代碼是必不可少的。

#include <stdio.h>#include <fcntl.h>#include <unistd.h>#define MEM_CLEAR 0x01#define CHAR_DEV_NAME "/dev/chr_dev"int main(){        int ret;        int fd;        int index;        char buf[32];/* open device */        fd = open(CHAR_DEV_NAME, O_RDWR | O_NONBLOCK);        if(fd < 0)        {                printf("open failed!\n");                return -1;        }/* set buffer data, which will be stored into device */        for(index = 0; index < 32; index ++)        {                buf[index] = index;        }/* write data */        write(fd, buf, 32);        memset(buf, 0, 32);/* read data */        lseek(fd, 0, SEEK_SET);        read(fd, buf, 32);        for(index = 0; index < 32; index ++)        {                printf("data[%d] = %d\n", index, buf[index]);        }/* reset all data to zero, read it and check whether it is ok */ioctl(fd, MEM_CLEAR, NULL);lseek(fd, 0, SEEK_SET);        read(fd, buf, 32);        for(index = 0; index < 32; index ++)        {                printf("data[%d] = %d\n", index, buf[index]);        }        close(fd);        return 0;}

    細心的朋友可能發現了,我們在使用者側代碼中使用了很多的處理函數,基本上從open、release、read、write、lseek、ioctl全部包括了。測試代碼處理的流程也非常簡單,首先開啟裝置,接著寫資料,後面就是讀取資料,最後利用ioctl清除資料,程式返回。因為代碼中包含了注釋的內容,在此我們就不過多贅述了。大家慢慢看代碼,應該都會瞭解和明白的。

    希望以上的這段內容對大家有所協助。



聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.