我的第一个模拟字符驱动

 Hi,Linux driver fans们好,我现在从事touch/display ic tuning工作,脱离linux 嵌入式开发好多年了,最近想捡起来温习一下。按照我自己的拙见,linux纯驱动开发一般分为几大类:

1. 字符类设备(char device)驱动开发,常见的串口,并口,canbus,以及键盘/鼠标/touchscreen等input之类的device,常见字符设备节点/dev/ttyS, /dev/tty, /dev/audio,/dev/lp0 etc.

2.块设备驱动(block device)开发,常见的硬盘驱动(IDE/SATA接口)      常见块设备设备节点,/dev/cdrom, /dev/fd0, /dev/hda,/dev/sda,

3.network device, 如常见的pci接口的intel e100/e1000, Realtek 8139 series,  访问节点eth0, wlan0

x86平台上,可能以字符驱动和网络驱动调试比较多,非x86基本上都会碰到,如果想提高综合能力,非x86我个人认为更合适(如arm/mips, powerpc)

一般驱动开发可以粗鲁地分为几个组成部分。

1.driver code

2.Makefile(如果是要上传到linux kernel,可能还需要提供Kconfig和内核版本的Makefile)

3.demo code

下面贴上一个简单的字符模拟驱动编写实例,(临时编写,代码风格不规范,不作为参考内容,重点请参考一个驱动架构和访问方式,入门式参考)

我的运行环境:

Centos [root@localhost chardemoRef]# uname -r
4.18.0-240.1.1.el8_3.x86_64

Vbox虚拟机

驱动部分:(driver会在/dev目录下自动创建/dev/demochar0 /dev/demochar1节点)

#include <linux/init.h> 
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>      //head file for file_operations
#include <linux/uaccess.h> //for memory operations copy_to/from_user
#include <linux/device.h>  //for class/device create
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/notifier.h>
#include <linux/platform_device.h>
#include <linux/version.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kevin.Jiang");

#define DEV_NAME "chardev"
#define DEVICE_COUNT  2 
#define DEV_SIZE    0x400  //define char device size, 400 bytes

static ssize_t demo_chardevRead(struct file *,char *,size_t,loff_t*);
static ssize_t demo_chardevWrite(struct file *,const char *,size_t,loff_t*);

static int char_major = 0;
static char  chardevData[1024] = "Wellcome!pls input new data\n"; //char device global variable.
static struct class *char_demo_class;   //for auto creating char device interface

//initialize char device’s file_operations struct
struct file_operations chardev_fops =
{

	.read = demo_chardevRead,
	.write = demo_chardevWrite 

};


static int __init chardev_init(void)
{

	int major;
	int i;
    	int retval;
	struct device *char_demo_device;
    
	major = register_chrdev(char_major,DEV_NAME,&chardev_fops);

        printk( "=====================chardev_init====================\n");

    	if(major < 0){
            retval = major;
	    printk("chardev Reg Fail!\n");
            goto chrdev_err;
    	}
	else
	{
		printk("chardev Reg Success!\n");
        	char_major = major;
        	printk("Major = %d\n",char_major);
    	}
 
    /*create device class*/
    char_demo_class = class_create(THIS_MODULE,"demo_char_class");

    if(IS_ERR(char_demo_class)){
           retval =  PTR_ERR(char_demo_class);
           goto class_err;
    }
 
    /*create device file,notify user the interface created in /dev/ with named demoX*/
    for(i=0; i<DEVICE_COUNT; i++){ /*create 255 nodes at most between range 0-255 for the slave device */
        char_demo_device = device_create(char_demo_class,NULL, MKDEV(major, i), NULL,"demochar%d",i);
        if(IS_ERR(char_demo_device)){
              retval = PTR_ERR(char_demo_device);
              goto device_err;
        }
    }    
	return 0;
device_err: 
    while(i--) //recycle operations for device; 
         class_destroy(char_demo_class);  
class_err: 
    unregister_chrdev(major, "demo_chardev");
chrdev_err: 
    return retval; 

    	return retval;
}

static void __exit chardev_exit(void)
{
	int i = 0;

	printk("[msg]:in module exit function\n");

      /*unregister the char major device*/
    	unregister_chrdev(char_major,DEV_NAME);
  
    	/*delete the device nodes*/
      for(i=0; i<DEVICE_COUNT; i++)
          device_destroy(char_demo_class,MKDEV(char_major, i));
      
      class_destroy(char_demo_class);
    
      printk("chardev is dead now!\n");
}


