linux增加字符设备,linux简单字符设备

linux设备驱动分3类:字符设备驱动、块设备驱动、网络设备驱动。废话少说:

1、字符设备结构体描述:cdev

struct cdev{

struct kobject kobj;

strcut module *owner;

struct file_operations *ops;

struct list_head list;

dev_t dev;

unsigned int count;

};

2、linux2.6内核提供了一组函数来操作cdev结构体

void cdev_init(struct cdev *,struct file_operations *);

struct cdev *cdev_alloc(void);

void cdev_put(struct cdev *p);

int cdev_add(struct cdev *,dev_t,unsigned);

void cdev_del(struct cdev *);

在调用cdev_add函数向系统注册字符设备之前,应该先调用register_chrdev_region()函数或是alloc_chrdev_region()函数向系统申请设备号;模型为:

int register_chrdev_region(dev_t from,unsigned count,const

char *name);

int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned

count,const char *name)

在系统调用cdev_del函数从系统注销字符设备后,unregister_chrdev_region()应该释放之前申请的设备号,该函数原型为:

unregister_chrdev_region(dev_t from,unsigned count)

3、关于file_operations的内容请参考下篇分析

下面代码是基于虚拟的globalmem设备进行字符设备的分析,具体在linux中设备驱动远要比这个复杂。

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define GLOBALMEM_SIZE 0x1000

#define MEM_CLEAR 0x1

#define GLOBALMEM_MAJOR 250

static int globalmem_major = GLOBALMEM_MAJOR;

struct globalmem_dev {

struct cdev cdev;

unsigned char mem[GLOBALMEM_SIZE];

};

struct globalmem_dev *globalmem_devp;

int globalmem_open(struct inode *inode, struct file *filp)

{

filp->private_data = globalmem_devp;

return 0;

}

int globalmem_release(struct inode *inode, struct file *filp)

{

return 0;

}

static int globalmem_ioctl(struct inode *inodep, struct file *filp,

unsigned

int cmd, unsigned long arg)

{

struct globalmem_dev *dev = filp->private_data;

switch (cmd) {

case MEM_CLEAR:

memset(dev->mem, 0, GLOBALMEM_SIZE);

printk(KERN_INFO "globalmem is set to zero\n");

break;

default:

return - EINVAL;

}

return 0;

}

static ssize_t globalmem_read(struct file *filp, char __user *buf,

size_t size,

loff_t *ppos)

{

unsigned long p = *ppos;

unsigned int count = size;

int ret = 0;

struct globalmem_dev *dev = filp->private_data;

if (p >= GLOBALMEM_SIZE)

return 0;

if (count > GLOBALMEM_SIZE - p)

count = GLOBALMEM_SIZE - p;

if (copy_to_user(buf, (void *)(dev->mem + p),

count)) {

ret = - EFAULT;

} else {

*ppos += count;

ret = count;

printk(KERN_INFO "read %u bytes(s) from %lu\n", count, p);

}

return ret;

}

static ssize_t globalmem_write(struct file *filp, const char __user

*buf,

size_t size, loff_t *ppos)

{

unsigned long p = *ppos;

unsigned int count = size;

int ret = 0;

struct globalmem_dev *dev = filp->private_data;

if (p >= GLOBALMEM_SIZE)

return 0;

if (count > GLOBALMEM_SIZE - p)

count = GLOBALMEM_SIZE - p;

if (copy_from_user(dev->mem + p, buf, count))

ret = - EFAULT;

else {

*ppos += count;

ret = count;

printk(KERN_INFO "written %u bytes(s) from %lu\n", count,

p);

}

return ret;

}

static loff_t globalmem_llseek(struct file *filp, loff_t offset,

int orig)

{

loff_t ret = 0;

switch (orig) {

case 0:

if (offset < 0) {

ret = - EINVAL;

break;

}

if ((unsigned int)offset > GLOBALMEM_SIZE) {

ret = - EINVAL;

break;

}

filp->f_pos = (unsigned int)offset;

ret = filp->f_pos;

break;

case 1:

if ((filp->f_pos + offset) >

GLOBALMEM_SIZE) {

ret = - EINVAL;

break;

}

if ((filp->f_pos + offset) < 0)

{

ret = - EINVAL;

break;

}

filp->f_pos += offset;

ret = filp->f_pos;

break;

default:

ret = - EINVAL;

break;

}

return ret;

}

