}
IO控制函数
static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
struct gpio_dev *dev = filp->private_data;
struct gpio_qset *qset;
//cmd == 2 设计成为释放arg端口的功能,之后申请内存的任务便不必执行了,所以直接返回
if(cmd == 2)
{
gpio_free(arg);
return arg;
}
dev->size++;
if(dev->dptr == NULL)
{
dev->dptr = (struct gpio_qset *)kzalloc(sizeof(struct gpio_qset), GFP_KERNEL);
qset = dev->dptr;
}
else
{
for(qset = dev->dptr; qset->next != NULL; qset = qset->next); //找到链表最后一项
qset->next = (struct gpio_qset *)kzalloc(sizeof(struct gpio_qset), GFP_KERNEL);
qset = qset->next;
}
/*链表数据*/
qset->num = dev->size; //确定自己的编号
qset->ddr = cmd; //确定方向
qset->port = arg; //确定端口号
qset->next = NULL; //最后一项地址清空
//printk(KERN_ERR "qset->num=%d,qset->ddr=%d,qset->port=%d\n", qset->num, qset->ddr, qset->port);
sprintf(qset->label, "gpio%ld", qset->port); //确定申请的GPIO使用名称(和端口直接相关)
ret = gpio_request(qset->port, qset->label); //申请端口
/*由于gpio_requset会自己判断成功与否并且退出函数,故注释掉对ret的判断
if(ret < 0)
printk(KERN_ERR "%s_requset failled!%d \n", qset->label, ret);
*/
/*判断GPIO工作方向(输出或输出)*/
switch(qset->ddr)
{
case 0: ret = gpio_direction_input(qset->port);
if(ret < 0) printk(KERN_ERR "gpio_direction_input failled!\n");
break;
case 1: ret = gpio_direction_output(qset->port, 1);
if(ret < 0) printk(KERN_ERR "gpio_direction_output failled!\n");
break;
default:
return -EPERM; /* Operation not permitted */
}
return qset->num;
}
分析:功能:申请新的gpio_qset的内存,确定相应的GPIO端口功能
* cmd:实现的功能,
* 0:读,
* 1:写,
* 2:释放GPIO端口
* arg:执行操作的GPIO端口
* 描述:用户空间调用ioctl时运行
* long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
* 返回值:成功返回操作的GPIO端口号
* 错误返回相应的错误码
应用层使用实例:ioctl(fd, 1, GPIO_TO_PIN(1,22));
ret = gpio_request(qset->port, qset->label);
功能:申请端口
参数:qset->port 对应申请的哪个管脚,这个对应GPIO_TO_PIN(1,22) ,
此引脚含义是GPIO1_22 对应1*32+22=64引脚号
qset->label 此变量为管脚围棋取的名字。
ret = gpio_direction_input(qset->port);
功能:设定IO口方向 ,参数对应GPIO_TO_PIN(1,22) ,
之所以这步是因为只有用户态数据能传给应用程序,才能被应用
IO写操作函数
static ssize_t gpio_write (struct file *filp, const char __user *writeBuf, size_t port, loff_t *offp)
{
long ret;
struct gpio_dev *dev = filp->private_data;
struct gpio_qset *qset;
if(dev->dptr == NULL)
return -ENODEV; /* No such device */
for(qset = dev->dptr; qset != NULL; qset = qset->next)
{
// printk(KERN_ERR "qset->port=%d,port=%d\n", qset->port, port);
if(qset->port == port)
break;
if(qset->next == NULL)
return -ENODEV; /* No such device */
}
if(qset->ddr != 1) //判断是否ioctl设置为写操作
return -EPERM; /* Operation not permitted */
ret = copy_from_user(&qset->value, writeBuf, 1);
//printk(KERN_ERR "write:%d\n", qset->value);
switch(qset->value)
{
case '0': qset->value = 0;
gpio_set_value(qset->port, 0);
break;
default : qset->value = 1;
gpio_set_value(qset->port, 1);
break;
}
return qset->value;
}
此函数用于对IO口赋值
ret = copy_from_user(&qset->value, writeBuf, 1);
功能:将应用程序的参数转为内核态参数,以便下面的对IO口赋值
gpio_set_value(qset->port, 0);
功能:设置IO口port端口的值为0
三、 应用程序测试
完整测试程序如下:
#include
#include
#include
#include
#include
#include
#include
#include
#define GPIO_TO_PIN(bank, gpio) (32 * (bank) + (gpio))
int main(int argc, char * argv)
{
int i, n, fd;
char num;
int ret;
fd = open("/dev/gpioCtl", O_RDWR); //打开设备
if (fd < 0)
{
printf("can't open /dev/gpioCtl!\n");
exit(1);
}
sleep(1);
ioctl(fd, 1, GPIO_TO_PIN(1,22)); //设置gpio1-22为输出(user:led3)
ioctl(fd, 0, GPIO_TO_PIN(2, 1)); //设置gpio2-1 为输入(p8-18)
while (1)
{
num = 1;
ret = write(fd,"1",GPIO_TO_PIN(1,22));
printf("on");
if(ret < 0)
{
perror("write");
return -1;
}
sleep(1);
ret = write(fd,"0",GPIO_TO_PIN(1,22));
printf("off");
if(ret < 0)
{
perror("write");
return -1;
}
sleep(1);
}
}
分析:
程序定义了GPIO1_22为输出,GPIO2_1为输入,通过write函数实现数据写入,即对IO口赋值,通过read函数实现获取此时相应GPIO端口的状态(0或1)。
注:
GPIO驱动加在成功后,会有/dev/gpioCtl这个设备文件产生,驱动是作为一个文件在系统在存在的,如若对设备进行操作,首先打开设备文件,之后进行相应读写等操作
GPIO端口与GPIO号是有对应关系的,在此程序中,例如标注GPIO2_1端口对应的设备号为:2*38+1,
GPIO端口作为中断口时也要将端口号转换为相应的中断号