天天看點

OK6410 usb驅動分析

ok6410usb 驅動架構分析

目前為止,寫驅動嘛,雖然我隻是菜鳥,但還是直到一些常識,故我從常識開始分析,

一,USB控制器,也就時主機測,看待裝置,是一個裝置 -》配置 -》接口 -》端點的順序來配置usb并和usb裝置通信的

二,在usb裝置内部運作着固件程式,來對usb控制器發送過來的資訊進行解包,并執行相應的操作(不确定這句話的正确性,但覺得是對的)

三,在usb裝置内部存儲着裝置的配置資訊,比如有多少個配置了,配置有多少個接口,接口有多少個端點,還有一些辨別符(廠商号,用的什麼協定之類的用來表明我是個什麼子的裝置,我應該和哪個驅動比對又或者加載什麼樣的驅動)正是通過了這些usb裝置内部存儲的資訊,我們才能配置usb驅動

越來越感覺,想實作一個驅動,即使是我這樣,先湊出來的也一定要對協定和原理了解,這倆周真的是舉步維艱

下面分析usb-skeleton驅動

static const struct file_operations skel_fops = { //usb的操作
	.owner =	THIS_MODULE,
	.read =		skel_read,
	.write =	skel_write,
	.open =		skel_open,
	.release =	skel_release,
	.flush =	skel_flush,
	.llseek =	noop_llseek,
};

/*
 * usb class driver info in order to get a minor number from the usb core,
 * and to have the device registered with the driver core
 */
static struct usb_class_driver skel_class = { //初始化usb_class_diver 用于配置usb的操作
	.name =		"skel%d",
	.fops =		&skel_fops,
	.minor_base =	USB_SKEL_MINOR_BASE,
};

static int skel_probe(struct usb_interface *interface,
		      const struct usb_device_id *id) //在hub探測到新裝置添加後,并探測到其對應我們的id_table後,運作probe,其實從這裡也可以間接的分析出,裝置内部自己存儲了自己由幾個配置,幾個接口,幾個端點,不然也不會傳過來usb_interface啊
{
	struct usb_skel *dev;//自定義的usb資料 每種特定的usb裝置都不一樣
	struct usb_host_interface *iface_desc; //我們要從傳過來的usb_interface,選出我們自己的設定
//簡而言之, 也接口中有多個設定,每個設定中的端點并不同,我們從中選出我們需要的設定,然後再擷取端點
	struct usb_endpoint_descriptor *endpoint;//端點
	size_t buffer_size;
	int i;
	int retval = -ENOMEM;

	/* allocate memory for our device state and initialize it */
	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev) {
		err("Out of memory");
		goto error;
	}
	kref_init(&dev->kref);//初始化引用基數
	sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);//信号量
	mutex_init(&dev->io_mutex);//互斥
	spin_lock_init(&dev->err_lock);//鎖
	init_usb_anchor(&dev->submitted);
	init_completion(&dev->bulk_in_completion);

	dev->udev = usb_get_dev(interface_to_usbdev(interface));
	dev->interface = interface;

	/* set up the endpoint information */
	/* use only the first bulk-in and bulk-out endpoints */
	iface_desc = interface->cur_altsetting;//擷取目前的設定 為什麼擷取的目前的設定就是正确的阿?不是由多個設定阿?
	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {//擷取所有的端點
		endpoint = &iface_desc->endpoint[i].desc;

		if (!dev->bulk_in_endpointAddr &&
		    usb_endpoint_is_bulk_in(endpoint)) { //是否有端點位址和是否時輸入端點
			/* we found a bulk in endpoint */
			buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
			dev->bulk_in_size = buffer_size;
			dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
			dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
			if (!dev->bulk_in_buffer) {
				err("Could not allocate bulk_in_buffer");
				goto error;
			}
			dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL); //配置設定urb
			if (!dev->bulk_in_urb) {
				err("Could not allocate bulk_in_urb");
				goto error;
			}
		}

		if (!dev->bulk_out_endpointAddr &&
		    usb_endpoint_is_bulk_out(endpoint)) {//是否位輸出端點
			/* we found a bulk out endpoint */
			dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
		}
	}
	if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
		err("Could not find both bulk-in and bulk-out endpoints");
		goto error;
	}

	/* save our data pointer in this interface device */
	usb_set_intfdata(interface, dev);//連結我們的自定義資料和接口

	/* we can register the device now, as it is ready */
	retval = usb_register_dev(interface, &skel_class);//注冊usb裝置
	if (retval) {
		/* something prevented us from registering this driver */
		err("Not able to get a minor for this device.");
		usb_set_intfdata(interface, NULL);
		goto error;
	}

	/* let the user know what node this device is now attached to */
	dev_info(&interface->dev,
		 "USB Skeleton device now attached to USBSkel-%d",
		 interface->minor);
	return 0;

