kernel gpio userspace 使用

kernel gpio userspace sysfs

  • kernel userspace gpio的sysfs的接口在/sys/class/gpio目录下,该目录下存在gpiochip915,915是kernel生成的虚拟gpio chip base,这个虚拟的base的由来出自gpiochip的注册过程,从ARCH_NR_GPIO递减找到违背其他gpio chip占用的ngpio大小的空间,1024 - ngpio = 915
int gpiochip_add_data(struct gpio_chip *chip, void *data)
{
/*
	 * TODO: this allocates a Linux GPIO number base in the global
	 * GPIO numberspace for this chip. In the long run we want to
	 * get *rid* of this numberspace and use only descriptors, but
	 * it may be a pipe dream. It will not happen before we get rid
	 * of the sysfs interface anyways.
	 */
	if (base < 0) {
		base = gpiochip_find_base(chip->ngpio);
}
/* dynamic allocation of GPIOs, e.g. on a hotplugged device */
static int gpiochip_find_base(int ngpio)
{
	int base = ARCH_NR_GPIOS - ngpio;
	list_for_each_entry_reverse(gdev, &gpio_devices, list) {
		/* found a free space? */
		if (gdev->base + gdev->ngpio <= base)
			break;
		else
			/* nope, check the space right before the chip */
			base = gdev->base - ngpio;
	}

	if (gpio_is_valid(base)) {
		pr_debug("%s: found new base at %d\n", __func__, base);
		return base;
	}
}
  • 在使用时需要 gpio_num + chip_base ,使用方法(以enable gpio78中断为例 中断的类型有rising falling both):
  echo 984 > /sys/class/gpio/export
  echo in > /sys/class/gpio993/direction
  cat /sys/class/gpio984/value  
  echo falling > /sys/class/gpio993/edge  

poll gpiofd 配合gpio 中断monitor gpio状态

  • gpio 默认input high,falling中断触发事件的userspace 示例代码
	fds[0].fd=fd_down;
	//(/sys/class/gpio/gpio993/value)
    fds[0].events=POLLPRI;
    while(1)
    {    
        if(poll(fds,2,0)==-1)
        {
            printf("poll failed!\n");
            continue;
        }
        if(fds[0].revents&POLLPRI)
        { //读取gpio状态
        }
    }
  • poll 函数提供非忙轮询的阻塞的获取IO流的方法,poll最终会用到kernel的系统调用poll,do_sys_poll会先把userspace 需要监听的ufds copy到kernel space并构建成一个链表poll_list. poll_initwait初始化等待队列,
  • 其中poll_initwait init_poll_funcptr(&pwq->pt, __pollwait) 会初始化pt->_qproc
    的值为__pollwait, __pollwait函数会最终让该进程休眠再当前的waitqueue
static int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds,
		struct timespec64 *end_time)
{
	struct poll_wqueues table;
	long stack_pps[POLL_STACK_ALLOC/sizeof(long)];
	struct poll_list *const head = (struct poll_list *)stack_pps;
 	struct poll_list *walk = head;

	len = min_t(unsigned int, nfds, N_STACK_PPS);
	//从userspace copy ufds结构体并组成一个链表
	for (;;) {
		walk->next = NULL;
		walk->len = len;
		if (!len)
			break;
		if (copy_from_user(walk->entries, ufds + nfds-todo,
					sizeof(struct pollfd) * walk->len))
			goto out_fds;

		todo -= walk->len;
		if (!todo)
			break;

		len = min(todo, POLLFD_PER_PAGE);
		size = sizeof(struct poll_list) + sizeof(struct pollfd) * len;
		walk = walk->next = kmalloc(size, GFP_KERNEL);
		if (!walk) {
			goto out_fds;
		}
	}
	poll_initwait(&table);
	fdcount = do_poll(head, &table, end_time);
}
  • do_sys_poll 做好poll_list 和 poll_wqueues 的准备后调用do_poll,遍历poll_list中的每一个fd,并调用fd的fileopration中的poll函数
static int do_poll(struct poll_list *list, struct poll_wqueues *wait,
		   struct timespec64 *end_time)
{
	poll_table* pt = &wait->pt;
	ktime_t expire, *to = NULL;
	int timed_out = 0, count = 0;
	u64 slack = 0;
	unsigned int busy_flag = net_busy_loop_on() ? POLL_BUSY_LOOP : 0;
	unsigned long busy_start = 0;

