嵌入式安卓系统开发实践笔记一

需求

Ubuntu 18.04 基于VM虚拟机。参考suspendmonitor,实现USB插拔监控驱动+APP用户应用程序。

  • 参考 suspend_monitor,实现USB 拔插监控驱动+应用程序 
  • 编写一个kernel module, 注册usb callback, 用于监控USB设备的插拔情况
  • 提供 /proc/usb_monitor 设备节点,提供读取接口,功能开关接口。
  •  Userspace 应用程序,读取/proc/usb_monitor设备节点数据,以RingBuffer形式存储插拔数据 (最多512条),数据格式自定义,但必须包括:USB设备名称+插入时间+拔离时间。

 思路

  • 注册usb的插拔的回调函数,设置notifier_block结构体并初始化此结构体,设定.notifier_call函数地址,通过usb_register_notify,注册函数,并编写回调函数信息。
// USB设备的通知回调函数
static int usb_notify(struct notifier_block* self, unsigned long action, void* dev) {
	struct usb_device* usb_dev = (struct usb_device*)dev;
	int index;
	mutex_lock(&monitor->usb_monitor_mutex); // 互斥锁
	
	if (monitor->enable_usb_monitor == 0) {
		LOGE("%s:usb monitor is disable\n", TAG);
		mutex_unlock(&monitor->usb_monitor_mutex);
		return 0;
	}

	switch (action) {
		// 状态判断 
	case USB_DEVICE_ADD:
		// USB设备插入时进行添加
		index = write_in_message(1, usb_dev);//拿到index
		LOGI("### The add device name is %s %d\n", monitor->message[index].name,
			monitor->usb_message_count);
		printk(KERN_INFO "### The add device name is %s %d\n", monitor->message[index].name,
			monitor->usb_message_count);
		wake_up_interruptible(&monitor->usb_monitor_queue); 	// 唤醒中断,
		break;

	case USB_DEVICE_REMOVE:
		index = write_in_message(0, usb_dev);
		LOGI("### The remove device name is %s %d\n", monitor->message[index].name,
			monitor->usb_message_count);
		printk(KERN_INFO "### The remove device name is %s %d\n", monitor->message[index].name,
			monitor->usb_message_count);
		wake_up_interruptible(&monitor->usb_monitor_queue);
		break;
	}
	mutex_unlock(&monitor->usb_monitor_mutex);//解锁
	return NOTIFY_OK;
}
static struct notifier_block usb_nb = {
	.notifier_call = usb_notify, // 设定USB通知回调函数 
};
  •  通过字符设备驱动方法,让用户和驱动程序交互。使用proc_create的方法,创建/proc/usb_monitor文件。为实现交互同时创建read和write函数。主要参考suspend_monitor中的部分。到此,最终实现也只要在用户程序中读取/proc/usb_monitor设备节点即可,并通过驱动提供的接口,控制驱动的开关。
static int init_module_(void) {

	monitor = kzalloc(sizeof(struct usb_monitor_t), GFP_KERNEL);/*初始化申请内存空间*/

	if (!monitor) {
		LOGE("%s:failed to kzalloc\n", TAG);
		return -ENOMEM;
	}
	// 初始化变量
	monitor->usb_message_count = 0;
	monitor->usb_message_index_read = 0;
	monitor->usb_message_index_write = 0;
	monitor->enable_usb_monitor = 1;
	monitor->flag = "init_module_ is inited !\n";

	proc_create("usb_monitor", 0644, NULL, &usb_monitor_fops); /*创建proc/usb_monitor*/
	init_waitqueue_head(&monitor->usb_monitor_queue); /*初始化等待队列*/
	mutex_init(&monitor->usb_monitor_mutex);/*初始化互斥锁*/
	printk(KERN_INFO "Init USB hook.\n");
	usb_register_notify(&usb_nb);/*注册回调函数*/
	return 0;
}
  • 功能开关接口的具体实现
