Linux驱动开发---字符设备驱动

字符设备驱动(Linux kernel 4.9.x)

按键驱动

  1. 数据结构
    1.1 设备结构体:缓存键值的buf、缓存按键状态、等待队列、cdev结构体
    1.2 定时器:软件延时(防抖)
    1.3 按键硬件信息、键值结构体:记录每个按键对应的中断、GPIO、键值
    1.4 文件操作结构体:打开、释放、读。

  2. 流程
    2.1 确认按键的流程
    (中断)第一次有按键按下中断,屏蔽中断,延时,再次检测看是否仍是按下状态,若是,则确认按下。否则认为是抖动。
    2.2 定时器处理流程
    上述确认按键流程最后,再次查询按键状态是否仍是按下,若是,则将键值录入缓冲区、唤醒等待队列。同时可以更新并启动新的定时器延迟(可以是一个去抖时间更长的一个时间),此后每次定时器到期后,查询按键是否仍是按下状态,如果是,则重新启用新的延迟。若不是,则开启对应的按键中断,等待新的按键。

input子系统

  1. 以某触摸屏驱动为例:
			struct xxxts {
				struct xxx_adc_client *client;
				struct device *dev;
				struct input_dev *input;
				struct clk *clock;
				void __iomem *io;
				unsigned long xp;
				unsigned long yp;
				int irq_tc;
				int count;
				int shift;
				int features;
			};
			static struct xxxts ts;
static int xxx_ts_probe(strcut platform_device *pdev)
			{
				...
				strcut input_dev *input_dev;
				...
				...
				input_dev = input_allocate_device();
				...
				ts.input = input_dev;
				ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
				ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
				input_set_abs_params(ts.input, ABS_X, 0, 0x3FF, 0, 0);
				input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0);
			
				ts.input->name = "XXXXX TouchScreen";
				ts.input->id.bustype = BUS_HOST;
				ts.input->id.vendor = 0xDEAD;
				ts.input->id.product = 0xBEEF;
				ts.input->id.version = 0x0102;
				
				ret = request_irq(ts.irq_tc, stylus_irq, 0, "xxx_ts_pen", ts.input);
				
				ret = input_register_device(ts.input);			
				
				return 0;
			}
  1. 相关函数
2.1 int input_allocate_device(void);					/* 给输入设备分配、初始化内存 */
2.2 int input_register_device(struct input_dev *dev);	/* 注册输入设备  */ 
2.3 void input_report_xxx(struct input_dev \*dev, unsigned int code, int value));	/* 报告发生的事件及相应的键值/坐标等状态 */
				input_report_abs(...);
				input_report_key(...);
				input_report_rel(...);
2.4 void input_sync(strcut input_dev \*dev);		 		/* 事件同步,告知事件接收者驱动已发出了一个完整报告 */
2.5 void input_unregister_device(struct input_dev *dev); 	/* 注销输入设备 */

NVRAM设备驱动: miscdevice概念

#include <linux/miscdevice.h>
		...
		...
		static const struct file_operations nvram_fops = {
			.owner		= THIS_MODULE,
			.llseek		= nvram_llseek,
			.read		= nvram_read,
			.write		= nvram_write,
			.unlocked_ioctl	= nvram_ioctl,
			.open		= nvram_open,
			.release	= nvram_release,
		};
		static struct miscdevice nvram_dev = {
			NVRAM_MINOR,
			"nvram",
			&nvram_fops
		};
		static int __init nvram_init(void)
		{
			...
			ret = misc_register(&nvram_dev);
			...
		}

小结: miscdevice共享一个主设备号MISC_MAJOR,但次设备号不同。
所有的miscdevice设备形成一个链表,对设备访问时内核根据次设备号查找对应的miscdevice设备。

看门狗(watchdog)设备驱动:platform_device/platform_driver的概念

