目录
1. 结构体ft260_device
2. hid设备的初始化
3. ft260初始化
上一节中有讲到ft260_driver的结构体,其中成员函数probe就是这个probe函数。
static struct hid_driver ft260_driver = {
.name = "ft260",
.id_table = ft260_devices,
.probe = ft260_probe,
.remove = ft260_remove,
.raw_event = ft260_raw_event,
};
当设备插入系统,系统通过id_table判断设备是否符合当前驱动,符合则调用probe函数。
static const struct hid_device_id ft260_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_FUTURE_TECHNOLOGY,
USB_DEVICE_ID_FT260) },
{ /* END OF LIST */ }
};
这里对应FT260的VID和PID。
1. 结构体ft260_device
这个结构体用于表示本设备,这是内核驱动常规的写法。在probe函数里面会动态分配一个该结构的指针空间。
struct ft260_device *dev;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
kzalloc
函数用于分配内存,sizeof(*dev)
获取dev
类型的大小,GFP_KERNEL
表示在内核模式下分配内存。
该指针会存储到hdev的私有数据区域,同时该指针的私有数据区域hdev也保存HID的设备结构体hdev。
hid_set_drvdata(hdev, dev);
dev->hdev = hdev;
ft260_probe的参数struct hid_device *hdev会贯穿所有的api函数,所以在其他函数这个参数就可以实现各个函数之间参数的传递。
2. hid设备的初始化
先判断hid设备是不是USB设备。
if (!hid_is_usb(hdev))
return -EINVAL;
接着解析hid设备的报告描述符,将解析的结果保存在hdev指向的数据结构中。
ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "failed to parse HID\n");
goto hid_fail;
}
然后启动hid设备的硬件
ret = hid_hw_start(hdev, 0);
if (ret) {
hid_err(hdev, "failed to start HID HW\n");
goto hid_fail;
}
在Linux源码里面查看这个函数:
/**
* hid_hw_start - start underlying HW
* @hdev: hid device
* @connect_mask: which outputs to connect, see HID_CONNECT_*
*
* Call this in probe function *after* hid_parse. This will setup HW
* buffers and start the device (if not defeirred to device open).
* hid_hw_stop must be called if this was successful.
*/
int hid_hw_start(struct hid_device *hdev, unsigned int connect_mask)
connect_mask为0则表示没有链接需求。
最后是打开该hid设备
ret = hid_hw_open(hdev);
if (ret) {
hid_err(hdev, "failed to open HID HW\n");
goto err_hid_stop;
}
打开成功后就能够读写这个设备了。
3. ft260初始化
先通过ft260_hid_feature_report_get获取芯片版本号,需要查看FT260的AN_394手册4.4.1 CHip Version了解细节
ret = ft260_hid_feature_report_get(hdev, FT260_CHIP_VERSION,
(u8 *)&version, sizeof(version));
if (ret < 0) {
hid_err(hdev, "failed to retrieve chip version\n");
goto err_hid_close;
}
hid_info(hdev, "chip code: %02x%02x %02x%02x\n",
version.chip_code[0], version.chip_code[1],
version.chip_code[2], version.chip_code[3]);
再通过ft260_get_interface_type获取到当前设备的接口类型,
static int ft260_get_interface_type(struct ft260_device *dev,
struct ft260_get_system_status_report *cfg)
这个属于FT260内部定义的命令,需要查看FT260的AN_394手册4.4.2 Get System Status了解细节,其实就是读入26个字节
struct ft260_get_system_status_report {
u8 report; /* FT260_SYSTEM_SETTINGS */
u8 chip_mode; /* DCNF0 and DCNF1 status, bits 0-1 */
u8 clock_ctl; /* 0 - 12MHz, 1 - 24MHz, 2 - 48MHz */
u8 suspend_status; /* 0 - not suspended, 1 - suspended */
u8 pwren_status; /* 0 - FT260 is not ready, 1 - ready */
u8 i2c_enable; /* 0 - disabled, 1 - enabled */
u8 uart_mode; /* 0 - OFF; 1 - RTS_CTS, 2 - DTR_DSR, */
/* 3 - XON_XOFF, 4 - No flow control */
u8 hid_over_i2c_en; /* 0 - disabled, 1 - enabled */
u8 gpio2_func; /* 0 - GPIO, 1 - SUSPOUT, */
/* 2 - PWREN, 4 - TX_LED */
u8 gpioa_func; /* 0 - GPIO, 3 - TX_ACTIVE, 4 - TX_LED */
u8 gpiog_func; /* 0 - GPIO, 2 - PWREN, */
/* 5 - RX_LED, 6 - BCD_DET */
u8 suspend_out_pol; /* 0 - active-high, 1 - active-low */
u8 enable_wakeup_int; /* 0 - disabled, 1 - enabled */
u8 intr_cond; /* Interrupt trigger conditions */
u8 power_saving_en; /* 0 - disabled, 1 - enabled */
u8 reserved[10];
} __packed;
ret = ft260_get_system_config(hdev, cfg);
if (ret < 0)
return ret;
由于FT260可以硬件上配置为3种模式:仅UART、仅I2C和两者都有,所以需要通过chip_mode判断当前接口是哪种模式
switch (cfg->chip_mode) {
case FT260_MODE_ALL:
case FT260_MODE_BOTH:
if (dev->iface_id == 1)
ret = FT260_IFACE_UART;
else
ret = FT260_IFACE_I2C;
break;
case FT260_MODE_UART:
ret = FT260_IFACE_UART;
break;
case FT260_MODE_I2C:
ret = FT260_IFACE_I2C;
break;
}
当两者都有的模式时,FT260在系统里面会有2个接口,一个是UART,另一个则为I2C。
最后就是根据模式选择不同的probe。
if (ret == FT260_IFACE_I2C)
ret = ft260_i2c_probe(dev, &cfg);
else
ret = ft260_uart_probe(dev, &cfg);
if (ret)
goto err_hid_close;