1. 程式人生 > >轉載:一個簡單的linux驅動示例

轉載:一個簡單的linux驅動示例

        本文參考百度文庫“linux驅動開發入門”點選開啟連結和轉載博文:點選開啟連結

一、基本知識        

        Linux裝置驅動分為:字元裝置、塊裝置和網路裝置。原理圖如下:


二、示例

示例主要轉載自部落格園的部落格,見上。只是我採用的的Linux核心版本比那篇博文的新,有小許改動,貼上程式碼如下:

核心版本:

[email protected]:/$ uname -a
Linux ubuntu 3.16.0-30-generic #40~14.04.1-Ubuntu SMP Thu Jan 15 17:43:14 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

檔案:hello_mod.c

[cpp] view plain copy print?
  1. /* 
  2.  * ===================================================================================== 
  3.  * 
  4.  *       Filename:  hello.c 
  5.  * 
  6.  *    Description:  hello_mod 
  7.  * 
  8.  *        Version:  1.0 
  9.  *        Created:  01/28/2011 05:07:55 PM 
  10.  *       Revision:  none 
  11.  *       Compiler:  gcc
     
  12.  * 
  13.  *         Author:  Tishion (shion), [email protected] 
  14.  *        Company:  LIM 
  15.  * 
  16.  * ===================================================================================== 
  17.  */
  18. #include <linux/module.h>
  19. #include <linux/init.h>
  20. #include <linux/kernel.h>
  21. #include <linux/fs.h>
  22. #include <linux/uaccess.h>
  23. #include <linux/semaphore.h>
  24. #include <linux/cdev.h>
  25. #include <linux/device.h>
  26. #include <linux/ioctl.h>
  27. #include <linux/slab.h>
  28. #include <linux/errno.h>
  29. #include <linux/string.h>
  30. #include “hello_mod_ioctl.h”
  31. #define MAJOR_NUM 250
  32. #define MINOR_NUM 0
  33. #define IN_BUF_LEN 256
  34. #define OUT_BUF_LEN 512
  35. MODULE_AUTHOR(”Tishion”);  
  36. MODULE_DESCRIPTION(”Hello_mod driver by tishion”);  
  37. staticstructclass * hello_class;  
  38. staticstruct cdev hello_cdev;  
  39. static dev_t devnum = 0;  
  40. staticchar * modname = “hello_mod”;  
  41. staticchar * devicename = “hello”;  
  42. staticchar * classname = “hello_class”;  
  43. staticint open_count = 0;  
  44. staticstruct semaphore sem;  
  45. static DEFINE_SPINLOCK(spin);  
  46. staticchar * inbuffer = NULL;  
  47. staticchar * outbuffer = NULL;  
  48. static lang_t langtype;  
  49. staticint hello_mod_open(struct inode *, struct file *);  
  50. staticint hello_mod_release(struct inode *, struct file *);  
  51. static ssize_t hello_mod_read(struct file *, char *, size_t, loff_t *);  
  52. static ssize_t hello_mod_write(struct file *, constchar *, size_t, loff_t *);  
  53. staticlong hello_mod_ioctl(struct file *, unsigned int, unsigned long);  
  54. struct file_operations hello_mod_fops =   
  55. {  
  56.     .owner = THIS_MODULE,  
  57.     .open = hello_mod_open,  
  58.     .read = hello_mod_read,  
  59.     .write = hello_mod_write,  
  60.     .unlocked_ioctl = hello_mod_ioctl,  
  61.     .release = hello_mod_release,  
  62. };  
  63. staticint hello_mod_open(struct inode *inode, struct file *pfile)  
  64. {  
  65.     printk(”+hello_mod_open()!/n”);  
  66.     spin_lock(&spin);  
  67.     if(open_count)  
  68.     {  
  69.         spin_unlock(&spin);  
  70.         return -EBUSY;  
  71.     }  
  72.     open_count++;  
  73.     spin_unlock(&spin);  
  74.     printk(”-hello_mod_open()!/n”);  
  75.     return 0;  
  76. }  
  77. staticint hello_mod_release(struct inode *inode, struct file *pfile)  
  78. {  
  79.     printk(”+hello_mod_release()!/n”);  
  80.     open_count–;  
  81.     printk(”-hello_mod_release()!/n”);  
  82.     return 0;  
  83. }  
  84. static ssize_t hello_mod_read(struct file *pfile, char *user_buf, size_t len, loff_t *off)  
  85. {  
  86.     printk(”+hello_mod_read()!/n”);  
  87.     if(down_interruptible(&sem))  
  88.     {  
  89.         return -ERESTARTSYS;   
  90.     }  
  91.     memset(outbuffer, 0, OUT_BUF_LEN);  
  92.     printk(”    +switch()/n”);  
  93.     switch(langtype)  
  94.     {  
  95.         case english:  
  96.             printk(”        >in case: english/n”);  
  97.             sprintf(outbuffer, ”Hello! %s.”, inbuffer);  
  98.             break;  
  99.         case chinese:  
  100.             printk(”        >in case: chinese/n”);  
  101.             sprintf(outbuffer, ”你好! %s.”, inbuffer);  
  102.             break;  
  103.         case pinyin:  
  104.             printk(”        >in case: pinyin/n”);  
  105.             sprintf(outbuffer, ”ni hao! %s.”, inbuffer);  
  106.             break;  
  107.         default:  
  108.             printk(”        >in case: default/n”);  
  109.             break;  
  110.     }  
  111.     printk(”    -switch()/n”);  
  112.     if(copy_to_user(user_buf, outbuffer, len))  
  113.     {  
  114.         up(&sem);  
  115.         return -EFAULT;  
  116.     }  
  117.     up(&sem);  
  118.     printk(”-hello_mod_read()!/n”);  
  119.     return 0;  
  120. }  
  121. static ssize_t hello_mod_write(struct file *pfile, constchar *user_buf, size_t len, loff_t *off)  
  122. {  
  123.     printk(”+hello_mod_write()!/n”);  
  124.     if(down_interruptible(&sem))  
  125.     {  
  126.         return -ERESTARTSYS;  
  127.     }  
  128.     if(len > IN_BUF_LEN)  
  129.     {  
  130.         printk(”Out of input buffer/n”);  
  131.         return -ERESTARTSYS;  
  132.     }  
  133.     if(copy_from_user(inbuffer, user_buf, len))  
  134.     {  
  135.         up(&sem);  
  136.         return -EFAULT;  
  137.     }  
  138.     up(&sem);      
  139.     printk(”-hello_mod_write()!/n”);  
  140.     return 0;  
  141. }  
  142. staticlong hello_mod_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg)  
  143. {  
  144.     int err = 0;  
  145.     printk(”+hello_mod_ioctl()!/n”);  
  146.     printk(”    +switch()/n”);  
  147.     switch(cmd)  
  148.     {  
  149.         case HELLO_IOCTL_RESETLANG:  
  150.             printk(”        >in case: HELLO_IOCTL_RESETLANG/n”);  
  151.             langtype = english;  
  152.             break;  
  153.         case HELLO_IOCTL_GETLANG:  
  154.             printk(”        >in case: HELLO_IOCTL_GETLANG/n”);  
  155.             err = copy_to_user((int *)arg, &langtype, sizeof(int));  
  156.             break;  
  157.         case HELLO_IOCTL_SETLANG:  
  158.             printk(”        >in case: HELLO_IOCTL_SETLANG/n”);  
  159.             err = copy_from_user(&langtype,(int *)arg, sizeof(int));  
  160.             break;  
  161.         default:  
  162.             printk(”        >in case: default/n”);  
  163.             err = ENOTSUPP;  
  164.             break;  
  165.     }  
  166.     printk(”    -switch()/n”);  
  167.     printk(”-hello_mod_ioctl()!/n”);  
  168.     return err;  
  169. }  
  170. staticint __init hello_mod_init(void)  
  171. {  
  172.     int result;  
  173.     printk(”+hello_mod_init()!/n”);  
  174.     devnum = MKDEV(MAJOR_NUM, MINOR_NUM);  
  175.     result = register_chrdev_region(devnum, 1, modname);  
  176.     if(result < 0)  
  177.     {  
  178.         printk(”hello_mod : can’t get major number!/n”);  
  179.         return result;  
  180.     }      
  181.     cdev_init(&hello_cdev, &hello_mod_fops);  
  182.     hello_cdev.owner = THIS_MODULE;  
  183.     hello_cdev.ops = &hello_mod_fops;  
  184.     result = cdev_add(&hello_cdev, devnum, 1);  
  185.     if(result)  
  186.         printk(”Failed at cdev_add()”);  
  187.     hello_class = class_create(THIS_MODULE, classname);  
  188.     if(IS_ERR(hello_class))  
  189.     {  
  190.         printk(”Failed at class_create().Please exec [mknod] before operate the device/n”);  
  191.     }  
  192.     else
  193.     {  
  194.         device_create(hello_class, NULL, devnum,NULL, devicename);  
  195.     }  
  196.     open_count = 0;  
  197.     langtype = english;  
  198.     inbuffer = (char *)kmalloc(IN_BUF_LEN, GFP_KERNEL);  
  199.     outbuffer = (char *)kmalloc(OUT_BUF_LEN, GFP_KERNEL);  
  200.     sema_init(&sem, 1);  
  201.     printk(”-hello_mod_init()!/n”);  
  202.     return 0;  
  203. }  
  204. staticvoid __exit hello_mod_exit(void)  
  205. {  
  206.     printk(”+hello_mod_exit!/n”);  
  207.     kfree(inbuffer);  
  208.     kfree(outbuffer);  
  209.     cdev_del(&hello_cdev);  
  210.     device_destroy(hello_class, devnum);  
  211.     class_destroy(hello_class);  
  212.     unregister_chrdev_region(devnum, 1);  
  213.     printk(”-hello_mod_exit!/n”);  
  214.     return ;  
  215. }  
  216. module_init(hello_mod_init);  
  217. module_exit(hello_mod_exit);  
  218. MODULE_LICENSE(”GPL”);  