static ssize_t demo_chardevRead(struct file *filp,char __user *buf,size_t len,loff_t *off)
{

//	static char  chardevData[1024] = "Wellcome!pls input new data\n"; //"chardev" device’s global variable.
    	unsigned long offset =  *off;
    	unsigned int count = len;
    	unsigned int Maxlen = 0;
    	int ret = 0;

	/*get the valid length for input content*/
        printk("------------------Start Read------------------\n");
	Maxlen = strlen(chardevData);

	if (offset > DEV_SIZE)
    	{
		printk("offset > DEV_SIZE\n");
        	return count ?  - ENXIO: 0;
    	}
    	else if(offset == DEV_SIZE)
    	{
		printk("offset = DEV_SIZE\n");
        	return 0;   //prevent EOF error when cat /dev/chardev 
    	}
    	if (count > DEV_SIZE - offset)
    	{
		printk("count > DEV_SIZE -offset\n");
        	count = DEV_SIZE - offset;
    	}

   //  	printk("read char_len=%ld\n",strlen(chardevData));
   // 	printk("read chardevData=%s\n",chardevData);
    	
   	if (!(copy_to_user(buf,(char*)chardevData, strlen(chardevData)+1)))
    	{
		*off += count;
        	printk(KERN_INFO "read %d bytes(s) from %ld addr\n", count, offset);
        	//ret = count;

		if(chardevData[Maxlen-1]=='\n')
		{
        	 	ret = count;
        	printk("------------------Here xyz------------------\n");
		}
	    	else	
		{
			//ret = snprintf(buf,1024,"%As\n",buf);
			ret = snprintf(buf,1024,"%s\n",chardevData);
        	printk("------------------Here abc------------------\n");
		}

        	printk("------------------Here A------------------\n");
    	}
    	else
	{
        	return -EFAULT;
	}
    
	printk("chardev is read: buf=%s",buf);
    	printk("chardev is read: ref=%d\n",ret);


	return ret;
}

static ssize_t demo_chardevWrite(struct file *filp,const char __user *buf,size_t len,loff_t *off)
{
    	unsigned long offset =  *off;
    	unsigned int count = len; 
//	static char  chardevData[1024] = "Wellcome!pls input new data\n"; //"chardev" device’s global variable.
	
    	int ret = 0;
	
        offset = 0;
    
        printk(KERN_ALERT "--------Start Write-------------\n");
    	//printk("chardev is write now!\n");
    	printk("chardev is write: buf=%s\n",buf);
	
	    memset(chardevData,0,strlen(chardevData));
    	
    	if (offset > DEV_SIZE)
    	{
		printk("offset > DEV_SIZE\n");
        	return count ?  - ENXIO: 0;
    	}
    	else if(offset == DEV_SIZE)
    	{
		printk("offset = DEV_SIZE\n");
        	return 0;   // present for EOF error when echo /dev/chardev
    	}
    	if (count > DEV_SIZE - offset)
    	{
        	count = DEV_SIZE - offset;
    	}
    	printk("count= %d\n",count);
    	printk("need len:%ld, offset:%ld\n",len,offset);
 
    	if (copy_from_user(chardevData,buf,count))
    	{
    		printk("copy error! chardevData=%s\n",chardevData);
 printk("------------------Here B------------------\n");
	    	return -EFAULT;
    	}
	else
	{
		*off += count;
 printk("------------------Here C------------------\n");
        	printk("read %d bytes(s) from %ld addr\n", count, offset);
        	ret = count;
	}
		printk("chardevData=%s\n",chardevData);

    	return ret;
}

module_init(chardev_init);
module_exit(chardev_exit);

Makefile部分:

obj-m:=demochar.o
KERNELDIR:= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
	make -C $(KERNELDIR) M=$(PWD) modules
default:
	make -C $(KERNELDIR) M=$(PWD) modules
clean:
	rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions modules.*  Module.* *.rc

demo部分:(自己写代码或者直接用管道方式写入)

运行编译方式:gcc demo.c -o demo

执行方式:./demo 

从键盘输入随机字符串,并printf出来。(也可以在dmesg里面看driver里面printk出来的信息)

 

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

//#define DEV_NAME "/dev/chardev"  
#define DEV_NAME "/dev/demochar0"  
 
int main(void)
{
    int fd;
   // char num[1024]={1,2};;
    char num[1024]="This is my input data----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n";
//    int no1 = 0;
    /*打开"/dev/globalvar"*/
    //fd = open(DEV_NAME,O_RDWR | O_NDELAY);
    fd = open(DEV_NAME, O_RDWR, S_IRUSR | S_IWUSR);
    if (fd != -1 )
    {
        /*初次读globalvar*/
#if 0 //not must
        read(fd, &num, strlen(num));
        printf("The input data to /dev/demo0 is %s\n", num);
#endif
        /*写globalvar*/
        printf("Please input the num written to /dev/demochar0\n");
        scanf("%s", &num);
        write(fd, &num, strlen(num));

#if 0
        /*再次读/dev/demo0*/
        read(fd, &num, strlen(num));
        
	printf("The input data is %s\n", num);
#endif
        /*关闭"/dev/demo0"*/
        close(fd);
    }
    else
    {
        /* if not sudo, maybe come here */
        printf("Device open failure\n");
    }

    return 0;
}

Pipe方式:

写:echo 1111111111222222222222 >/dev/demochar0

读:cat /dev/demochar0

 

本文完毕

我的email address:

xianqiaoren@163.com

Kevin.Jiang

欢迎大家来email一起探讨学习交流。

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值