1.简单的字符设备

1.实现步骤

首先想要实现一个字符设备,需要以下3步

  1. 注册设备号
  2. 初始化字符设备
  3. 实现需要的文件操作
1.1 注册设备号
1.1.1 主次设备号以及设备ID

一般情况下,设备的注册是在模块加载入口所指向的函数中完成的。
  
  
1)先将主次设备号合成为设备ID,这里需要用到LINUX内核提供的宏MKDEV
2)通过设备ID在内核中对设备进行注册
 【注意】 加载函数中的申请资源和卸载函数中的释放资源要一一配对

	//把主次设备号合并生成设备ID
 	dev_t devno = MKDEV(LED_MA, LED_MI);  
	//注册设备号:
		//"newled"是设备的名字,注册成功后可以在 /proc/devices文件中看到这名字
		//ret返回值用于判断是否成功
	ret =register_chrdev_region(devno, LED_NUM, "newled"); 

	cdev_del(&led);	//删除设备
    unregister_chrdev_region(devno, LED_NUM);  //取消注册
1.1.2 MKDEV宏的实现
/*****内核中MKDEV宏的实现*******/
 	//  文件/include/linux/kdev_t.h
 	#define MINORBITS	20
 	#define MKDEV(ma,mi)	(((ma) << MINORBITS) | (mi))
 	//作用:将主设备号MA左移20位与次设备号MI相或,最后得到设备ID
 	/*
 		|	31~20位	|	19~0位	|
		|	主设备号	|	次设备号	|
	*/
1.2 初始化字符设备
1)声明字符设备cdev类型的变量
2)声明文件操作函数指针结构体,将函数指针指向相应的文件操作函数
3)将设备和文件操作进行关联
4)将字符设备添加到系统之中
/*定义字符设备*/
struct cdev led; 

/*设备的文件操作函数指针结构体*/
struct file_operations   led_fps={ 
  .open = led_open, 
  .release = led_release,	
  .unlocked_ioctl = led_ioctl,
};

cdev_init(&led,&led_fps); 				//将设备和文件操作关联起来 	
ret = cdev_add(&led, devno, LED_NUM); 	//添加字符设备到系统中
1.3 实现需要的文件操作
1.3.1物理地址映射

由于硬件的物理地址和内核的地址是两种不同的地址,所以在内核中是不能直接访问硬件的物理地址的,需要将硬件物理地址映射成内核的虚地址。

/*LED实际的物理地址*/
#define LED2_CON	0x11000c40

/*ioremap映射后的地址*/
unsigned int  *led2con=NULL;

/*把物理地址映射为内核虚地址*/
led2con =ioremap(LED2_CON,4); 
if(led2con ==  NULL){
	printk("ioremap led2con  error\n");
	return -1;
}
1.3.2 实现ioctl
  • 当上层应用调用ioctl的时候,最终会调用到驱动中的unlocked_ioctl指向的函数,根据上层应用传入来的cmd,可以执行定制个性化的命令 (驱动提供机制,应用提供策略)
  • 驱动提供机制其实就是通过查芯片手册,对寄存器的值进行相应修改 (这一步和裸机编程是一样的)
  • 修改和读取寄存器一般使用writel和readl函数。
long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	  //printk(" led_ioctl go\n");
      switch(cmd)
      {
      case LED2_ON:  
		writel((readl(led2dat)& (~(0x1 << 7))) | (0x1<<7),led2dat);
        break;
      case LED2_OFF:  
		writel(readl(led2dat)&(~(0x1<<7)),led2dat);
        break; 
      case LED3_ON:  
		writel((readl(led3dat)& (~(0x1 << 0))) | (0x1<<0),led3dat);
        break;
      case LED3_OFF:  
		writel(readl(led3dat) &(~(0x1<<0)),led3dat);
        break;
	  case LED4_ON:  
		writel((readl(led4dat)& (~(0x1 << 4))) | (0x1<<4),led4dat);
        break;
      case LED4_OFF:  
		writel(readl(led4dat)&(~(0x1<<4)),led4dat);
        break; 
      default:
        printk(" no found cmd = %d\n",cmd);	  	
	  	break;
    }		
	return 0;
}
2.驱动代码文件

LED驱动

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值