	/* Optimise the no-wait case */
	if (end_time && !end_time->tv_sec && !end_time->tv_nsec) {
		pt->_qproc = NULL;
		timed_out = 1;
	}

	if (end_time && !timed_out)
		slack = select_estimate_accuracy(end_time);

	for (;;) {
		struct poll_list *walk;
		bool can_busy_loop = false;

		for (walk = list; walk != NULL; walk = walk->next) {
			struct pollfd * pfd, * pfd_end;

			pfd = walk->entries;
			pfd_end = pfd + walk->len;
			for (; pfd != pfd_end; pfd++) {
			//遍历每一个fd
				/*
				 * Fish for events. If we found one, record it
				 * and kill poll_table->_qproc, so we don't
				 * needlessly register any other waiters after
				 * this. They'll get immediately deregistered
				 * when we break out and return.
				 */
				if (do_pollfd(pfd, pt, &can_busy_loop,
					      busy_flag)) {
					count++;
					pt->_qproc = NULL;
					/* found something, stop busy polling */
					busy_flag = 0;
					can_busy_loop = false;
				}
			}
		}
		/*
		 * All waiters have already been registered, so don't provide
		 * a poll_table->_qproc to them on the next loop iteration.
		 */
		pt->_qproc = NULL;
		if (!count) {
			count = wait->error;
			if (signal_pending(current))
				count = -EINTR;
		}
		if (count || timed_out)
			break;

		/* only if found POLL_BUSY_LOOP sockets && not out of time */
		if (can_busy_loop && !need_resched()) {
			if (!busy_start) {
				busy_start = busy_loop_current_time();
				continue;
			}
			if (!busy_loop_timeout(busy_start))
				continue;
		}
		busy_flag = 0;

		/*
		 * If this is the first loop and we have a timeout
		 * given, then we convert to ktime_t and set the to
		 * pointer to the expiry value.
		 */
		if (end_time && !to) {
			expire = timespec64_to_ktime(*end_time);
			to = &expire;
		}

		if (!poll_schedule_timeout(wait, TASK_INTERRUPTIBLE, to, slack))
			timed_out = 1;
	}
	return count;
}
  • f.file->f_op->poll(f.file, pwait);实际调用的gpio 驱动中实现的file_oprations 中的poll回调
/*
 * Fish for pollable events on the pollfd->fd file descriptor. We're only
 * interested in events matching the pollfd->events mask, and the result
 * matching that mask is both recorded in pollfd->revents and returned. The
 * pwait poll_table will be used by the fd-provided poll handler for waiting,
 * if pwait->_qproc is non-NULL.
 */
static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait,
				     bool *can_busy_poll,
				     unsigned int busy_flag)
{
	unsigned int mask;
	int fd;

	mask = 0;
	fd = pollfd->fd;
	if (fd >= 0) {
		struct fd f = fdget(fd);
		mask = POLLNVAL;
		if (f.file) {
			mask = DEFAULT_POLLMASK;
			if (f.file->f_op->poll) {
				pwait->_key = pollfd->events|POLLERR|POLLHUP;
				pwait->_key |= busy_flag;
				mask = f.file->f_op->poll(f.file, pwait);
				if (mask & busy_flag)
					*can_busy_poll = true;
			}
			/* Mask out unneeded events. */
			mask &= pollfd->events | POLLERR | POLLHUP;
			fdput(f);
		}
	}
	pollfd->revents = mask;

	return mask;
}
  • gpio lib中的poll回调实现,注册gpio的irq,在irq_thread_handle中有调用wake_up 唤醒
static const struct file_operations lineevent_fileops = {
	.release = lineevent_release,
	.read = lineevent_read,
	.poll = lineevent_poll,
	.owner = THIS_MODULE,
	.llseek = noop_llseek,
	.......
	}
static irqreturn_t lineevent_irq_thread(int irq, void *p)
{
	struct lineevent_state *le = p;
	struct gpioevent_data ge;
	level = gpiod_get_value_cansleep(le->desc);
	.......
	if (ret != 0)
		wake_up_poll(&le->wait, EPOLLIN);

	return IRQ_HANDLED;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

shenhuxi_yu

感谢投币,继续输出

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

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

打赏作者

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

抵扣说明:

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

余额充值