static long usb_monitor_ioctl(struct file* filp, unsigned int cmd,
	unsigned long arg)
{
	void __user* ubuf = (void __user*)arg;
	unsigned char status;

	LOGI("%s:%s\n", TAG, __func__);

	mutex_lock(&monitor->usb_monitor_mutex);

	switch (cmd) {
	case CMD_GET_STATUS: /*返回当前驱动的开关状态*/
		LOGI("%s:ioctl:get enable status\n", TAG);
		if (monitor->enable_usb_monitor == 0)
			status = 0x00;
		else
			status = 0xff;

		LOGI("%s:ioctl:status=0x%x\n", TAG, status);
		/*拷贝数据*/
		if (copy_to_user(ubuf, &status, sizeof(status))) { 
			LOGE("%s:ioctl:copy_to_user fail\n", TAG);
			mutex_unlock(&monitor->usb_monitor_mutex);
			return -EFAULT;
		}
		break;
	default:
		LOGE("%s:invalid cmd\n", TAG);
		mutex_unlock(&monitor->usb_monitor_mutex);
		return -ENOTTY;
	}

	mutex_unlock(&monitor->usb_monitor_mutex);

	return 0;
}
static ssize_t usb_monitor_write(struct file* filp, const char __user* buf,
	size_t size, loff_t* ppos) //接收用户程序的命令
{
	char end_flag = 0x0a, cmd;

	LOGI("%s:%s\n", TAG, __func__);

	/*仅支持size=2 */
	if (size != 2) {
		LOGE("%s:invalid cmd size: size = %d\n", TAG, (int)size);
		return -EINVAL;
	}
	/*拷贝用户态数据*/
	if (copy_from_user(monitor->write_buff, buf, size)) {
		LOGE("%s:copy_from_user error!\n", TAG);
		return -EFAULT;
	}

	if (monitor->write_buff[1] != end_flag) {
		LOGE("%s:invalid cmd: end_flag != 0x0a\n", TAG);
		return -EINVAL;
	}

	cmd = monitor->write_buff[0];

	mutex_lock(&monitor->usb_monitor_mutex);

	switch (cmd) { //控制驱动开关状态
	case '0':
		monitor->enable_usb_monitor = 0;
		LOGI("%s:disable usb monitor\n", TAG);
		break;
	case '1':
		monitor->enable_usb_monitor = 1;
		LOGI("%s:enable usb monitor\n", TAG);
		break;
	default:
		LOGE("%s:invalid cmd: cmd = %d\n", TAG, cmd);
		mutex_unlock(&monitor->usb_monitor_mutex);
		return -EINVAL;
	}

	mutex_unlock(&monitor->usb_monitor_mutex);

	return size;
}
  • Userspace 应用程序,读取/proc/usb_monitor设备节点数据,以RingBuffer形式存储插拔数据 (最多512条),数据格式自定义,但必须包括:USB设备名称+插入时间+拔离时间。
static void* DoUsbMonitor(void* arg) {
    int ret;
    ssize_t lenth = 0, i = 0;
    struct epoll_event epev;
    UsbInfo deviceinfo;
    UsbMonitorDevice* device = (UsbMonitorDevice*)arg;
    char* buf = device->getBuffer();
    device->FifoReset(BUFFER_SIZE);

    while (1) {
        printf("usb_monitor epoll_wait... \n");
        ret = epoll_wait(device->getepollfd(), &epev, MAX_EPOLL_EVENTS, -1); //epoll等待
        if (ret == -1 && errno != EINTR) {
            printf("usb_monitor epoll_wait failed; errno=%d\n", errno);
            return (void*)-1;
        }
        lenth = read(device->getFd(), buf, KERNEL_DATA_LENG);
        if (lenth > 0) {
            printf("read length is %d\n", lenth);
            //8 字节的 kernel time
            for (i = 0; i < 8; i++) {
                data_info.kernel_time[i] = buf[i];
                printf("kernel_time[%d] = 0x%x \n", i, data_info.kernel_time[i]);
            }
            ret = pthread_mutex_lock(&data_mutex); //get lock 互斥锁
            if (ret != 0) {
                printf("Error on pthread_mutex_lock(), ret = %d\n", ret);
                return (void*)-1;
            }
            // 记录插拔状态
            data_info.status = buf[8];
            // 记录USB名称
            memcpy(data_info.name, buf + 9, lenth - 9);

            ret = pthread_mutex_unlock(&data_mutex); //unlock 解锁互斥锁
            if (ret != 0) {
                printf("Error on pthread_mutex_unlock(), ret = %d\n", ret);
                return (void*)-1;
            }

            device->AppendDatainfo(deviceinfo); //保存数据到ringbuffer
            fifo_size = device->GetFifoSize();

            if (data_info.status == 1) {  //根据插拔状态打印信息
                printf("USB %s -> plug In \n", data_info.name);
            }
            else {
                printf("USB %s -> plug Out \n", data_info.name);
            }
            printf("\n");
            if (isEmpty) {
                for (i = 0; i < isEmpty; i++)
                    pthread_cond_signal(&fifo_nonzero);
            }
            printf("Current BufferSize = %ld \n", fifo_size);

        }
    }

}

买了专栏的用户可以私信我要源码 thanks. 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浮云依旧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值