/*
 * =====================================================================================
 *
 *       Filename:  hello.c
 *
 *    Description:  hello_mod
 *
 *        Version:  1.0
 *        Created:  01/28/2011 05:07:55 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Tishion (shion), [email protected]
 *        Company:  LIM
 *
 * =====================================================================================
 */





include <linux/module.h>

include <linux/init.h>

include <linux/kernel.h>

include <linux/fs.h>

include <linux/uaccess.h>

include <linux/semaphore.h>

include <linux/cdev.h>

include <linux/device.h>

include <linux/ioctl.h>

include <linux/slab.h>

include <linux/errno.h>

include <linux/string.h>

include "hello_mod_ioctl.h"

define MAJOR_NUM 250

define MINOR_NUM 0

define IN_BUF_LEN 256

define OUT_BUF_LEN 512

MODULE_AUTHOR("Tishion");
MODULE_DESCRIPTION("Hello_mod driver by tishion");

static struct class * hello_class;
static struct cdev hello_cdev;
static dev_t devnum = 0;
static char * modname = "hello_mod";
static char * devicename = "hello";
static char * classname = "hello_class";

static int open_count = 0;
static struct semaphore sem;
static DEFINE_SPINLOCK(spin);
static char * inbuffer = NULL;
static char * outbuffer = NULL;
static lang_t langtype;

