博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ds1620混杂设备驱动
阅读量:4058 次
发布时间:2019-05-25

本文共 10664 字,大约阅读时间需要 35 分钟。

下面分析一下数字温度计DS1620的驱动程序。先从网上找到其数据手册,看看它的功能,再找到时序图片一下。这样有利于分析驱动,再套用上linux的驱动框架就可以了。

同样,先找驱动的入口和出口,看看他们都做了什么。

module_init(ds1620_init);

module_exit(ds1620_exit);

先来看一些常量的定义吧

/*

* arch/arm/include/asm/therm.h: Definitions for Dallas Semiconductor

* DS1620 thermometer driver (as used in the Rebel.com NetWinder)

*/

#ifndef __ASM_THERM_H

#define __ASM_THERM_H

/* ioctl numbers for /dev/therm */

#define CMD_SET_THERMOSTATE 0x53

#define CMD_GET_THERMOSTATE 0x54

#define CMD_GET_STATUS 0x56

#define CMD_GET_TEMPERATURE 0x57

#define CMD_SET_THERMOSTATE2 0x58

#define CMD_GET_THERMOSTATE2 0x59

#define CMD_GET_TEMPERATURE2 0x5a

#define CMD_GET_FAN 0x5b

#define CMD_SET_FAN 0x5c

#define FAN_OFF 0

#define FAN_ON 1

#define FAN_ALWAYS_ON 2

struct therm {

int hi;

int lo;

};

#endif

定义了一些常量寄存器的值:

/*Definitions for DS1620 chip */

#defineTHERM_START_CONVERT 0xee

#defineTHERM_RESET 0xaf

#defineTHERM_READ_CONFIG 0xac

#defineTHERM_READ_TEMP 0xaa

#defineTHERM_READ_TL 0xa2

#defineTHERM_READ_TH 0xa1

#defineTHERM_WRITE_CONFIG 0x0c

#defineTHERM_WRITE_TL 0x02

#defineTHERM_WRITE_TH 0x01

#defineCFG_CPU 2

#defineCFG_1SHOT 1

staticint__init ds1620_init(void)

{

intret;

structtherm th, th_start;

这个什么东东????

if(!machine_is_netwinder())

return-ENODEV;

这个是设置一些参数?复位,配置,停止转换。

ds1620_out(THERM_RESET,0, 0);

ds1620_out(THERM_WRITE_CONFIG,8, CFG_CPU);

ds1620_out(THERM_START_CONVERT,0, 0);

/*

* Trigger the fan to start by setting

* temperature high point low. This kicks

* the fan into action.

*/

ds1620_read_state(&th);

th_start.lo= 0;

th_start.hi= 1;

ds1620_write_state(&th_start);

msleep(2000);

ds1620_write_state(&th);

这里是注册一个混杂设备(混杂设备就是不知道如何归类的)

ret= misc_register(&ds1620_miscdev);

if(ret < 0)

returnret;

这里创建/proc文件系统节点,方便从/proc中获取信息,这样就不用写标准的测试程序了。

就不用走那套老的程序open(),read(),write(),close().

#ifdefTHERM_USE_PROC

proc_therm_ds1620= create_proc_entry("therm",0, NULL);

if(proc_therm_ds1620)

proc_therm_ds1620->read_proc= proc_therm_ds1620_read;

else

printk(KERN_ERR"therm:unable to register /proc/therm\n");

#endif

读取一下当前状态,输出一下信息。

ds1620_read_state(&th);

ret= cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9));

printk(KERN_INFO"Thermostat:high %i.%i, low %i.%i, "

"current%i.%i C, fan %s.\n",

th.hi >> 1, th.hi & 1 ? 5 : 0,

th.lo >> 1, th.lo & 1 ? 5 : 0,

ret >> 1, ret & 1 ? 5 : 0,

fan_state[netwinder_get_fan()]);

return0;

}

staticvoid__exit ds1620_exit(void)

{

移除/proc节点

#ifdefTHERM_USE_PROC

remove_proc_entry("therm",NULL);

#endif

注销混杂设备。

misc_deregister(&ds1620_miscdev);

}

再来看一下混杂设备的结构体及接口函数:

structmiscdevice {

intminor;

constchar*name;

conststructfile_operations*fops;

structlist_headlist;

structdevice*parent;

structdevice*this_device;

constchar*nodename;

mode_tmode;

};

externintmisc_register(structmiscdevice* misc);

externintmisc_deregister(structmiscdevice*misc);

这里是真实代码:

staticstructmiscdeviceds1620_miscdev= {

TEMP_MINOR,

"temp",

&ds1620_fops

};

从文件操作指针可以看来,也只支持open,read,ioctl三个函数。

staticconststructfile_operationsds1620_fops = {

.owner =THIS_MODULE,

.open =ds1620_open,

.read =ds1620_read,

.unlocked_ioctl =ds1620_unlocked_ioctl,

.llseek =no_llseek,

};

看一下打开函数,这里调用了一个奇怪的函数,以前没有见到过呀:

立即F3查看一下

好像就是设置一个这个fd,不支持seek的功能。

staticintds1620_open(structinode*inode, structfile*file)

{

returnnonseekable_open(inode, file);

}

/*

*This is used by subsystems that don't want seekable

*file descriptors. The function is not supposed to ever fail, the only

*reason it returns an 'int'and not 'void' is so that it can be plugged

*directly into file_operations structure.

*/

intnonseekable_open(structinode*inode, structfile*filp)

{

filp->f_mode&= ~(FMODE_LSEEK| FMODE_PREAD| FMODE_PWRITE);

return0;

}

EXPORT_SYMBOL(nonseekable_open);

这个不管,直接返回0也行。再来看看read()函数。最为关键是读取数据那部分。

到底是怎么读取回来的。这些只是套用linux的驱动框架而已。

staticssize_t

ds1620_read(structfile*file, char__user *buf, size_tcount, loff_t*ptr)

{

signedintcur_temp;

signedcharcur_temp_degF;

读取值并做转换。

cur_temp= cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9)) >> 1;

/*convert to Fahrenheit,as per wdt.c */

cur_temp_degF= (cur_temp * 9) / 5 + 32;

拷贝到用户空间。

if(copy_to_user(buf, &cur_temp_degF, 1))

return-EFAULT;

return1;

}

再来看看ioctl()支持的功能,这里使用了互斥锁,有效的防止多线程访问时导致的不可见错误。

staticlong

ds1620_unlocked_ioctl(structfile*file, unsignedintcmd, unsignedlongarg)

{

intret;

mutex_lock(&ds1620_mutex);

ret= ds1620_ioctl(file, cmd, arg);

mutex_unlock(&ds1620_mutex);

returnret;

}

这里的ioctl()支持好几个命令呢。

staticint

ds1620_ioctl(structfile*file, unsignedintcmd, unsignedlongarg)

{

structthermtherm;

union{

structtherm__user *therm;

int__user *i;

}uarg;

inti;

uarg.i= (int__user *)arg;

switch(cmd){

caseCMD_SET_THERMOSTATE:

caseCMD_SET_THERMOSTATE2:

这里检测是否具有能力

if(!capable(CAP_SYS_ADMIN))

return-EPERM;

if(cmd == CMD_SET_THERMOSTATE) {

if(get_user(therm.hi,uarg.i))

return-EFAULT;

therm.lo= therm.hi- 3;

}else{

if(copy_from_user(&therm, uarg.therm,sizeof(therm)))

return-EFAULT;

}

therm.lo<<= 1;

therm.hi<<= 1;

ds1620_write_state(&therm);

break;

caseCMD_GET_THERMOSTATE:

caseCMD_GET_THERMOSTATE2:

ds1620_read_state(&therm);

therm.lo>>= 1;

therm.hi>>= 1;

if(cmd == CMD_GET_THERMOSTATE) {

if(put_user(therm.hi,uarg.i))

return-EFAULT;

}else{

if(copy_to_user(uarg.therm,&therm, sizeof(therm)))

return-EFAULT;

}

break;

caseCMD_GET_TEMPERATURE:

caseCMD_GET_TEMPERATURE2:

i= cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9));

if(cmd == CMD_GET_TEMPERATURE)

i>>= 1;

returnput_user(i,uarg.i)? -EFAULT : 0;

caseCMD_GET_STATUS:

i= ds1620_in(THERM_READ_CONFIG, 8) & 0xe3;

returnput_user(i,uarg.i)? -EFAULT : 0;

caseCMD_GET_FAN:

i= netwinder_get_fan();

returnput_user(i,uarg.i)? -EFAULT : 0;

caseCMD_SET_FAN:

if(!capable(CAP_SYS_ADMIN))

return-EPERM;

if(get_user(i,uarg.i))

return-EFAULT;

netwinder_set_fan(i);

break;

default:

return-ENOIOCTLCMD;

}

return0;

}

再来看看子函数的实现:

staticvoidds1620_write_state(structtherm*therm)

