第一个嵌入式Linux的驱动程序

本文介绍了一个简单的字符设备驱动开发过程,包括驱动结构、关键函数实现、编译及测试流程。通过具体实例,展示了如何在Linux环境下进行驱动开发。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

   从这个总结正式开始了我的驱动之旅,心情不免有点激动,尽管现在对驱动的编写还很模糊,但还是有纪念意义的……
 
开发平台:
    虚拟机下RedHat9,内核2.4.20.8
    交叉编译编译器:天嵌公司提供的arm-linux-gcc 4.3.3
    内核树:/home/linux-2.6.30.4
目标平台:
    TQ2440,内核是ARM9
    OS内核:天嵌公司提供的linux-2.6.30.4
 
关于天嵌的软件可以到 http://bbs.embedsky.net/下载
 
废话少说,我的驱动源码是:
demo.h:
#ifndef _DEMO_H_
#define _DEMO_H_
 
#include <linux/ioctl.h>
 
#undef PDEBUG            
#ifdef DEMO_DEBUG
#ifdef __KERNEL__
#    define PDEBUG(fmt, args...) printk( KERN_DEBUG "DEMO: " fmt, ## args)
#else//usr space
#    define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)
#endif
#else
#  define PDEBUG(fmt, args...)
#endif
 
#undef PDEBUGG
#define PDEBUGG(fmt, args...)
 
//设备号
#define DEMO_MAJOR 224
#define DEMO_MINOR 0
#define COMMAND1 1
#define COMMAND2 2
 
//设备结构
struct DEMO_dev
{
     struct cdev cdev;  
};
 
//函数申明
ssize_t DEMO_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos);
ssize_t DEMO_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos);
loff_t  DEMO_llseek(struct file *filp, loff_t off, int whence);
int     DEMO_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);

#endif
 
demo.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/cdev.h>
#include <linux/version.h>
#include <linux/vmalloc.h>
#include <linux/ctype.h>
#include <linux/pagemap.h>
 
#include "demo.h"
 
MODULE_AUTHOR("fgj");
MODULE_LICENSE("Dual BSD/GPL");
 
struct DEMO_dev *DEMO_devices;
static unsigned char demo_inc=0;
static u8 demoBuffer[256];
 
int DEMO_open(struct inode *inode, struct file *filp)
{
    struct DEMO_dev *dev;
 
    if(demo_inc>0)return -ERESTARTSYS;
    demo_inc++;
 
    dev = container_of(inode->i_cdev, struct DEMO_dev, cdev);
    filp->private_data = dev;
 
    return 0;
}
 
int DEMO_release(struct inode *inode, struct file *filp)
{
    demo_inc--;
    return 0;
}
 
ssize_t DEMO_read(struct file *filp, char __user *buf, size_t count,loff_t *f_pos)
{
    int result;
    loff_t pos= *f_pos;
    if(pos>=256)
    {
         result=0;
         goto out;
    }
 if(count>(256-pos))
 {
     count=256-pos;
 }
 pos += count;
 
 if (copy_to_user(buf,demoBuffer+*f_pos,count))
 {
      count=-EFAULT;
      goto out;
 }
 
 *f_pos = pos;
 out:
 return count;
}
 
ssize_t DEMO_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)
{
    ssize_t retval = -ENOMEM;
    loff_t pos= *f_pos;
    if(pos>=256)
    {
         goto out;
    }
 if(count>(256-pos))
 {
     count=256-pos;
 }
 pos += count;
 
 if (copy_from_user(demoBuffer+*f_pos, buf, count))
 {
     retval = -EFAULT;
     goto out;
 }
 
 *f_pos = pos;
 return count;
  out:
 return retval;
}
 
int DEMO_ioctl(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg)
{
   if(cmd==COMMAND1)
   {
        printk("ioctl command1 successfully/n");
        return 0;
   }
   if(cmd==COMMAND2)
   {
        printk("ioctl command2 successfully/n");
        return 0;
   }
 
   printk("ioctl error/n");
          return -EFAULT;
}
 
loff_t DEMO_llseek(struct file *filp, loff_t off, int whence)
{
 loff_t pos;
 pos = filp->f_pos;
 switch (whence)
 {
 case 0:
     pos = off;
     break;
 case 1:
     pos += off;
     break;
 case 2:
 default:
     return -EINVAL;
 }
 
 if ((pos>256) || (pos<0))
 {
     return -EINVAL;
 }
 
 return filp->f_pos=pos;
}
 