static int hello_mod_open(struct inode , struct file );
static int hello_mod_release(struct inode , struct file );
static ssize_t hello_mod_read(struct file , char , size_t, loff_t *);
static ssize_t hello_mod_write(struct file , const char , size_t, loff_t *);
static long hello_mod_ioctl(struct file *, unsigned int, unsigned long);

struct file_operations hello_mod_fops =
{
.owner = THIS_MODULE,
.open = hello_mod_open,
.read = hello_mod_read,
.write = hello_mod_write,
.unlocked_ioctl = hello_mod_ioctl,
.release = hello_mod_release,
};

static int hello_mod_open(struct inode *inode, struct file *pfile)
{
printk("+hello_mod_open()!/n");
spin_lock(&spin);
if(open_count)
{
spin_unlock(&spin);
return -EBUSY;
}
open_count++;
spin_unlock(&spin);
printk("-hello_mod_open()!/n");
return 0;
}
static int hello_mod_release(struct inode *inode, struct file *pfile)
{
printk("+hello_mod_release()!/n");
open_count--;
printk("-hello_mod_release()!/n");
return 0;
}
static ssize_t hello_mod_read(struct file *pfile, char *user_buf, size_t len, loff_t *off)
{
printk("+hello_mod_read()!/n");

if(down_interruptible(&amp;sem))
{
    return -ERESTARTSYS; 
}
memset(outbuffer, 0, OUT_BUF_LEN);
printk("    +switch()/n");
switch(langtype)
{
    case english:
        printk("        &gt;in case: english/n");
        sprintf(outbuffer, "Hello! %s.", inbuffer);
        break;
    case chinese:
        printk("        &gt;in case: chinese/n");
        sprintf(outbuffer, "你好! %s.", inbuffer);
        break;
    case pinyin:
        printk("        &gt;in case: pinyin/n");
        sprintf(outbuffer, "ni hao! %s.", inbuffer);
        break;
    default:
        printk("        &gt;in case: default/n");
        break;
}
printk("    -switch()/n");
if(copy_to_user(user_buf, outbuffer, len))
{
    up(&amp;sem);
    return -EFAULT;
}
up(&amp;sem);
printk("-hello_mod_read()!/n");
return 0;

}
static ssize_t hello_mod_write(struct file *pfile, const char *user_buf, size_t len, loff_t *off)
{
printk(“+hello_mod_write()!/n”);
if(down_interruptible(&sem))
{
return -ERESTARTSYS;
}
if(len > IN_BUF_LEN)
{
printk(“Out of input buffer/n”);
return -ERESTARTSYS;
}
if(copy_from_user(inbuffer, user_buf, len))
{
up(&sem);
return -EFAULT;
}
up(&sem);
printk(“-hello_mod_write()!/n”);
return 0;
}
static long hello_mod_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg)
{
int err = 0;
printk(“+hello_mod_ioctl()!/n”);
printk(” +switch()/n”);
switch(cmd)
{
case HELLO_IOCTL_RESETLANG:
printk(” >in case: HELLO_IOCTL_RESETLANG/n”);
langtype = english;
break;
case HELLO_IOCTL_GETLANG:
printk(” >in case: HELLO_IOCTL_GETLANG/n”);
err = copy_to_user((int *)arg, &langtype, sizeof(int));
break;
case HELLO_IOCTL_SETLANG:
printk(” >in case: HELLO_IOCTL_SETLANG/n”);
err = copy_from_user(&langtype,(int *)arg, sizeof(int));
break;
default:
printk(” >in case: default/n”);
err = ENOTSUPP;
break;
}
printk(” -switch()/n”);
printk(“-hello_mod_ioctl()!/n”);
return err;
}
static int __init hello_mod_init(void)
{
int result;
printk(“+hello_mod_init()!/n”);
devnum = MKDEV(MAJOR_NUM, MINOR_NUM);
result = register_chrdev_region(devnum, 1, modname);

if(result &lt; 0)
{
    printk("hello_mod : can't get major number!/n");
    return result;
}    

cdev_init(&amp;hello_cdev, &amp;hello_mod_fops);
hello_cdev.owner = THIS_MODULE;
hello_cdev.ops = &amp;hello_mod_fops;
result = cdev_add(&amp;hello_cdev, devnum, 1);
if(result)
    printk("Failed at cdev_add()");
hello_class = class_create(THIS_MODULE, classname);
if(IS_ERR(hello_class))
{
    printk("Failed at class_create().Please exec [mknod] before operate the device/n");
}
else
{
    device_create(hello_class, NULL, devnum,NULL, devicename);
}

open_count = 0;
langtype = english;
inbuffer = (char *)kmalloc(IN_BUF_LEN, GFP_KERNEL);
outbuffer = (char *)kmalloc(OUT_BUF_LEN, GFP_KERNEL);
sema_init(&amp;sem, 1);
printk("-hello_mod_init()!/n");
return 0;

}