{

ds1620_out(THERM_WRITE_CONFIG,8, CFG_CPU);

ds1620_out(THERM_WRITE_TL,9, therm->lo);

ds1620_out(THERM_WRITE_TH,9, therm->hi);

ds1620_out(THERM_START_CONVERT,0, 0);

}

staticvoidds1620_read_state(structtherm*therm)

{

therm->lo= cvt_9_to_int(ds1620_in(THERM_READ_TL, 9));

therm->hi= cvt_9_to_int(ds1620_in(THERM_READ_TH, 9));

}

接着调用子函数:

staticvoidds1620_out(intcmd, intbits, intvalue)

{

unsignedlongflags;

netwinder_lock(&flags);

netwinder_ds1620_set_clk(1);

netwinder_ds1620_set_data_dir(0);

netwinder_ds1620_reset();

udelay(1);

ds1620_send_bits(8,cmd);

if(bits)

ds1620_send_bits(bits,value);

udelay(1);

netwinder_ds1620_reset();

netwinder_unlock(&flags);

msleep(20);

}

staticunsignedintds1620_in(intcmd, intbits)

{

unsignedlongflags;

unsignedintvalue;

netwinder_lock(&flags);

netwinder_ds1620_set_clk(1);

netwinder_ds1620_set_data_dir(0);

netwinder_ds1620_reset();

udelay(1);

ds1620_send_bits(8,cmd);

netwinder_ds1620_set_data_dir(1);

value= ds1620_recv_bits(bits);

netwinder_ds1620_reset();

netwinder_unlock(&flags);

returnvalue;

}

接着嵌套调用,唉,调用这么多

看到send()函数这样写法,很明显是使用GPIO来模拟时序的。低---高。模拟时钟。

staticvoidds1620_send_bits(intnr, intvalue)

{

inti;

for(i = 0; i < nr; i++) {

netwinder_ds1620_set_data(value& 1);

netwinder_ds1620_set_clk(0);

udelay(1);

netwinder_ds1620_set_clk(1);

udelay(1);

value>>= 1;

}

}

这个接收函数也是使用GPIO来模拟时序。

staticunsignedintds1620_recv_bits(intnr)

{

unsignedintvalue = 0, mask = 1;

inti;

netwinder_ds1620_set_data(0);

for(i = 0; i < nr; i++) {

netwinder_ds1620_set_clk(0);

udelay(1);

if(netwinder_ds1620_get_data())

value|= mask;

mask<<= 1;

netwinder_ds1620_set_clk(1);

udelay(1);

}

returnvalue;

}

下面这几个函数就是最终操作的GPIO的高低电平值:

staticinlinevoidnetwinder_ds1620_set_clk(intclk)

{

nw_gpio_modify_op(GPIO_DSCLK,clk ? GPIO_DSCLK: 0);

}

staticinlinevoidnetwinder_ds1620_set_data(intdat)

{

nw_gpio_modify_op(GPIO_DATA,dat ? GPIO_DATA: 0);

}

staticinlineintnetwinder_ds1620_get_data(void)

{

returnnw_gpio_read() & GPIO_DATA;

}

staticinlinevoidnetwinder_ds1620_set_data_dir(intdir)

{

nw_gpio_modify_io(GPIO_DATA,dir ? GPIO_DATA: 0);

}

staticinlinevoidnetwinder_ds1620_reset(void)

{

nw_cpld_modify(CPLD_DS_ENABLE,0);

nw_cpld_modify(CPLD_DS_ENABLE,CPLD_DS_ENABLE);

}

staticinlinevoidnetwinder_lock(unsignedlong*flags)

{

spin_lock_irqsave(&nw_gpio_lock,*flags);

}

staticinlinevoidnetwinder_unlock(unsignedlong*flags)

{

spin_unlock_irqrestore(&nw_gpio_lock,*flags);

}

staticinlinevoidnetwinder_set_fan(inti)

{

unsignedlongflags;

spin_lock_irqsave(&nw_gpio_lock,flags);

nw_gpio_modify_op(GPIO_FAN,i ? GPIO_FAN: 0);

spin_unlock_irqrestore(&nw_gpio_lock,flags);

}

staticinlineintnetwinder_get_fan(void)

{

if((system_rev & 0xf000) == 0x4000)

returnFAN_ALWAYS_ON;

return(nw_gpio_read() & GPIO_FAN)? FAN_ON : FAN_OFF;

}

这里面反复调用了nw_gpio_modify_op这个函数,来看看这个是干什么用的

,这驱动谁写的,搞得嵌套这么多函数,头不疼吗。俺好累呀。