struct file_operations DEMO_fops =
{
    .owner =    THIS_MODULE,
    .llseek =   DEMO_llseek,
    .read =     DEMO_read,
    .write =    DEMO_write,
    .ioctl =    DEMO_ioctl,
    .open =     DEMO_open,
    .release =  DEMO_release,
};
 

void DEMO_cleanup_module(void)
{
 dev_t devno = MKDEV(DEMO_MAJOR, DEMO_MINOR);
 
 if (DEMO_devices)
 {
     cdev_del(&DEMO_devices->cdev);
     kfree(DEMO_devices);
 }
 
 unregister_chrdev_region(devno,1);
}
 
int DEMO_init_module(void)
{
 int result;
 dev_t dev = 0;
 
 dev = MKDEV(DEMO_MAJOR, DEMO_MINOR);
 result = register_chrdev_region(dev, 1, "DEMO");
 if (result < 0)
 {
     printk(KERN_WARNING "DEMO: can't get major %d/n", DEMO_MAJOR);
     return result;
 }
 
 DEMO_devices = kmalloc(sizeof(struct DEMO_dev), GFP_KERNEL);
 if (!DEMO_devices)
 {
     result = -ENOMEM;
     goto fail;
 }
 memset(DEMO_devices, 0, sizeof(struct DEMO_dev));
 
 cdev_init(&DEMO_devices->cdev, &DEMO_fops);
 DEMO_devices->cdev.owner = THIS_MODULE;
 DEMO_devices->cdev.ops = &DEMO_fops;
 result = cdev_add (&DEMO_devices->cdev, dev, 1);
 if(result)
 {
     printk(KERN_NOTICE "Error %d adding DEMO/n", result);
     goto fail;
 }
 
 return 0;
 
fail:
 DEMO_cleanup_module();
 return result;
}
 
module_init(DEMO_init_module);
module_exit(DEMO_cleanup_module);
 
Makefile:
AR=ar
ARCH = arm
CC = arm-linux-gcc
#CFLAGS += $(DEBFLAGS) -Wall
#CFLAGS += -I$(LDDINC)
#LDFLAGS = -Xlinker -rpath-link /opt/arm-linux-gcc/4.3.3
ifneq($(KERNELRELEASE),)
#call from kernel build system
obj-m := demo.o
else
KERNELDIR ?= /home/linux-2.6.30.4
PWD := $(shell pwd)
modules:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) LDDINC=$(PWD)/../include modules
endif
 
好,现在我们要对内核树做点什么
    cd /home/linux-2.6.30.4/
    cp config_EmbedSky_W35_256MB .config
    make oldconfig
    make prepare(我试了试,这个不执行也行,不知道为啥)
    make(或make zImage或make bzImage)
好,现在转到驱动程序目录
    make
现在编译完毕,在编译过程中有可能出现很多问题,比如说,如果你的编译器不是4.3.3,可能出现:
cc1: error: invalid option `abi=aapcs-linux'
解决办法:
修改内核配置:make menuconfig
    Kernel Features-->Use the ARM EABIto compile the kernel(不选择)
 
测试程序
test.c
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<linux/rtc.h>
#include<linux/ioctl.h>
#include<stdio.h>
#include<stdlib.h>
 
#define COMMAND1 1
#define COMMAND2 2
 
main()
{
  int fd;
  int i;
 char data[256];
 
  int retval;
  fd=open("/dev/fgj",O_RDWR);
  if(fd==-1)
  {
     perror("error open/n");
     exit(-1);
  }
  printf("open /dev/smbus successfully/n");
  retval=ioctl(fd,COMMAND1,0);
  if(retval==-1)
  {
    perror("ioctl error/n");
    exit(-1);
  }
  printf("send command1 successfully/n");
 
  retval=write(fd,"fgj",3);
  if(retval==-1)
  {
    perror("write error/n");
    exit(-1);
  }
  retval=lseek(fd,0,0);
  if(retval==-1)
  {
    perror("lseek error/n");
    exit(-1);
  }
  retval=read(fd,data,3);
 
 if(retval==-1)
  {
    perror("read error/n");
    exit(-1);
  }
   printf("read successfully:%s/n",data);
  close(fd);
}
编译:arm-linux-gcc test.c -o test
 
将编译生成的demo.ko下载到开发板上,执行:
    insmod demo.ko(可以用lsmod命令查看是否加载上,可以通过cat /proc/devices查看主设备号)
    mknod /dev/fgj c 224 0(创建设备节点,224是主设备号)
 
将生成的测试文件下载到开发板上
    chmod 777 test
    ./test
 
如果打开设备成功,读写成功,OK!!!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值