从内核中最简单的驱动程序入手,描述Linux驱动开发,主要文章目录如下(持续更新中): 01-第一个内核模块程序 02-注册字符设备驱动 03-open&close函数的应用 04-read&write函数的应用 05-ioctl的应用 06-ioctlLED灯硬件分析 07-ioctl控制LED软件实现(寄存器操作) 08-ioctl控制LED软件实现(库函数操作) 09-注册字符设备的另一种方法(常用) 10-一个cdev实现对多个设备的支持 11-四个cdev控制四个LED设备 12-虚拟串口驱动 13-I2C驱动 14-SPI协议及驱动讲解 15-SPILinux驱动代码实现
文章目录1spi_drv.c2user_lib.c3test.c4user.h5pdebug.h6Makefile
1spi_drv.c
主要包含了spi驱动的注册、注销、probe、ioctl和读写函数
#include<linux/module.h>#include<linux/spi/spi.h>#include<linux/spi/spidev.h>#include<linux/mod_devicetable.h>#include<linux/slab.h>#include<linux/cdev.h>#include<linux/uaccess.h>#include<linux/stddef.h>#include<linux/delay.h>#include<asm-generic/gpio.h>#include<asm/gpio.h>#include"pdebug.h"#defineTHTF_SPI_CHRDEV_NAME"my_spi_chrdev"#defineTHTF_SPI_CLASS_NAME"my_spi_class"#defineTHTF_SPI_DEVICE_NAME"my_spi_device"structmy_spi_dev{dev_tdev_no;structspi_device*myspi_dev;structcdevcdev;structclass*cls;structdevice*dev;};staticintmy_spi_open(structinode*inode,structfile*filp){structmy_spi_dev*my_spi_dev_temp=NULL;PDEBUG("%s--%s--%d.\n",__FILE__,__FUNCTION__,__LINE__);my_spi_dev_temp=container_of(inode->i_cdev,structmy_spi_dev,cdev);filp->private_data=my_spi_dev_temp;return0;}staticintmy_spi_release(structinode*inode,structfile*filp){PDEBUG("%s--%s--%d.\n",__FILE__,__FUNCTION__,__LINE__);filp->private_data=NULL;return0;}staticssize_tmy_spi_read(structfile*filp,char__user*userbuf,size_tsize,loff_t*offset){structmy_spi_dev*my_spi_dev_temp=filp->private_data;intret;unsignedcharbuf[4]={0};PDEBUG("%s--%s--%d.\n",__FILE__,__FUNCTION__,__LINE__);ret=spi_read(my_spi_dev_temp->myspi_dev,buf,size);if(ret<0){printk("spi_readfailed.\n");gotoerr0;}PDEBUG("0x%02x0x%02x0x%02x0x%02x..\n",buf[0],buf[1],buf[2],buf[3]);ret=copy_to_user(userbuf,buf,size);if(ret){printk("copy_to_userfailed.\n");gotoerr0;}return0;err0:returnret;}staticssize_tmy_spi_write(structfile*filp,constchar__user*userbuf,size_tsize,loff_t*offset){structmy_spi_dev*my_spi_dev_temp=filp->private_data;intret;unsignedcharbuf[3];PDEBUG("%s--%s--%d.\n",__FILE__,__FUNCTION__,__LINE__);ret=copy_from_user(buf,userbuf,size);if(ret<0){printk("copy_from_userfailed.\n");gotoerr0;}ret=spi_write(my_spi_dev_temp->myspi_dev,buf,3);if(ret<0){printk("spi_writefailed.\n");gotoerr0;}return0;err0:returnret;}staticlongmy_spi_ioctl(structfile*filp,unsignedintcmd,unsignedlongargs){intret;u16mode;u32max_speed_hz;u8bits_per_word;structmy_spi_dev*my_spi_dev_temp=filp->private_data;PDEBUG("%s--%s--%d.\n",__FILE__,__FUNCTION__,__LINE__);if(_IOC_TYPE(cmd)!=SPI_IOC_MAGIC){printk("SPI_IOC_MAGICerror.\n");ret=-ENOTTY;/*notatypewriter*/gotoerr0;}switch(cmd){caseSPI_IOC_RD_MODE:/*获取SPI主机控制器的模式*/ret=__put_user(my_spi_dev_temp->myspi_dev->mode,(__u8__user*)args);/*复制的内存是简单类型,如char,int,long等*/if(ret<0){printk("SPI_IOC_RD_MODE__put_userfailed.\n");gotoerr0;}break;caseSPI_IOC_RD_BITS_PER_WORD:/*获取SPI的字长*/ret=__put_user(my_spi_dev_temp->myspi_dev->bits_per_word,(__u8__user*)args);if(ret<0){printk("SPI_IOC_RD_MODE__put_userfailed.\n");gotoerr0;}break;caseSPI_IOC_RD_MAX_SPEED_HZ:/*获取SPI的最高工作速率*/ret=__put_user(my_spi_dev_temp->myspi_dev->max_speed_hz,(__u8__user*)args);if(ret<0){printk("SPI_IOC_RD_MODE__put_userfailed.\n");gotoerr0;}break;caseSPI_IOC_WR_MODE:/*设置SPI主机控制器的模式*/ret=__get_user(mode,(u8__user*)args);if(ret<0){printk("SPI_IOC_WR_MODE__get_userfailed.\n");gotoerr0;}my_spi_dev_temp->myspi_dev->mode=mode;ret=spi_setup(my_spi_dev_temp->myspi_dev);if(ret<0){printk("SPI_IOC_WR_MODEspi_setupfailed.\n");gotoerr0;}break;caseSPI_IOC_WR_BITS_PER_WORD:/*设置SPI的字长*/ret=__get_user(bits_per_word,(u8__user*)args);if(ret<0){printk("SPI_IOC_WR_BITS_PER_WORD__get_userfailed.\n");gotoerr0;}my_spi_dev_temp->myspi_dev->bits_per_word=bits_per_word;ret=spi_setup(my_spi_dev_temp->myspi_dev);if(ret<0){printk("SPI_IOC_WR_BITS_PER_WORDspi_setupfailed.\n");gotoerr0;}break;caseSPI_IOC_WR_MAX_SPEED_HZ:/*设置SPI的最大工作速率*/ret=__get_user(max_speed_hz,(u32__user*)args);if(ret<0){printk("SPI_IOC_WR_MAX_SPEED_HZ__get_userfailed.\n");gotoerr0;}my_spi_dev_temp->myspi_dev->max_speed_hz=max_speed_hz;ret=spi_setup(my_spi_dev_temp->myspi_dev);if(ret<0){printk("SPI_IOC_WR_MAX_SPEED_HZspi_setupfailed.\n");gotoerr0;}break;default:;}return0;err0:returnret<0?ret:-ret;}structfile_operationsmy_spi_ops={.open=my_spi_open,.release=my_spi_release,.read=my_spi_read,.write=my_spi_write,.unlocked_ioctl=my_spi_ioctl,};structspi_device_idmy_spi_id[]={{"spidev0"},{},};MODULE_DEVICE_TABLE(spi,my_spi_id);staticintmy_spi_probe(structspi_device*spi){intret;structmy_spi_dev*my_temp_spi_dev;PDEBUG("%s--%s--%d.\n",__FILE__,__FUNCTION__,__LINE__);spi->mode=SPI_MODE_0;spi->bits_per_word=8;ret=spi_setup(spi);if(ret<0){printk("spi_setupfailed.\n");gotospi_setup_err;}my_temp_spi_dev=kzalloc(sizeof(structmy_spi_dev),GFP_KERNEL);/*申请空间*/if(IS_ERR(my_temp_spi_dev)){ret=PTR_ERR(my_temp_spi_dev);printk("kzallocfailed.\n");gotokzalloc_err;}spi_set_drvdata(spi,my_temp_spi_dev);/*spi->dev.driver_data*/my_temp_spi_dev->myspi_dev=spi;/*把内核解析设备树生成的spi设备结构体保存在自己定义的机构体中*/ret=alloc_chrdev_region(&my_temp_spi_dev->dev_no,0,1,THTF_SPI_CHRDEV_NAME);if(ret<0){printk("alloc_chrdev_regionfailed.\n");gotoalloc_err;}PDEBUG("my_spi_majoris%d.\n",MAJOR(my_temp_spi_dev->dev_no));/*打印主设备号*/cdev_init(&my_temp_spi_dev->cdev,&my_spi_ops);/*将字符设备和文件操作方法描述集绑定*/ret=cdev_add(&my_temp_spi_dev->cdev,my_temp_spi_dev->dev_no,1);if(ret<0){printk("cdev_addfailed.\n");gotoadd_err;}my_temp_spi_dev->cls=class_create(THIS_MODULE,THTF_SPI_CLASS_NAME);if(IS_ERR(my_temp_spi_dev->cls)){ret=PTR_ERR(my_temp_spi_dev->cls);printk("class_createfailed.\n");gotocls_err;}my_temp_spi_dev->dev=device_create(my_temp_spi_dev->cls,NULL,my_temp_spi_dev->dev_no,NULL,THTF_SPI_DEVICE_NAME);if(IS_ERR(my_temp_spi_dev->dev)){ret=PTR_ERR(my_temp_spi_dev->dev);printk("device_createfailed.\n");gotodev_err;}return0;dev_err:class_destroy(my_temp_spi_dev->cls);cls_err:cdev_del(&my_temp_spi_dev->cdev);add_err:unregister_chrdev_region(my_temp_spi_dev->dev_no,1);alloc_err:kfree(my_temp_spi_dev);kzalloc_err:spi_setup_err:returnret;}staticintmy_spi_remove(structspi_device*spi){structmy_spi_dev*my_temp_spi_dev=spi_get_drvdata(spi);PDEBUG("%s--%s--%d.\n",__FILE__,__FUNCTION__,__LINE__);device_destroy(my_temp_spi_dev->cls,my_temp_spi_dev->dev_no);class_destroy(my_temp_spi_dev->cls);cdev_del(&my_temp_spi_dev->cdev);unregister_chrdev_region(my_temp_spi_dev->dev_no,1);kfree(my_temp_spi_dev);return0;}staticstructspi_drivermy_spi_drv={.probe=my_spi_probe,.remove=my_spi_remove,.id_table=my_spi_id,.driver={.name="spidev0",.owner=THIS_MODULE,},};module_spi_driver(my_spi_drv);/*注册注销驱动*/MODULE_LICENSE("GPL");MODULE_DESCRIPTION("myspidrv");2user_lib.c
主要包含了应用层spi模式、字长和频率的设置及获取,以及spi读写函数的实现。
#include<linux/spi/spidev.h>#include"user.h"u8str_to_hex(constchar*p,u8*data){inti;intlen=strlen(p);u8temp=0;for(i=0;i<len;i++){if(p[i]>='a'&&p[i]<='f'){temp=16*temp+(p[i]-'a'+10);}elseif(p[i]>='A'&&p[i]<='F'){temp=16*temp+(p[i]-'A'+10);}elseif(p[i]>='0'&&p[i]<='9'){temp=16*temp+p[i]-'0';}}*data=temp;}intget_my_spi_mode(u16*mode){intret;ret=ioctl(my_spi_fd,SPI_IOC_RD_MODE,mode);/*成功0失败-1*/if(ret==-1){printf("get_my_spi_modefailed.\n");return-1;}return0;}intget_my_spi_bits_per_word(u8*bits_per_word){intret;ret=ioctl(my_spi_fd,SPI_IOC_RD_BITS_PER_WORD,bits_per_word);if(ret==-1){printf("get_my_spi_bits_per_wordfailed.\n");return-1;}}intget_my_spi_max_speed_hz(u32*max_speed_hz){intret;ret=ioctl(my_spi_fd,SPI_IOC_RD_MAX_SPEED_HZ,max_speed_hz);if(ret==-1){printf("get_my_spi_max_speed_hzfailed.\n");return-1;}}intset_my_spi_mode(u16*mode){intret;ret=ioctl(my_spi_fd,SPI_IOC_WR_MODE,mode);if(ret==-1){printf("get_my_spi_modefailed.\n");return-1;}}intset_my_spi_bits_per_word(u8*bits_per_word){intret;ret=ioctl(my_spi_fd,SPI_IOC_WR_BITS_PER_WORD,bits_per_word);if(ret==-1){printf("get_my_spi_bits_per_wordfailed.\n");return-1;}}intset_my_spi_max_speed_hz(u32*max_speed_hz){intret;ret=ioctl(my_spi_fd,SPI_IOC_WR_MAX_SPEED_HZ,max_speed_hz);if(ret==-1){printf("get_my_spi_modefailed.\n");return-1;}}intspi_user_write(u8*write_buf,u32len){intret;ret=write(my_spi_fd,write_buf,len);/*成功:写入的字节数失败:-1*/if(ret==-1){printf("spi_user_writefailed.\n");return-1;}returnret;}intspi_user_read(u8*read_buf,u32len){intret;ret=read(my_spi_fd,read_buf,len);/*成功:读到的字节数失败:-1*/if(ret==-1){printf("spi_user_readfailed.\n");return-1;}returnret;}3test.c
应用层的测试代码
#include"user.h"intmain(intargc,constchar*argv[]){inti,ret,size=4;u16mode;u32max_speed_hz;u8bits_per_word;u8write_buf[4]={0x00,0x00,0x00};u8read_buf[4]={0x00,0x00,0x00,0x00};charstr0[3]="\0";charstr1[3]="\0";charstr2[3]="\0";charstr3[3]="\0";my_spi_fd=open("/dev/my_spi_device",O_RDWR,0666);if(my_spi_fd<0){perror("openspi_dev");return-1;}get_my_spi_mode(&mode);printf("mode=%d.\n",mode);get_my_spi_bits_per_word(&bits_per_word);printf("bits_per_word=%d\n",bits_per_word);get_my_spi_max_speed_hz(&max_speed_hz);printf("max_speed_hz=%d\n",max_speed_hz);while(1){printf("inputwrite_buf[0]write_buf[1]write_buf[2]:");scanf("%s%s%s",str0,str1,str2);str_to_hex(str0,&write_buf[0]);str_to_hex(str1,&write_buf[1]);str_to_hex(str2,&write_buf[2]);str_to_hex(str3,&write_buf[2]);spi_user_write(write_buf,sizeof(write_buf));memset(read_buf,0,sizeof(read_buf));spi_user_read(read_buf,size);for(i=0;i<4;i++){printf("read_buf[%d]=0X%02X\n",i,read_buf[i]);}}close(my_spi_fd);return0;err0:return-1;}4user.h
自定义头文件
#ifndef_USER_H_#define_USER_H_#include<stdio.h>#include<stdlib.h>#include<string.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<unistd.h>#include<sys/ioctl.h>typedefunsignedintu32;typedefunsignedshortu16;typedefunsignedcharu8;intmy_spi_fd;u8str_to_hex(constchar*,u8*);intget_my_spi_mode(u16*);intget_my_spi_bits_per_word(u8*);intget_my_spi_max_speed_hz(u32*);intset_my_spi_mode(u16*);intset_my_spi_bits_per_word(u8*);intset_my_spi_max_speed_hz(u32*);intspi_user_write(u8*,u32);intspi_user_read(u8*,u32);#endif/*user.h*/5pdebug.h
此部门摘自博客Linux内核驱动调试 在调试时使用printk会打印调试信息,而通常打印的调试信息较多,为了在调试完毕时不用将这些调试信息手动删除或者注释,参考上面的博客,将printk重新定义成PDEBUG。8行__KERNEL__是定义了的,在4行定义了DEBUG,所以会使用10行的语句。如果不想打印调试信息,将4行注释掉,DEBUG没有定义,会执行15行的语句,此时PDEBUG宏定义成空,打印信息不输出。*/
#ifndef_PDEBUG_H_#define_PDEBUG_H_#defineDEBUG#undefPDEBUG#ifdefDEBUG#ifdef__KERNEL__//#definePDEBUG(fmt,args...)printk(KERN_EMERG"leds:"fmt,##args)#definePDEBUG(fmt,args...)printk(fmt,##args)#else#definePDEBUG(fmt,args...)fprintf(stderr,fmt,##args)#endif#else#definePDEBUG(fmt,args...)#endif#undefPDEBUGG#definePDEBUGG(fmt,args...)6MakefileKERNELDIR?=/home/linux/ti-processor-sdk-linux-am335x-evm-04.00.00.04/board-support/linux-4.9.28/PWD:=$(shellpwd)EXEC=appOBJS=test.ouser_lib.oCC=arm-linux-gnueabihf-gcc$(EXEC):$(OBJS)makeARCH=armCROSS_COMPILE=arm-linux-gnueabihf--C$(KERNELDIR)M=$(PWD)modules$(CC)$^-o$@.o:.c$(CC)-c$<clean:makeARCH=armCROSS_COMPILE=arm-linux-gnueabihf--C$(KERNELDIR)M=$(PWD)cleanrmappobj-m+=spi_drv.o