在SC60上打算实现usb device和otg的软切换,即通过软件设置实现usb device和otg的切换。
原理图上可以设计一个GPIO来控制USB ID和数据线的切换。VBUS的供电方面,otg使用外供电,可以不用考虑;作device时vbus用来检测插入即可。
参考原理图如下:
图中使用GPIO_96作为控制切换的管脚,它输出0时,USB_ID脚为高,s脚为高,usb切换开关选择的是HSD1:USB_DP_EXT和USB_DM_EXT,此时模块作为device;
GPIO_96输出1时,USB_ID脚为低,usb切换开关选择的是HSD2:USB_DP_COM和USB_DM_COM,此时模块作为host,外部连接otg设备。
参考驱动:
#include <linux/device.h>
#include <asm/io.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/semaphore.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#define SC60_GPIO_SET_MAJOR 198
#define SC60_DO1 0x1
#define SC60_DO2 0x2
u32 gpio_num[]={
96,
};
struct SC60_gpio_dev{
struct cdev cdev;
};
struct SC60_gpio_dev *SC60_gpio_devp;
static int SC60_gpio_major = SC60_GPIO_SET_MAJOR;
int SC60_gpio_open(struct inode *inode, struct file *filp)
{
//printk(KERN_ERR "###sc60### SC60_gpio open in\n");
filp->private_data = SC60_gpio_devp;
return 0;
}
int SC60_gpio_release(struct inode *inode, struct file *filp)
{
//printk(KERN_ERR "###sc60### SC60_gpio release in\n");
return 0;
}
static ssize_t SC60_gpio_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
__attribute__((__unused__)) struct SC60_gpio_dev *dev = filp->private_data;
//copy_to_user
//printk(KERN_ERR "###sc60### SC60_gpio read in\n");
return 0;
}
static ssize_t SC60_gpio_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
__attribute__((__unused__)) struct SC60_gpio_dev *dev = filp->private_data;
//copy_from_user
//printk(KERN_ERR "###sc60### SC60_gpio write in\n");
return -1;
}
static loff_t SC60_gpio_llseek(struct file *filp, loff_t offset, int orig)
{
//printk(KERN_ERR "###sc60### SC60_gpio llseek in\n");
//filp->f_pos
switch(orig){
case 0:
break;
case 1:
break;
default:
break;
}
return 0;
}
static long SC60_gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
__attribute__((__unused__)) struct SC60_gpio_dev *dev = filp->private_data;
//printk(KERN_ERR "###sc60### SC60_gpio ioctl in\n");
switch(cmd){
case SC60_DO1:
break;
case SC60_DO2:
default:
break;
}
return 0;
}
static const struct file_operations SC60_gpio_fops = {
.owner = THIS_MODULE,
.llseek = SC60_gpio_llseek,
.read = SC60_gpio_read,
.write = SC60_gpio_write,
.unlocked_ioctl = SC60_gpio_ioctl,
.open = SC60_gpio_open,
.release = SC60_gpio_release,
};
struct class *SC60_gpio_class;
static void SC60_gpio_setup_cdev(struct SC60_gpio_dev *dev, int index)
{
int err, devno;
devno = MKDEV(SC60_gpio_major, index);
printk(KERN_ERR "###sc60### SC60_gpio SC60_gpio_setup_cdev\n");
cdev_init(&dev->cdev, &SC60_gpio_fops);
dev->cdev.owner = THIS_MODULE;
err = cdev_add(&dev->cdev, devno, 1);
if(err)
{
printk(KERN_ERR "###sc60### %d add SC60_gpio %d\n", err, index);
}
SC60_gpio_class = class_create(THIS_MODULE, "sc60_otg_set");
//stone comment
//这里的名字是/dev/下面的名字
// create device in /dev/sc60_otg_set
//实际在/sys/class/sc60_otg_set
// but the real device is /sys/bus/platform/devices/SC60_otg_control
device_create(SC60_gpio_class, NULL, devno, NULL, "sc60_otg_set");
}
static bool gpio_flag = 0;
static ssize_t SC60_gpio_get(struct device *dev, struct device_attribute *attr, char *buf)
{
int ret = 0;
if(gpio_flag)
//print chars to buf
//ret = snprintf(buf, PAGE_SIZE, "%d\n", SC60_adc1_to_vol());
ret = snprintf(buf, PAGE_SIZE, "--otg set--\n");
else
ret = snprintf(buf, PAGE_SIZE, "--host set--\n");
return ret;
}
static ssize_t SC60_gpio_set(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int stat;
//char to int
stat = buf[0] - '0';
printk(KERN_ERR "set gpio status :%d \n", stat);
gpio_free(gpio_num[0]);
gpio_request(gpio_num[0], NULL);
if(stat == 0)
{
gpio_direction_output(gpio_num[0], 0);
gpio_flag = stat;
}
if(stat == 1)
{
gpio_direction_output(gpio_num[0], 1);
gpio_flag = stat;
}
return count;
}
static DEVICE_ATTR(SC60_gpio, 0664, SC60_gpio_get, SC60_gpio_set);
static int __init SC60_gpio_probe(struct platform_device *pdev)
{
int result;
dev_t devno = MKDEV(SC60_gpio_major, 0);
printk(KERN_ERR "###sc60### SC60_gpio_probe in\n");
if(SC60_gpio_major)
{
//这里是分配设备号
//cat proc/devices 看到198 SC60_gpio_control
result = register_chrdev_region(devno, 1, "SC60_gpio_control");
}
else
{
result = alloc_chrdev_region(&devno, 0, 1, "SC60_gpio_control");
SC60_gpio_major = MAJOR(devno);
}
if(result < 0)
{
return result;
}
SC60_gpio_devp = kmalloc(sizeof(struct SC60_gpio_dev), GFP_KERNEL);
if(!SC60_gpio_devp){
result = -ENOMEM;
goto fail_malloc;
}
memset(SC60_gpio_devp, 0, sizeof(struct SC60_gpio_dev));
SC60_gpio_setup_cdev(SC60_gpio_devp, 0);
//这里创建的节点是在/sys/bus/platform/devices/SC60_otg_control下
//SC60_otg_control这个名字是由SC60_gpio_device和SC60_gpio_device_driver的名字决定的
device_create_file(&pdev->dev, &dev_attr_sc60_gpio);
printk(KERN_ERR "###sc60### SC60_gpio_probe out\n");
return 0;
fail_malloc:
unregister_chrdev_region(devno, 1);
return result;
}
static int __exit SC60_gpio_remove(struct platform_device *pdev)
{
cdev_del(&SC60_gpio_devp->cdev);
kfree(SC60_gpio_devp);
device_destroy(SC60_gpio_class, MKDEV(SC60_gpio_major, 0));
class_destroy(SC60_gpio_class);
unregister_chrdev_region(MKDEV(SC60_gpio_major, 0), 1);
return 0;
}
static struct platform_driver SC60_gpio_device_driver = {
.probe = SC60_gpio_probe,
.remove = SC60_gpio_remove,
.driver = {
.name = "SC60_otg_control",
.owner = THIS_MODULE,
},
};
static struct platform_device SC60_gpio_device = {
.name = "SC60_otg_control",
.id = -1,
};
static int __init SC60_gpio_init(void)
{
platform_device_register(&SC60_gpio_device);
return platform_driver_register(&SC60_gpio_device_driver);
}
static void __exit SC60_codec_exit(void)
{
platform_driver_unregister(&SC60_gpio_device_driver);
platform_device_unregister(&SC60_gpio_device);
}
module_init(SC60_gpio_init);
module_exit(SC60_codec_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("stone");
MODULE_DESCRIPTION("stone for SC60 gpio-set CTRL");
这样就可以使用节点控制usb状态了。
读取otg状态:
cat /sys/bus/platform/devices/sc60_otg_control/sc60_gpio
设置otg状态:
echo 1 > /sys/bus/platform/devices/sc60_otg_control/sc60_gpio
设置USB状态:
echo 0 > /sys/bus/platform/devices/sc60_otg_control/sc60_gpio