static void __exit hello_mod_exit(void)
{
printk(“+hello_mod_exit!/n”);
kfree(inbuffer);
kfree(outbuffer);
cdev_del(&hello_cdev);
device_destroy(hello_class, devnum);
class_destroy(hello_class);
unregister_chrdev_region(devnum, 1);
printk(“-hello_mod_exit!/n”);
return ;
}

module_init(hello_mod_init);
module_exit(hello_mod_exit);
MODULE_LICENSE(“GPL”);

標頭檔案:hello_mod_ioctl.h

[cpp] view plain copy print?
  1. /* 
  2.  * ===================================================================================== 
  3.  * 
  4.  *       Filename:  hello_mod_ioctl.h 
  5.  * 
  6.  *    Description:  define the cmd supported by hello_mod 
  7.  * 
  8.  *        Version:  1.0 
  9.  *        Created:  06/19/2011 10:24:20 PM 
  10.  *       Revision:  none 
  11.  *       Compiler:  gcc 
  12.  * 
  13.  *         Author:  Tishion (shion), [email protected] 
  14.  *        Company:  LIM 
  15.  * 
  16.  * ===================================================================================== 
  17.  */
  18. #ifndef __HELLO_MOD_IOCTL_H__
  19. #define __HELLO_MOD_IOCTL_H__
  20. #define HELLO_MAGIC    12
  21. #define HELLO_IOCTL_RESETLANG    _IO(HELLO_MAGIC,0)        //set langtype = english
  22. #define HELLO_IOCTL_GETLANG        _IOR(HELLO_MAGIC,1,int)    //get langtype
  23. #define HELLO_IOCTL_SETLANG        _IOW(HELLO_MAGIC,2,int)    //set langtype
  24. typedefenum _lang_t  
  25. {  
  26.     english, chinese, pinyin  
  27. }lang_t;  
  28. #endif