这里的outb()就是输出一个字节的意思啦。最终的输出在这里,

肯定是经过映射后的虚拟地址吧

voidnw_gpio_modify_op(unsignedintmask, unsignedintset)

{

unsignedintnew_gpio, changed;

new_gpio= (current_gpio_op & ~mask) | set;

changed= new_gpio ^ current_gpio_op;

current_gpio_op= new_gpio;

if(changed & 0xff)

outb(new_gpio,GP1_IO_BASE);

if(changed & 0xff00)

outb(new_gpio>> 8, GP2_IO_BASE);

}

EXPORT_SYMBOL(nw_gpio_modify_op);

unsignedintnw_gpio_read(void)

{

returninb(GP1_IO_BASE) | inb(GP2_IO_BASE) << 8;

}

EXPORT_SYMBOL(nw_gpio_read);

来看看混杂设备的设备号:

#ifndef_LINUX_MISCDEVICE_H

#define_LINUX_MISCDEVICE_H

#include<linux/module.h>

#include<linux/major.h>

/*

* Theseallocations are managed by device@lanana.org. If you use an

* entrythat is not in assigned your entry may well be moved and

* reassigned,or set dynamic if a fixed value is not justified.

*/

#definePSMOUSE_MINOR 1

#defineMS_BUSMOUSE_MINOR 2

#defineATIXL_BUSMOUSE_MINOR 3

/*#defineAMIGAMOUSE_MINOR 4 FIXME OBSOLETE */

#defineATARIMOUSE_MINOR 5

#defineSUN_MOUSE_MINOR 6

#defineAPOLLO_MOUSE_MINOR 7

#definePC110PAD_MINOR 9

/*#defineADB_MOUSE_MINOR 10 FIXME OBSOLETE */

#defineWATCHDOG_MINOR 130 /* Watchdog timer */

#defineTEMP_MINOR 131 /* Temperature Sensor */ 这里温度传感器

#defineRTC_MINOR 135 这里是RTC的设备号.

#defineEFI_RTC_MINOR 136 /* EFI Time services */

#defineSUN_OPENPROM_MINOR 139

#defineDMAPI_MINOR 140 /* DMAPI */

#defineNVRAM_MINOR 144

#defineSGI_MMTIMER 153

#defineSTORE_QUEUE_MINOR 155

#defineI2O_MINOR 166

#defineMICROCODE_MINOR 184

#defineTUN_MINOR 200

#defineMWAVE_MINOR 219 /* ACP/Mwave Modem */

#defineMPT_MINOR 220

#defineMPT2SAS_MINOR 221

#defineUINPUT_MINOR 223

#defineHPET_MINOR 228

#defineFUSE_MINOR 229

#defineKVM_MINOR 232

#defineBTRFS_MINOR 234

#defineAUTOFS_MINOR 235

#defineMAPPER_CTRL_MINOR 236

#defineMISC_DYNAMIC_MINOR 255

structdevice;

structmiscdevice {

intminor;

constchar *name;

conststruct file_operations *fops;

structlist_head list;

structdevice *parent;

structdevice *this_device;

constchar *nodename;

mode_tmode;

};

externint misc_register(struct miscdevice * misc);

externint misc_deregister(struct miscdevice *misc);

#defineMODULE_ALIAS_MISCDEV(minor) \

MODULE_ALIAS("char-major-"__stringify(MISC_MAJOR) \

"-"__stringify(minor))

#endif

你可能感兴趣的文章
9、VUE面经
查看>>
关于进制转换的具体实现代码
查看>>
Golang 数据可视化利器 go-echarts ,实际使用
查看>>
mysql 跨机器查询,使用dblink
查看>>
mysql5.6.34 升级到mysql5.7.32
查看>>
dba 常用查询
查看>>
Oracle 异机恢复
查看>>
Oracle 12C DG 搭建(RAC-RAC/RAC-单机)
查看>>
Truncate 表之恢复
查看>>
Oracle DG failover 后恢复
查看>>
mysql 主从同步配置
查看>>
为什么很多程序员都选择跳槽?
查看>>
mongdb介绍
查看>>
mongdb在java中的应用
查看>>
区块链技术让Yotta企业云盘为行政事业服务助力
查看>>
Yotta企业云盘更好的为媒体广告业服务
查看>>
Yotta企业云盘助力科技行业创高峰
查看>>
Yotta企业云盘更好地为教育行业服务
查看>>
Yotta企业云盘怎么帮助到能源化工行业
查看>>
企业云盘如何助力商业新发展
查看>>