error:
	if (dev)
		/* this frees allocated memory */
		kref_put(&dev->kref, skel_delete);
	return retval;
}

static void skel_disconnect(struct usb_interface *interface)//在斷開usb時做的操作
{
	struct usb_skel *dev;
	int minor = interface->minor; //擷取裝置号

	dev = usb_get_intfdata(interface);//通過interface擷取dev(自定義資料,我們會在probe使interface與我們自定義的資料相關聯)
	usb_set_intfdata(interface, NULL);//取消接口與自定義資料的關聯

	/* give back our minor */
	usb_deregister_dev(interface, &skel_class); //反注冊接口(特麼解除安裝另一個同義詞是什麼,忘了)

	/* prevent more I/O from starting */
	mutex_lock(&dev->io_mutex);
	dev->interface = NULL; //取消dev與接口的關聯
	mutex_unlock(&dev->io_mutex);

	usb_kill_anchored_urbs(&dev->submitted);//關閉所有的urb

	/* decrement our usage count */
	kref_put(&dev->kref, skel_delete);//減小引用計數

	dev_info(&interface->dev, "USB Skeleton #%d now disconnected", minor);
}

static void skel_draw_down(struct usb_skel *dev) //等待所有urb完成,若逾時, 則強制取消
{
	int time;

	time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000);
	if (!time)
		usb_kill_anchored_urbs(&dev->submitted);
	usb_kill_urb(dev->bulk_in_urb);
}

static int skel_suspend(struct usb_interface *intf, pm_message_t message)//挂起操作
{
	struct usb_skel *dev = usb_get_intfdata(intf);

	if (!dev)
		return 0;
	skel_draw_down(dev);
	return 0;
}

static int skel_resume(struct usb_interface *intf)
{
	return 0;
}

static int skel_pre_reset(struct usb_interface *intf) //相當于加鎖把,在解鎖(即複位結束前,不允許其他程序操作)
{
	struct usb_skel *dev = usb_get_intfdata(intf);

	mutex_lock(&dev->io_mutex);
	skel_draw_down(dev);

	return 0;
}

static int skel_post_reset(struct usb_interface *intf)
{
	struct usb_skel *dev = usb_get_intfdata(intf);

	/* we are sure no URBs are active - no locking needed */
	dev->errors = -EPIPE;
	mutex_unlock(&dev->io_mutex);

	return 0;
}

static struct usb_driver skel_driver = {  //usb_driver結構體, 
	.name =		"skeleton",
	.probe =	skel_probe,
	.disconnect =	skel_disconnect,
	.suspend =	skel_suspend,
	.resume =	skel_resume,
	.pre_reset =	skel_pre_reset, //衆所周知, 當usb裝置第一次被探測到時, usb裝置會被reset, pre_reset會被usb_reset_device調用,直到post_reset被調用,即複位結束
	.post_reset =	skel_post_reset,
	.id_table =	skel_table,
	.supports_autosuspend = 1,
};

static int __init usb_skel_init(void) //驅動加載, 加__init的作用應該是當執行完一次後,就會把這個函數從記憶體中删掉
{
	int result;

	/* register this driver with the USB subsystem */
	result = usb_register(&skel_driver);	//注冊usb驅動, 根絕id_table進行比對, 在hub裡會識别裝置并添加新裝置
	if (result)
		err("usb_register failed. Error number %d", result);

	return result;
}

static void __exit usb_skel_exit(void)//驅動寫在, __exit的作用和__init的作用類似
{
	/* deregister this driver with the USB subsystem */
	usb_deregister(&skel_driver);
}

module_init(usb_skel_init); //驅動加載, 沒什麼好說的
module_exit(usb_skel_exit); //驅動解除安裝

MODULE_LICENSE("GPL");
           

一, 不保證其正确性

二, 還有一些疑問

    一個usb接口有多個設定,我如何确定我的驅動在probe裡收到interface後怎麼直到我需要哪個設定呢?

   一個接口有多個端點,如果有多個輸入端點,我在擷取usb資訊時,應該用哪個端點呢?

接下來分析usb控制器驅動。。。