/*
 * =====================================================================================
 *
 *       Filename:  hello_mod_ioctl.h
 *
 *    Description:  define the cmd supported by hello_mod
 *
 *        Version:  1.0
 *        Created:  06/19/2011 10:24:20 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Tishion (shion), [email protected]
 *        Company:  LIM
 *
 * =====================================================================================
 */





#ifndef __HELLO_MOD_IOCTL_H__ #define __HELLO_MOD_IOCTL_H__ #define HELLO_MAGIC 12 #define HELLO_IOCTL_RESETLANG _IO(HELLO_MAGIC,0) //set langtype = english #define HELLO_IOCTL_GETLANG _IOR(HELLO_MAGIC,1,int) //get langtype #define HELLO_IOCTL_SETLANG _IOW(HELLO_MAGIC,2,int) //set langtype typedef enum _lang_t { english, chinese, pinyin }lang_t; #endif
Makefile檔案:(注意:Makefile的’M’要大寫) [cpp] view plain copy print?
  1. #**********************************************
  2. # Makefile linux 2.6 Module 
  3. # This makefile is written for Ubuntu 10.10
  4. # It may not perfomance without erros on the
  5. # other version or distributions.
  6. #**********************************************
  7. #    BY:tishion
  8. #    Mail:[email protected]
  9. #    2011/06/19
  10. #**********************************************
  11. obj-m += hello_mod.o  
  12. CURRENT_PATH := (shell&nbsp;pwd)&nbsp;&nbsp;</span></li><li class="alt"><span>LINUX_KERNEL&nbsp;:=&nbsp;(shell uname -r)  
  13. LINUX_KERNEL_PATH := /usr/src/linux-headers-(LINUX_KERNEL)&nbsp;&nbsp;</span></li><li class="alt"><span>all:&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;make&nbsp;-C&nbsp;(LINUX_KERNEL_PATH) M=(CURRENT_PATH)&nbsp;modules&nbsp;&nbsp;</span></li><li class="alt"><span>clean:&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;make&nbsp;-C&nbsp;(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean  
  14. install:  
  15.     insmod hello_mod.ko  
  16. unistall:  
  17.     rmmod hello_mod  
#**********************************************




# Makefile linux 2.6 Module # This makefile is written for Ubuntu 10.10 # It may not perfomance without erros on the # other version or distributions. #********************************************** # BY:tishion # Mail:[email protected] # 2011/06/19 #********************************************** obj-m += hello_mod.o CURRENT_PATH := (shellpwd)LINUXKERNEL:=(shell uname -r) LINUX_KERNEL_PATH := /usr/src/linux-headers-(LINUXKERNEL)all:makeC(LINUX_KERNEL_PATH) M=(CURRENTPATH)modulesclean:makeC(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean install: insmod hello_mod.ko unistall: rmmod hello_mod
以上三個檔案即為驅動程式碼

另外,再給一個應用層測試程式碼,並共用驅動的標頭檔案

hello_mod_test.c檔案:

[cpp] view plain copy print?
  1. /* 
  2.  * ===================================================================================== 
  3.  * 
  4.  *       Filename:  hell_mod_test.c 
  5.  * 
  6.  *    Description:  hell_mod test app 
  7.  * 
  8.  *        Version:  1.0 
  9.  *        Created:  06/20/2011 01:44:11 AM 
  10.  *       Revision:  none 
  11.  *       Compiler:  gcc 
  12.  * 
  13.  *         Author:  Tishion (shion), [email protected] 
  14.  *        Company:  LIM 
  15.  * 
  16.  * ===================================================================================== 
  17.  */
  18. #include <stdio.h>
  19. #include <sys/types.h>
  20. #include <sys/stat.h>
  21. #include <fcntl.h>
  22. #include <unistd.h>
  23. #include <linux/ioctl.h>
  24. #include <string.h>
  25. #include <errno.h>
  26. #include ”../hello_mod_ioctl.h”
  27. int main()  
  28. {  
  29.     char outbuf[512];  
  30.     char * myname = “tishion”;  
  31.     lang_t langtype = english;  
  32.     int fd = open(“/dev/hello”, O_RDWR, S_IRUSR|S_IWUSR);  
  33.     if(fd != -1)  
  34.     {  
  35.         write(fd, myname, strlen(myname)+1);  
  36.         langtype = chinese;  
  37.         ioctl(fd, HELLO_IOCTL_SETLANG, &langtype);  
  38.         read(fd, outbuf, 512);  
  39.         printf(”langtype=chinese:%s/n”, outbuf);  
  40.         memset(outbuf, 0, 512);  
  41.         langtype = pinyin;  
  42.         ioctl(fd, HELLO_IOCTL_SETLANG, &langtype);  
  43.         read(fd, outbuf, 512);      
  44.         printf(”langtype=pinyin:%s/n”, outbuf);  
  45.         memset(outbuf, 0, 512);  
  46.         ioctl(fd, HELLO_IOCTL_RESETLANG, &langtype);      
  47.         read(fd, outbuf, 512);  
  48.         printf(”langtype=english:%s/n”, outbuf);  
  49.     }  
  50.     else
  51.     {  
  52.         perror(”Failed at open():”);  
  53.     }  
  54.     return 0;  
  55. }  
/*
 * =====================================================================================
 *
 *       Filename:  hell_mod_test.c
 *
 *    Description:  hell_mod test app
 *
 *        Version:  1.0
 *        Created:  06/20/2011 01:44:11 AM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Tishion (shion), [email protected]
 *        Company:  LIM
 *
 * =====================================================================================
 */





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

include “../hello_mod_ioctl.h”

int main()
{
char outbuf[512];