static int __init my_wdt_probe(struct platform_device *pdev)		/* 省去了返回值判断的代码 */
		{
			struct my_wdt_device *wdev;
			struct watchdog_device *wdog;
			struct resource *res;
			void __iomem *base;
			int ret;
			u32 val;
		
			wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL);
			...
		
			res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
			base = devm_ioremap_resource(&pdev->dev, res);
			...
		
			wdev->regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base,
								&my_wdt_regmap_config);
			...
		
			wdev->clk = devm_clk_get(&pdev->dev, NULL);
			...
		
			wdog			= &wdev->wdog;
			wdog->info		= &my_wdt_info;
			wdog->ops		= &my_wdt_ops;
			wdog->min_timeout	= 1;
			wdog->max_hw_heartbeat_ms = my_WDT_MAX_TIME * 1000;
			wdog->parent		= &pdev->dev;
		
			ret = platform_get_irq(pdev, 0);
			if (ret > 0)
				if (!devm_request_irq(&pdev->dev, ret, my_wdt_isr, 0,
							dev_name(&pdev->dev), wdog))
					wdog->info = &my_wdt_pretimeout_info;
		
			ret = clk_prepare_enable(wdev->clk);
			if (ret)
				return ret;
		
			regmap_read(wdev->regmap, my_WDT_WRSR, &val);
			wdog->bootstatus = val & my_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0;
		
			wdev->ext_reset = of_property_read_bool(pdev->dev.of_node,
								"fsl,ext-reset-output");
			wdog->timeout = clamp_t(unsigned, timeout, 1, my_WDT_MAX_TIME);
			if (wdog->timeout != timeout)
				dev_warn(&pdev->dev, "Initial timeout out of range! Clamped from %u to %u\n",
					timeout, wdog->timeout);
		
			platform_set_drvdata(pdev, wdog);
			watchdog_set_drvdata(wdog, wdev);
			watchdog_set_nowayout(wdog, nowayout);
			watchdog_set_restart_priority(wdog, 128);
			watchdog_init_timeout(wdog, timeout, &pdev->dev);
		
			if (my_wdt_is_running(wdev)) {
				my_wdt_set_timeout(wdog, wdog->timeout);
				set_bit(WDOG_HW_RUNNING, &wdog->status);
			}
		
			/*
			* Disable the watchdog power down counter at boot. Otherwise the power
			* down counter will pull down the #WDOG interrupt line for one clock
			* cycle.
			*/
			regmap_write(wdev->regmap, my_WDT_WMCR, 0);
		
			ret = watchdog_register_device(wdog);
			...
		
			dev_info(&pdev->dev, "timeout %d sec (nowayout=%d)\n",
				wdog->timeout, nowayout);
		
			return 0;
		}

		/* 采用在dtsi文件里,注册平台设备 */
		static const struct of_device_id my_wdt_dt_ids[] = {
			{ .compatible = "fsl,my-wdt", },
			{ /* sentinel */ }
		};														
		MODULE_DEVICE_TABLE(of, my_wdt_dt_ids);				
		
		/* 定义平台驱动结构体 */
		static struct platform_driver my_wdt_driver = {
			.remove		= __exit_p(my_wdt_remove),
			.shutdown	= my_wdt_shutdown,
			.driver		= {
				.name	= my-wdt,
				.pm     = &my_wdt_pm_ops,	
				.of_match_table = my_wdt_dt_ids,
			},
		};																
		module_platform_driver_probe(my_wdt_driver, my_wdt_probe);		/* 注册平台驱动及绑定porbe */

附:dtsi文件部分相关内容

		wdog1: wdog@30280000 {
				compatible = "fsl,my-wdt";
				reg = <0 0x30280000 0 0x10000>;
				interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>;
				...
				status = "disabled";
		};

小结: 结合dtsi文件来实现platform driver和platform device的匹配
首先,my_wdt_dt_ids[].compatible与dtsi文件中的compatible的字符串要完全一样;
其次,平台驱动.driver.of_match_table指向my_wdt_dt_ids.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值