static const struct file_operations globalmem_fops = {

.owner = THIS_MODULE,

.llseek = globalmem_llseek,

.read = globalmem_read,

.write = globalmem_write,

.ioctl = globalmem_ioctl,

.open = globalmem_open,

.release = globalmem_release,

};

static void globalmem_setup_cdev(struct globalmem_dev *dev, int

index)

{

int err, devno = MKDEV(globalmem_major, index);

cdev_init(&dev->cdev,

&globalmem_fops);

dev->cdev.owner = THIS_MODULE;

err = cdev_add(&dev->cdev, devno,

1);

if (err)

printk(KERN_NOTICE "Error %d adding LED%d", err, index);

}

int globalmem_init(void)

{

int result;

dev_t devno = MKDEV(globalmem_major, 0);

if (globalmem_major)

result = register_chrdev_region(devno, 1, "globalmem");

else {

result = alloc_chrdev_region(&devno, 0, 1,

"globalmem");

globalmem_major = MAJOR(devno);

}

if (result < 0)

return result;

globalmem_devp = kmalloc(sizeof(struct globalmem_dev),

GFP_KERNEL);

if (!globalmem_devp) {

result = - ENOMEM;

goto fail_malloc;

}

memset(globalmem_devp, 0, sizeof(struct globalmem_dev));

globalmem_setup_cdev(globalmem_devp, 0);

return 0;

fail_malloc:

unregister_chrdev_region(devno, 1);

return result;

}

void globalmem_exit(void)

{

cdev_del(&globalmem_devp->cdev);

kfree(globalmem_devp);

unregister_chrdev_region(MKDEV(globalmem_major, 0), 1);

}

