linux下如何写一个秒字符设备

做linux内核驱动,最重要的一种设备驱动就是字符设备驱动,也是最基本的最需要彻底掌握的。

字符设备几个重要的结构体和接口函数:

字符设备结构体
struct cdev
初始化字符设备
cdev_init(struct cdev*,struct file_operations*);
注册或者说添加一个字符设备到内核
cdev_add(struct cdev*,int,int);
删除某个字符设备
cdev_del(struct cdev*);

我们可以自己定义一个字符设备结构体,在这个结构体中包含struct cdev。事实上很多其他的驱动设备,也是这样做的,比方i2c设备、usb设备等。

第一步、

    初始化cdev_init这个cdev,这样就可以将cdev和file_operations结构体绑定在一起,用户空间才能够通过read、write等函数来操作这个cdev设备。

第二步、

    注册这个cdev到内核中,使用cdev_add接口。这样一个简易的cdev字符算是存在于内核中了。

卸载这人cdev可以通过cdev_del接口函数来做,一般在__exit函数中调用。


这里来实现一个简单的秒字符设备。

1. second_dev.c

#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <linux/uaccess.h>

#define SECOND_MAJOR 255

static struct class *firstdrv_class;//auto create a device node need a class

static int second_major = SECOND_MAJOR;// MAJOR 255

//struct second device, contians struct cdev

struct second_dev {
   struct cdev cdev;
   atomic_t counter;//define atomic_t counter
   struct timer_list s_timer;//define the timer handle function, timeout etc.
};

struct second_dev *second_devp;//a pointer of struct device

//timer handle func

static void second_timer_handle(unsigned long arg)
{
   unsigned long current_jiffies = jiffies;
   mod_timer(&second_devp->s_timer,jiffies + HZ);//reload timer the timeout plus 1s
   atomic_inc(&second_devp->counter);//counter plus 1

   printk("current jiffies is %ld\n",current_jiffies);//
}

int second_open(struct inode *inode,struct file *filp)//open 
{
   init_timer(&second_devp->s_timer);// init the timer
   second_devp->s_timer.function = &second_timer_handle;//specific the timer handle function
   second_devp->s_timer.expires = jiffies + HZ;//specific the expires jiffies + HZ

   add_timer(&second_devp->s_timer);//add the timer to timer_list

   atomic_set(&second_devp->counter,0);//init the counter to 0

return 0;
}

//close file

int second_release(struct inode *inode,struct file *filp)
{
   del_timer(&second_devp->s_timer);//delete the timer
return 0;
}

static ssize_t second_read(struct file *filp,char __user *buf,size_t count,loff_t *ppos)
{

   int counter;
   counter = atomic_read(&second_devp->counter);//get the counter
   if(put_user(counter,(int *)buf))//put_user()  kernel -> userspace
      return -EFAULT;
   else
      return sizeof(unsigned int);
}

static ssize_t second_write(struct file* filp,const char __user * buf, size_t count, loff_t* ppos)
{
   char* value = (char*)kmalloc(count,GFP_KERNEL);
   unsigned long size = copy_from_user((void*)value,buf,count);

   printk("xujiwei---> copy form user, value = %s\n",value);

   kfree(value);
return size;
}

static const struct file_operations second_fops = {
      .owner = THIS_MODULE,
      .open  = second_open,
      .release = second_release,
      .read  = second_read,
      .write = second_write,
};

//init cdev

static void second_setup_cdev(struct second_dev *dev,int index)
{
   int err,devno = MKDEV(second_major,index);//merge the major and minor device index

   cdev_init(&dev->cdev,&second_fops);//init the cdev and connect the cdev and file_operations
   dev->cdev.owner = THIS_MODULE;
   err = cdev_add(&dev->cdev,devno,1);//add the cdev
   if(err)
       printk(KERN_NOTICE "Error %d adding LED%d",err,index);
}

int __init second_init(void)
{
   int ret;
   dev_t devno = MKDEV(second_major,0);

   if(second_major)
      ret = register_chrdev_region(devno,1,"second");
   else{
      ret = alloc_chrdev_region(&devno, 0, 1, "second");
      second_major = MAJOR(devno);
   }

   if(ret < 0)
       return ret;
    
   firstdrv_class = class_create(THIS_MODULE, "second_major");//create the firstdrv class --> /sys/class/second_major
   device_create(firstdrv_class,NULL,devno,NULL,"second");//auto create device node /dev/LED


   second_devp = kmalloc(sizeof(struct second_dev),GFP_KERNEL);

   if(!second_devp){
      ret = -ENOMEM;
      goto fail_malloc;
   }
   
   memset(second_devp,0,sizeof(struct second_dev));

   second_setup_cdev(second_devp,0);

return 0;

fail_malloc:
   unregister_chrdev_region(devno, 1);
return ret;
}

void __exit second_exit(void)
{
   cdev_del(&second_devp->cdev);//delete the cdev
   kfree(second_devp);
   unregister_chrdev_region(MKDEV(second_major,0),1);
   device_destroy(firstdrv_class,MKDEV(second_major,0));
   class_destroy(firstdrv_class);
}

MODULE_LICENSE("GPL");
module_init(second_init);
module_exit(second_exit);

2. Makefile

#
# Made from Makefile.org by xujiweigo@163.com
#

obj-m += second-device.o
second-device-y := second-dev.o

3. make.sh

#/bin/sh
ver=`uname -r`
CONFIG_USBIP_CORE=m \
CONFIG_USBIP_VHCI_HCD=m \
CONFIG_USBIP_HOST=m \
make -C /lib/modules/$ver/build M=$PWD "$@"

直接运行make.sh就这可编译出一个ko文件second-device.ko,然后insmod到内核中,这样一个秒字符设备就做好了。


编写一个测试demo,看看效果如何。

second-dev-test.c

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

int main()
{
   int fd;
   int counter = 0;
   int old_counter = 0;
   char buf[20] = "xujiwei";

   fd = open("/dev/second",O_RDWR);//open device

   if(fd != -1){

      printf("write size = %ld\n",write(fd,buf,sizeof("xujiwei")));
      while(1){
         read(fd,&counter,sizeof(unsigned int));
            if(counter != old_counter){
            printf("seconds after open /dev/second : %d\n",counter);
            old_counter = counter;
         }
      }
  }
  else{
     printf("Device open failure\n");
  }
} 

编译然后运行一下。


成功。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值