首先聊一下linux中的软件工作岗位,有专门负责BSP的,负责把uboot、kernel、文件系统都搞定,这些都是比较复杂的;打包好了之后,基本万年不动,除非有问题暴露需要解决;内核驱动里面有基于总线架构和一些子系统实现,比如input子系统、pinctrl子系统、i2c、spi总线模型驱动;还有一些块驱动,比如,Nand、EMMC驱动;网络驱动,有网卡芯片、PHY芯片,和spi接口的网卡芯片驱动都算是网络驱动;最后介绍一下字符驱动,这是最常更改的一类驱动,也通常是自己实现的驱动。
驱动的范围包含:LED、DO控制、DI读取、按键读取等;
网上教学的LED字符驱动有很多,大多数从iormap,或者调用底层的地址去点灯,那是想让大家了解与单片机操作地址的区别;那些博主都非常的棒,我现在直接上干货,项目和工作中直接的用法。
关注微信公众号,回复“led字符驱动”,下载源代码。
1、模块驱动的入口函数
module_init(at91_led_init);//加载模块先运行init函数
module_exit(at91_led_cleanup);//卸载模块进入cleanup函数
//符合GPL规范,开源,否则无法加载进内核运行
MODULE_DESCRIPTION ("AT91 led DRIVER");
MODULE_VERSION(DRV_VERSION);
MODULE_AUTHOR ("Jack");
MODULE_LICENSE ("GPL");
//驱动模块的描述信息,可帮助使用者了解这个模块的功能
#define DRV_VERSION "1.0.0"
static int led_major;
module_param(led_major, int, 0);
MODULE_PARM_DESC(led_major, "led Major device number");
2、创建init和cleanup函数
定义初始化信息,4个LED灯
struct led_pin
{
ulong led_pin_run; /* 运行灯 */
ulong led_pin_fault; /* 故障灯 */
ulong led_pin_can5; /* CAN5 */
ulong led_pin_can6; /* CAN6 */
};
struct St_led
{
dev_t dev_major;
struct led_pin st_pin;
atomic_t open_count;
struct mutex led_mudex;//互斥锁
struct cdev led_dev;//
struct class *led_class;
struct device *led_device;
};
注册驱动流程
static int __init at91_led_init(void)
{
int retval = -1;
dev_t dev_id;
// 分配一个结构
pled = (struct St_led *)kzalloc(sizeof (struct St_led),GFP_KERNEL);
if(!pled)
{
printk("%s:kzalloc failded from:%s\n", __func__,LED_NAME);
return -ENOMEM;
}
//初始化锁,指定具体的PIN脚,根据数据手册定义的地址自定义宏
pled->st_pin.led_pin_run = AT91_PIN_PD26; /* 运行灯 */
pled->st_pin.led_pin_fault= AT91_PIN_PD27; /* 故障灯 */
pled->st_pin.led_pin_can5= AT91_PIN_PA29; /* CAN5灯 */
pled->st_pin.led_pin_can6= AT91_PIN_PA6; /* CAN6灯 */
//加了互斥锁,只能同时被一个应用程序调用
//printk("led_pin:green:%d\n",pwdt->wdg_pin);
mutex_init(&(pled->led_mudex));
//pwdt->open_count = ATOMIC_INIT(0);//初始化为0
//分配主设备号,注册字符驱动
if (led_major) {
dev_id = MKDEV(led_major, 0);
retval = register_chrdev_region(dev_id, MAX_LED_NB,
LED_NAME);
} else {
retval = alloc_chrdev_region(&dev_id, 0, MAX_LED_NB,
LED_NAME);
led_major = MAJOR(dev_id);
}
if(retval<0)
{
printk("led: register error!\n");
goto error_malloc;
}
else
printk("led:led_major:%d\n",led_major);
//绑定操作方法at91_led_fops
cdev_init(&(pled->led_dev), &at91_led_fops);
retval = cdev_add(&(pled->led_dev), dev_id,MAX_LED_NB);
if(retval<0)
{
printk("led:cdev_add failed!!!\n");
goto error_reg;
}
//创建类
pled->led_class = class_create(