MODULE_AUTHOR("Barry Song

<21cnbao@gmail.com>");

MODULE_LICENSE("Dual BSD/GPL");

module_param(globalmem_major, int, S_IRUGO);

module_init(globalmem_init);

module_exit(globalmem_exit);

359 static int

chrdev_open(struct inode *inode, struct file *filp)

360 {

361 struct cdev *p;

362 struct cdev *new = NULL;

363 int ret = 0;

364

365 spin_lock(&cdev_lock);

366 p = inode->i_cdev;

367 if (!p) {

368 struct kobject *kobj;

369 int idx;

370 spin_unlock(&cdev_lock);

371 kobj = kobj_lookup(cdev_map, inode->i_rdev,

&idx);

372 if (!kobj)

373 return

-ENXIO;

374 new = container_of(kobj, struct cdev, kobj);

375 spin_lock(&cdev_lock);

376 p = inode->i_cdev;

377 if (!p) {

378 inode->i_cdev = p = new;

379 inode->i_cindex = idx;

380 list_add(&inode->i_devices,

&p->list);

381 new = NULL;

382 } else if (!cdev_get(p))

383 ret = -ENXIO;

384 }

else if (!cdev_get(p))

385 ret = -ENXIO;

386 spin_unlock(&cdev_lock);

387 cdev_put(new);

388 if (ret)

389 return ret;

390 filp->f_op =

fops_get(p->ops);

391 if (!filp->f_op) {

392 cdev_put(p);

393 return -ENXIO;

394 }

395 if (filp->f_op->open) {

396 lock_kernel();

397 ret =

filp->f_op->open(inode,filp);

398 unlock_kernel();

399 }

400 if (ret)

401 cdev_put(p);

402 return ret;

403 }

下一步要创建设备文件。

mknod /dev/test c major

minor

c 是指字符设备,major是主设备号,就是在/proc/devices里看到的。

用shell命令

$ cat /proc/devices

就可以获得主设备号,可以把上面的命令行加入你的shell script中去。

minor是从设备号,设置成0就可以了。

我们现在可以通过设备文件来访问我们的驱动程序。写一个小小的测试程序。

#include

#include

#include

#include

main()

{

int testdev;

int i;

char buf[10];

testdev = open("/dev/test",O_RDWR);

if ( testdev == -1 )

{

printf("Cann't open file n");

exit(0);

}

read(testdev,buf,10);

for (i = 0; i < 10;i++)

printf("%dn",buf[i]);

close(testdev);

}

hello_ioctl()的定义如下:

static int hello_ioctl( struct file

*file,

unsigned int cmd, unsigned long arg)

{ int temp =

0;

switch(cmd)

{

case HELLO_CMD1:

{

temp = 1;

if(copy_to_user( (int *)arg,

&temp, sizeof(int))) return -EFAULT;

break;

}

case HELLO_CMD2:

{

temp = 2;

if(copy_to_user( (int *)arg,

&temp, sizeof(int))) return -EFAULT;

break;

}

}

printk(

KERN_NOTICE"ioctl CMD%d

done!\n",temp); return 0;

}

这里强调一下cmd的定义:

#define HELLO_MAGIC

'k'

#define

HELLO_CMD1 _IO(HELLO_MAGIC,0x1a)

#define

HELLO_CMD2 _IO(HELLO_MAGIC,0x1b)

其中'k'为幻数,要按照Linux内核的约定方法为驱动程序选择ioctl编号,应该首先看看include/asm/ioctl.h和Documentation/ioctl-number.txt这两个文件,下面是ioctl.h的部分内容,也是比较重要的:

_IO(type, nr)

用于构造无参数的命令编号;

_IOR(type, nr,

datatype)

用于构造从驱动程序中读取数据的命令编号;

_IOW(type, nr,

datatype)

用于写入数据的命令;

_IOWR(type, nr,

datatype)

用于双向传输。注意千万不能重复定义。

注意对幻数的编号千万不能重复定义,如ioctl-number.txt已经说明‘k'的编号已经被占用的范围为:

'k' 00-0F linux/spi/spidev.h conflict!

'k' 00-05 video/kyro.h conflict!

所以我们在这里分别编号为0x1a和0x1b,到这里,我们已经完成了对ioctl功能的编写,接下来就是在测试程序中利用系统调用来测试它。

=============================================================

ioctl测试程序

=============================================================

#include

#include

#include

#include

#include

#include

#include

#include

#define HELLO_MAGIC 'k' //当然我们也可以定义一个相应的头文件,把ioctl的cmd放进里面,然后再include进 来

#define HELLO_CMD1 _IO(HELLO_MAGIC,0x1a)

#define HELLO_CMD2 _IO(HELLO_MAGIC,0x1b)

int main(void)

{

int

ioctl_rdata;

int fd,

ret;

fd = open ( "/dev/hellow" , O_RDWR);

if ( fd ==

-1 )

{

perror("open");

exit(0);

}

ret = ioctl( fd, HELLO_CMD2,&ioctl_rdata);

if ( ret ==

-1)

{

perror("ioctl");

exit(0);

}

printf("ioctl_rdata= %d \n",ioctl_rdata);

close(fd);

return

0;

}

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define MEMDEV_MAJOR 251

#define MEMDEV_NUM 2

#define MEMDEV_SIZE 1024

struct mem_dev

{

unsignedint size;

char*data;

structsemaphore sem;

};

static int mem_major =

MEMDEV_MAJOR;

struct cdev mem_cdev;

struct mem_dev *mem_devp;

static int mem_open(struct inode *inode,struct file

*filp)

{

structmem_dev *dev;

unsignedint num;

printk("mem_open.\n");

num=

MINOR(inode->i_rdev);//获得次设备号

if(num> (MEMDEV_NUM

-1)) //检查次设备号有效性

return-ENODEV;

dev=

&mem_devp[num];

filp->private_data= dev;

//将设备结构保存为私有数据

return0;

}

static int mem_release(struct inode *inode,struct file

*filp)

{

printk("mem_release.\n");

return0;

}

static ssize_t mem_read(struct file *filp,char __user *buf, size_t

size, loff_t *ppos)

{

intret = 0;

structmem_dev *dev;

unsignedlong p;

unsignedlong count;

printk("mem_read.\n");

dev=

filp->private_data;//获得设备结构

count= size;

p= *ppos;

//检查偏移量和数据大小的有效性

if(p>

MEMDEV_SIZE)

return0;

if(count>

(MEMDEV_SIZE-p))

count= MEMDEV_SIZE - p;

if(down_interruptible(&dev->sem))//锁定互斥信号量

return -ERESTARTSYS;

//读取数据到用户空间

if(copy_to_user(buf,dev->data+p,

count)){

ret= -EFAULT;

printk("copyfrom user

failed\n");

}

else{

*ppos+= count;

ret= count;

printk("read%d bytes from dev\n",

count);

}

up(&dev->sem);//解锁互斥信号量

returnret;

}

static ssize_t mem_write(struct file *filp,const char __user *buf,

size_t size, loff_t

*ppos)//注意:第二个参数和read方法不同

{

intret = 0;

structmem_dev *dev;

unsignedlong p;

unsignedlong count;

printk("mem_write.\n");

dev=

filp->private_data;

count= size;

p= *ppos;

if(p>

MEMDEV_SIZE)

return0;

if(count>

(MEMDEV_SIZE-p))

count= MEMDEV_SIZE - p;

if(down_interruptible(&dev->sem))//锁定互斥信号量

return-ERESTARTSYS;

if(copy_from_user(dev->data+p,buf,

count)){

ret= -EFAULT;

printk("copyfrom user

failed\n");

}

else{

*ppos+= count;

ret= count;

printk("write%d bytes to dev\n",

count);

}

up(&dev->sem);//解锁互斥信号量

returnret;

}

static loff_t mem_llseek(struct file *filp,loff_t offset, int

whence)

{

intnewpos;

printk("mem_llseek.\n");

switch(whence)

{

case0:

newpos= offset;

break;

case1:

newpos= filp->f_pos +

offset;

break;

case2:

newpos= MEMDEV_SIZE - 1 +

offset;

break;

default:

return-EINVAL;

}

if((newpos<0)|| (newpos>(MEMDEV_SIZE

- 1)))

return-EINVAL;

filp->f_pos=

newpos;

returnnewpos;

}

static const struct file_operationsmem_fops =

{

.owner= THIS_MODULE,

.open= mem_open,

.write= mem_write,

.read= mem_read,

.release= mem_release,

.llseek= mem_llseek,

};

static int __init

memdev_init(void)

{

intresult;

interr;

inti;

//申请设备号

dev_tdevno = MKDEV(mem_major,

0);

if(mem_major)

result= register_chrdev_region(devno, MEMDEV_NUM,

"memdev");//注意静态申请的dev_t参数和动态dev_t参数的区别

else{ //静态直接传变量,动态传变量指针

result= alloc_chrdev_region(&devno, 0, MEMDEV_NUM,

"memdev");

mem_major= MAJOR(devno);

}

if(result< 0){

printk("can'tget major devno:%d\n",

mem_major);

returnresult;

}

//注册设备驱动

cdev_init(&mem_cdev,&mem_fops);

mem_cdev.owner= THIS_MODULE;

err= cdev_add(&mem_cdev, MKDEV(mem_major, 0),

MEMDEV_NUM);//如果有N个设备就要添加N个设备号

if(err)

printk("addcdev faild,err is %d\n",

err);

//分配设备内存

mem_devp= kmalloc(MEMDEV_NUM*(sizeof(struct mem_dev)),

GFP_KERNEL);

if(!mem_devp){

result = - ENOMEM;

goto fail_malloc;

}

memset(mem_devp,0, MEMDEV_NUM*(sizeof(struct

mem_dev)));

for(i=0;i

i++){

mem_devp[i].size= MEMDEV_SIZE;

mem_devp[i].data= kmalloc(MEMDEV_SIZE,

GFP_KERNEL);

memset(mem_devp[i].data,0,

MEMDEV_SIZE);

init_MUTEX(&mem_devp[i].sem);//初始化互斥锁

}

returnresult;

fail_malloc:

unregister_chrdev_region(MKDEV(mem_major,0),

MEMDEV_NUM);

returnresult;

}

static void memdev_exit(void)

{

cdev_del(&mem_cdev);

unregister_chrdev_region(MKDEV(mem_major,0),

MEMDEV_NUM);//注意释放的设备号个数一定要和申请的设备号个数保存一致

//否则会导致设备号资源流失

printk("memdev_exit\n");

}

module_init(memdev_init);

module_exit(memdev_exit);

MODULE_AUTHOR("Y-Kee");

MODULE_LICENSE("GPL");

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值