编写Linux驱动: USB接口光谱仪驱动的开发
最编程
2024-01-08 11:21:06
...
/*容纳所有设备特定内容的结构 */
struct usb_spectrometer
{
struct usb_device *udev; /* 此设备的USB设备 */
struct usb_interface *interface; /*该设备的接口*/
struct usb_anchor submitted; /* 万一需要撤回提交*/
struct urb *bulk_in_urb; /*用urb读取数据*/
unsigned char *bulk_in_buffer; /* 接收数据的缓冲区 */
size_t bulk_in_size; /*接收缓冲区的大小 */
size_t bulk_in_filled; /* 缓冲区中的字节数 */
size_t bulk_in_copied; /* 已经复制到用户空间 */
__u8 bulk_in_endpointAddr; /* 端点中的批量地址 */
__u8 bulk_out_endpointAddr; /*批量输出端点的地址 */
int errors; /* 最后一个请求被取消 */
bool ongoing_read; /* 读正在进行*/
bool processed_urb; /* 表示尚未处理 */
};
static struct usb_spectrometer *tiny4412_usb_dev=NULL;
/*
[ 25.845000] usb 1-2.2: new high-speed USB device number 6 using s5p-ehci
[ 25.950000] usb 1-2.2: New USB device found, idVendor=0661, idProduct=294b
[ 25.950000] usb 1-2.2: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[ 25.950000] usb 1-2.2: Product: EZ-USB
[ 25.950000] usb 1-2.2: Manufacturer: Cypress
[ 726.360000] usb 1-2.2: new high-speed USB device number 7 using s5p-ehci
[ 726.475000] usb 1-2.2: config 1 interface 0 altsetting 0 has 7 endpoint descriptors, different from the interface descriptor's value: 5
[ 726.480000] usb 1-2.2: New USB device found, idVendor=148f, idProduct=5370
[ 726.480000] usb 1-2.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 726.480000] usb 1-2.2: Product: 802.11 n WLAN
[ 726.480000] usb 1-2.2: Manufacturer: Ralink
[ 726.485000] usb 1-2.2: SerialNumber: 1.0
*/
//定义USB的IDTAB
static const struct usb_device_id tiny4412_usb_id[] =
{
{USB_DEVICE(0x0661,0x294b)},
{}
};
/*
MODULE_DEVICE_TABLE 有两个功能。
一是:将设备加入到外设队列中,
二是告诉程序阅读者该设备是热插拔设备或是说该设备支持热插拔功能。
该宏定义在<linux/module.h>下
这个宏有两个参数,第一个参数设备名,第二个参数该设备加入到模块中时对应产生的设备搜索符号,这个宏生成了一个名为__mod_pci_device_table
局部变量,这个变量指向第二个参数
*/
MODULE_DEVICE_TABLE (usb,tiny4412_usb_id);
static int usb_dev_open(struct inode *inode, struct file *file)
{
printk("open:USB光谱仪设备.\n");
printk("命令结构大小_drv:%d\n",sizeof(struct DEV_CMD));
return 0;
}
//读写命令
static long usb_dev_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long argv)
{
//转换指针类型
struct DEV_CMD *buff=(struct DEV_CMD *)argv;
struct DEV_CMD dev_cmd;
int ret=0;
int actual_length=0;
int err=0;
int n;
printk("读写:USB光谱仪设备.\n");
int read_len=0;
//拷贝应用层的数据到本地
if(copy_from_user(&dev_cmd,buff,sizeof(struct DEV_CMD)))
{
printk("copy_from_user error1...\n");
return -2;
}
//拷贝应用层的数据到本地
if(copy_from_user(tiny4412_usb_dev->bulk_in_buffer,dev_cmd.buff,dev_cmd.write_len))
{
printk("copy_from_user error2...\n");
return -2;
}
//命令符合要求
if(IOCTL_CMD_RW==cmd)
{
/*同步提交写请求*/
ret=usb_bulk_msg(tiny4412_usb_dev->udev,
usb_sndbulkpipe(tiny4412_usb_dev->udev,tiny4412_usb_dev->bulk_out_endpointAddr),
tiny4412_usb_dev->bulk_in_buffer,dev_cmd.write_len,&actual_length,HZ*100);
if(ret<0)
{
printk("写字节:%d Byte,端点:%#x\n",dev_cmd.write_len,tiny4412_usb_dev->bulk_out_endpointAddr);
for(n=0;n<dev_cmd.write_len;n++)
{
printk("%#x ",tiny4412_usb_dev->bulk_in_buffer[n]);
}
printk("\n");
printk("同步提交写请求错误.错误值:%d\n",ret);
return -3;
}
dev_cmd.write_len=actual_length; //成功写入的长度
printk("write:len=%d\n",actual_length);
//读取的长度大于0.就表示需要读取数据
if(dev_cmd.read_len>0)
{
//判断是否读取大数据
if(dev_cmd.buff[3]==0x2f&&dev_cmd.buff[7]==0x02)
{
printk("0x86端点:\n");
for(n=0;n<2;n++)
{
/*同步提交读请求:端点0x86*/
err=usb_bulk_msg(tiny4412_usb_dev->udev,
usb_rcvbulkpipe(tiny4412_usb_dev->udev,tiny4412_usb_dev->bulk_in_endpointAddr),
tiny4412_usb_dev->bulk_in_buffer,tiny4412_usb_dev->bulk_in_size, &actual_length,HZ*500);
if(err<0)
{
printk("0x86_读取第%d次,状态值:%d\n",n,err);
break;
}
memcpy(dev_cmd.buff+read_len,tiny4412_usb_dev->bulk_in_buffer,actual_length);
read_len+=actual_length;
}
printk("0x86_一共读取%d次,一共%d字节.\n",n,read_len);
printk("0x88端点:\n");
for(n=0;n<9;n++)
{
/*同步提交读请求:端点0x88*/
err=usb_bulk_msg(tiny4412_usb_dev->udev,
usb_rcvbulkpipe(tiny4412_usb_dev->udev,0x88),
tiny4412_usb_dev->bulk_in_buffer,tiny4412_usb_dev->bulk_in_size, &actual_length,HZ*500);
if(err<0)
{
printk("0x88_读取第%d次,状态值:%d\n",n,err);
break;
}
memcpy(dev_cmd.buff+read_len,tiny4412_usb_dev->bulk_in_buffer,actual_length);
read_len+=actual_length;
}
printk("0x88_一共读取%d次,一共%d字节.\n",n,read_len);
/*同步提交读请求:端点0x86*/
err=usb_bulk_msg(tiny4412_usb_dev->udev,
usb_rcvbulkpipe(tiny4412_usb_dev->udev,tiny4412_usb_dev->bulk_in_endpointAddr),
tiny4412_usb_dev->bulk_in_buffer,tiny4412_usb_dev->bulk_in_size, &actual_length,HZ*500);
if(err<0)
{
printk("0x86_读取最后一次,状态值:%d\n",err);
}
printk("0x86_最后读取%d字节:\n",actual_length);
memcpy(dev_cmd.buff+read_len,tiny4412_usb_dev->bulk_in_buffer,actual_length);
read_len+=actual_length;
}
else
{
//buff清0
memset(dev_cmd.buff,0,sizeof(dev_cmd.buff));
/*同步提交读请求*/
ret = usb_bulk_msg(tiny4412_usb_dev->udev,
usb_rcvbulkpipe(tiny4412_usb_dev->udev,tiny4412_usb_dev->bulk_in_endpointAddr),
tiny4412_usb_dev->bulk_in_buffer,tiny4412_usb_dev->bulk_in_size, &actual_length,HZ*500);
if(ret<0)
{
printk("同步提交读请求错误.错误值:%d\n",ret);
return -4;
}
//实际读取的长度
memcpy(dev_cmd.buff,tiny4412_usb_dev->bulk_in_buffer,actual_length);
read_len=actual_length;
}
dev_cmd.read_len=read_len;
printk("read:len=%d\n",dev_cmd.read_len);
}
}
//将数据拷贝到应用层
err=copy_to_user(buff,&dev_cmd,sizeof(struct DEV_CMD));
if(err)
{
printk("data copy-->user error!!!\n");
return -3;
}
return 0;
}
static int usb_dev_release(struct inode *inode, struct file *file)
{
printk("release:USB光谱仪设备.\n");
return 0;
}
static const struct file_operations tiny4412_usb_dev_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl=usb_dev_unlocked_ioctl,
.open = usb_dev_open,
.release = usb_dev_release,
};
static struct miscdevice usb_dev_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEV_NAME,
.fops = &tiny4412_usb_dev_fops,
};
//USB设备信息与驱动端匹配成功的时候调用。
static int test_probe(struct usb_interface *interface,const struct usb_device_id *id) //资源探索函数
{
int i=0;
size_t buffer_size;
struct usb_device *dev_info;
unsigned char *bcdUSB_p;
struct usb_host_interface *host_inter;
struct usb_endpoint_descriptor *endpoint;
int size;
tiny4412_usb_dev=kzalloc(sizeof(*tiny4412_usb_dev), GFP_KERNEL);
tiny4412_usb_dev->udev = usb_get_dev(interface_to_usbdev(interface));
tiny4412_usb_dev->interface = interface;
printk("识别到USB光谱仪设备,正在进行设备初始化.\n");
/*通过接口获取设备信息*/
dev_info = interface_to_usbdev(interface);
bcdUSB_p=(unsigned char *)&dev_info->descriptor.bcdUSB;
printk("设备与描述表兼容的USB设备说明版本号=0x%x%x\n", bcd2bin(*bcdUSB_p),bcd2bin(*(bcdUSB_p+1))); //从USB设备描述符中获取USB版本
printk("厂商ID = %#x\n",dev_info->descriptor.idVendor); //从USB设备描述符中获取厂商ID
printk("设备ID = %#x\n",dev_info->descriptor.idProduct);//从USB设备描述符中获取产品ID
printk("设备类 = %#x\n",interface->cur_altsetting->desc.bInterfaceClass); //从USB设备获取设备类
printk("设备从类 = %#x\n",interface->cur_altsetting->desc.bInterfaceSubClass);//从USB设备获取设备从类
printk("设备协议 = %#x\n",interface->cur_altsetting->desc.bInterfaceProtocol);//从USB设备获取设备协议
/*获取当前接口设置*/
host_inter=interface->cur_altsetting;
/*获取端点描述符*/
for(i=0;i<host_inter->desc.bNumEndpoints;i++)
{
endpoint = &host_inter->endpoint[i].desc;
printk("端点号[%d]:%#x\n",i,endpoint->bEndpointAddress&0xFF);
if(endpoint->bEndpointAddress&1<<7)
{
printk("端点[%d] 输入端点(设备到主机)\n",i);
}
else
{
printk("端点[%d] 输出端点(主机到设备)\n",i);
}
switch(endpoint->bmAttributes)
{
case 0:printk("端点[%d] 设备支持控制传输.\n",i);break;
case 1:printk("端点[%d] 设备支持同步传输.\n",i);break;
case 2:printk("端点[%d] 设备支持批量传输.\n",i);break;
case 3:printk("端点[%d] 设备支持中断传输.\n",i);break;
}
/*从端点描述符中获取传输的数据大小 */
size = usb_endpoint_maxp(endpoint);
printk("端点[%d] 传输的数据大小:%d\n",i,size);
//输入端点
if(!tiny4412_usb_dev->bulk_in_endpointAddr &&usb_endpoint_is_bulk_in(endpoint))
{
/* 批量输入端点 */
buffer_size = usb_endpoint_maxp(endpoint);
tiny4412_usb_dev->bulk_in_size = buffer_size;
tiny4412_usb_dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
printk("probe:tiny4412_usb_dev->bulk_in_size=%d\n",tiny4412_usb_dev->bulk_in_size);
printk("probe:tiny4412_usb_dev->bulk_in_endpointAddr=%#X\n",tiny4412_usb_dev->bulk_in_endpointAddr);
tiny4412_usb_dev->bulk_in_buffer = kmalloc(buffer_size,GFP_KERNEL);
if(!tiny4412_usb_dev->bulk_in_buffer)
{
printk("无法分配bulk_in_buffer");
break;
}
tiny4412_usb_dev->bulk_in_urb = usb_alloc_urb(0,GFP_KERNEL);
if(!tiny4412_usb_dev->bulk_in_urb)
{
printk("无法分配bulk_in_urb");
break;
}
}
//输出端点
if(!tiny4412_usb_dev->bulk_out_endpointAddr &&usb_endpoint_is_bulk_out(endpoint))
{
/* 批量输出端点 */
tiny4412_usb_dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
printk("probe:tiny4412_usb_dev->bulk_out_endpointAddr=%#x\n",tiny4412_usb_dev->bulk_out_endpointAddr);
}
}
/*向内核注册一个杂项字符设备*/
if(misc_register(&usb_dev_miscdev)==0)
{
printk("USB光谱仪设备节点注册成功:/tiny4412_usb_dev/%s ,主设备号:10,次设备号:%d\n",
usb_dev_miscdev.name,usb_dev_miscdev.minor);
}
printk("版本:2021/01/12 12:10");
return 0;
}
//USB断开的时候调用
static void test_disconnect(struct usb_interface *intf)
{
/*从内核注销一个杂项字符设备*/
misc_deregister(&usb_dev_miscdev);
printk("USB光谱仪设备已断开.\n");
}
//定义USB驱动结构体
static struct usb_driver tiny4412_usb_driver = {
.name = "spectrometer_usb_drv",
.id_table = tiny4412_usb_id,
.probe = test_probe,
.disconnect = test_disconnect
};
static int __init tiny4412_usb_init(void)
{
printk("正在安装USB光谱仪驱动.\n");
//注册USB设备驱动
usb_register(&tiny4412_usb_driver);
return 0;
}
static void __exit tiny4412_usb_exit(void)
{
//注销USB设备驱动
usb_deregister(&tiny4412_usb_driver);
printk("USB光谱仪驱动卸载成功.\n");
}
module_init(tiny4412_usb_init);
module_exit(tiny4412_usb_exit);
MODULE_AUTHOR("xiaolong");
MODULE_LICENSE("GPL");
下一篇: USB端口数据监控工具
推荐阅读
-
USB 闪存驱动器在 Linux 系统上是只读的,但不是只写的。
-
Linux 驱动程序开发--编写 W25Q64(Flash)驱动程序
-
Linux USB 驱动程序开发(三)--编写 USB 驱动程序
-
Linux 驱动程序开发(使用 I2C 总线设备驱动程序模型编写 AT24C02 驱动程序)
-
Linux设备驱动开发详解——学习笔记-设备驱动来联系。在没有操作系统的情况下,工程师可以根据硬件设备的特点自行定义接口。而在有操作系统的情况下,驱动的架构则由相应的操作系统来定义。驱动存在的意义就是给上层应用提供便利。 驱动针对的对象是存储器和外设。Linux将存储器和外设分为 3 个基础大类:字符设备、块设备、网络设备。 字符设备和块设备都被 Linux 映射到文件系统的文件和目录中,通过文件系统的接口(open、read、write、close等)来访问。其中,块设备可以通过类似 dd 命令对应的原始块设备来访问,也可以通过建立文件系统,以文件路径来访问。 学习 Linux 设备驱动,要求非常好的硬件基础、非常好的软件基础、一定的 Linux 内核基础和非常好的多任务并发控制和同步的基础。学习 Linux 设备驱动要将学习的函数、数据结构等放到整体架构中去理解,才能理清驱动中各组成部分之间的关系。 驱动设计的硬件基础 驱动工程师需要掌握 处理器、存储器、接口和总线、可编程门电路、原理图、硬件时序、芯片手册、仪器使用 等方面的内容。 处理器
-
Linux驱动开发-编写W25Q64(Flash)驱动
-
编写Linux驱动: USB接口光谱仪驱动的开发
-
Linux驱动开发: 使用usbmon抓取usb 总线上通信的数据
-
学习使用usbmon工具捕获usb总线通信数据的方法-二、Linux驱动开发中的usbmon应用
-
Linux驱动开发: 使用usbmon抓取usb 总线上通信的数据