usb觸摸屏驅動

源碼在/drivers/input/touchscreen/usbtouchscreen.c中
static int __init usbtouch_init(void)	//入口函數
{
	return usb_register(&usbtouch_driver);	//註冊usb觸摸屏驅動
}
module_init(usbtouch_init);

看usbtouch_driver的定義

static struct usb_driver usbtouch_driver = {
	.name		= "usbtouchscreen",
	.probe		= usbtouch_probe,	//usb觸摸屏探測到
	.disconnect	= usbtouch_disconnect,
	.suspend	= usbtouch_suspend,
	.resume		= usbtouch_resume,
	.reset_resume	= usbtouch_reset_resume,
	.id_table	= usbtouch_devices,
	.supports_autosuspend = 1,
};

當有設備匹配的時候會調用probe方法,也就是usbtouch_probe

在static const struct usb_device_id usbtouch_devices[]中定義了的usb設備插入就會匹配並觸發probe

可以用宏USB_DEVICE簡化設置usb設備id信息,如下:

{USB_DEVICE(0x3823, 0x0001), .driver_info = DEVTYPE_EGALAX},

driver_info是驅動類型,有一下選擇

enum {
	DEVTYPE_IGNORE = -1,
	DEVTYPE_EGALAX,
	DEVTYPE_PANJIT,
	DEVTYPE_3M,
	DEVTYPE_ITM,
	DEVTYPE_ETURBO,
	DEVTYPE_GUNZE,
	DEVTYPE_DMC_TSC10,
	DEVTYPE_IRTOUCH,
	DEVTYPE_IDEALTEK,
	DEVTYPE_GENERAL_TOUCH,
	DEVTYPE_GOTOP,
	DEVTYPE_JASTEC,
	DEVTYPE_E2I,
	DEVTYPE_ZYTRONIC,
	DEVTYPE_TC45USB,
	DEVTYPE_NEXIO,
};

沒有選擇也可以自己添加一個在枚舉體後面
(0x3823,0x0001)這兩個分別是usb設備的廠商id和產品id
下面代碼是我插拔usb觸摸屏的打印信息

usb 1-1.1: new full speed USB device using musb-hdrc and address 9
usb 1-1.1: New USB device found, idVendor=0408, idProduct=3001
usb 1-1.1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
usb 1-1.1: Product: HCTouch    
usb 1-1.1: Manufacturer: HC
input: HC HCTouch     as /devices/platform/omap/ti81xx-usbss/musb-hdrc.0/usb1/1-1/1-1.1/1-1.1:1.0/input/input6
input: HC HCTouch     as /devices/platform/omap/ti81xx-usbss/musb-hdrc.0/usb1/1-1/1-1.1/1-1.1:1.1/input/input7

作爲我的設備,我就把idVendor=0408, idProduct=3001添加進USB_DEVICE宏就行

{USB_DEVICE(0x0408, 0x3001), .driver_info = DEVTYPE_HCTOUCH},

OK!插上設備就會匹配的

input: HC HCTouch     as /devices/platform/omap/ti81xx-usbss/musb-hdrc.0/usb1/1-1/1-1.1/1-1.1:1.0/input/input6
input: HC HCTouch     as /devices/platform/omap/ti81xx-usbss/musb-hdrc.0/usb1/1-1/1-1.1/1-1.1:1.1/input/input7

這個就是匹配後的打印信息

接着就是probe方法了

static int usbtouch_probe(struct usb_interface *intf,const struct usb_device_id *id)
{
	struct usbtouch_usb *usbtouch;
	struct input_dev *input_dev;
	struct usb_endpoint_descriptor *endpoint;
	struct usb_device *udev = interface_to_usbdev(intf);
	struct usbtouch_device_info *type;
	int err = -ENOMEM;

	/* some devices are ignored */
	if (id->driver_info == DEVTYPE_IGNORE)	//忽略的設備類型
		return -ENODEV;

	endpoint = usbtouch_get_input_endpoint(intf->cur_altsetting);	//獲取端點描述符數組指針
	if (!endpoint)
		return -ENXIO;

	usbtouch = kzalloc(sizeof(struct usbtouch_usb), GFP_KERNEL);	//分配usbtouch_usb結構體對象內存
	input_dev = input_allocate_device();	//分配輸入設備對象內存
	if (!usbtouch || !input_dev)	//分配不成功退出
		goto out_free;

	type = &usbtouch_dev_info[id->driver_info];	//根據id的driver_info信息獲取全局usbtouch_dev_info數組項
	usbtouch->type = type;	//指定usbtouch_dev_info
	if (!type->process_pkt)	//若usbtouch_dev_info不存在process_pkt方法
		type->process_pkt = usbtouch_process_pkt;	//則默認設置爲usbtouch_process_pkt

	usbtouch->data = usb_alloc_coherent(udev, type->rept_size,GFP_KERNEL, &usbtouch->data_dma);	//分配緩衝區
	if (!usbtouch->data)
		goto out_free;

	if (type->get_pkt_len) {	//若usbtouch_dev_info存在get_pkt_len方法
		usbtouch->buffer = kmalloc(type->rept_size, GFP_KERNEL);	//則要根據rept_size分配usb_touch_usb對象緩衝區
		if (!usbtouch->buffer)
			goto out_free_buffers;
	}

	usbtouch->irq = usb_alloc_urb(0, GFP_KERNEL);	//分配urb
	if (!usbtouch->irq) {
		dbg("%s - usb_alloc_urb failed: usbtouch->irq", __func__);
		goto out_free_buffers;
	}

	usbtouch->interface = intf;	//設置usb_touch_usb的usb接口
	usbtouch->input = input_dev;//捆綁usb_touch_usb和輸入設備

	if (udev->manufacturer)	//存在工廠名則設置工廠名
		strlcpy(usbtouch->name, udev->manufacturer, sizeof(usbtouch->name));

	if (udev->product) {	//存在產品名則設置產品名
		if (udev->manufacturer)
			strlcat(usbtouch->name, " ", sizeof(usbtouch->name));
		strlcat(usbtouch->name, udev->product, sizeof(usbtouch->name));
	}

	if (!strlen(usbtouch->name))	//若不存在工廠名和產品名
		snprintf(usbtouch->name, sizeof(usbtouch->name),
			"USB Touchscreen %04x:%04x",
			 le16_to_cpu(udev->descriptor.idVendor),
			 le16_to_cpu(udev->descriptor.idProduct));

	usb_make_path(udev, usbtouch->phys, sizeof(usbtouch->phys));	//設置usb設備路徑
	strlcat(usbtouch->phys, "/input0", sizeof(usbtouch->phys));

	input_dev->name = usbtouch->name;	//設置輸入設備名
	input_dev->phys = usbtouch->phys;	//設置輸入設備路徑
	usb_to_input_id(udev, &input_dev->id);
	input_dev->dev.parent = &intf->dev;	//設置usb設備爲輸入設備的父設備

	input_set_drvdata(input_dev, usbtouch);

	input_dev->open = usbtouch_open;	//設置輸入設備的open方法
	input_dev->close = usbtouch_close;	//設置輸入設備的close方法

	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);	//按鍵和絕對位移事件
	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);	//觸摸按鍵
	input_set_abs_params(input_dev, ABS_X, type->min_xc, type->max_xc, 0, 0);	//絕對x座標位移
	input_set_abs_params(input_dev, ABS_Y, type->min_yc, type->max_yc, 0, 0);	//絕對y座標位移
	if (type->max_press)
		input_set_abs_params(input_dev, ABS_PRESSURE, type->min_press,type->max_press, 0, 0);

	if (usb_endpoint_type(endpoint) == USB_ENDPOINT_XFER_INT)	//中斷傳輸方式
		usb_fill_int_urb(usbtouch->irq, udev,
			 usb_rcvintpipe(udev, endpoint->bEndpointAddress),
			 usbtouch->data, type->rept_size,
			 usbtouch_irq, usbtouch, endpoint->bInterval);
	else	//bulk傳輸方式
		usb_fill_bulk_urb(usbtouch->irq, udev,
			 usb_rcvbulkpipe(udev, endpoint->bEndpointAddress),
			 usbtouch->data, type->rept_size,
			 usbtouch_irq, usbtouch);

	usbtouch->irq->dev = udev;	//urb和usb設備捆綁
	usbtouch->irq->transfer_dma = usbtouch->data_dma;	//傳輸數據dma地址緩衝區
	usbtouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;	//傳輸標誌物dma映射傳輸

	/* device specific allocations */
	if (type->alloc) {	//usbtouch_dev_info對象存在alloc方法
		err = type->alloc(usbtouch);	//則調用該方法
		if (err) {
			dbg("%s - type->alloc() failed, err: %d", __func__, err);
			goto out_free_urb;
		}
	}

	/* device specific initialisation*/
	if (type->init) {	//usbtouch_dev_info對象存在初始化方法
		err = type->init(usbtouch);	//則調用該初始化方法
		if (err) {
			dbg("%s - type->init() failed, err: %d", __func__, err);
			goto out_do_exit;
		}
	}

	err = input_register_device(usbtouch->input);	//註冊輸入設備
	if (err) {
		dbg("%s - input_register_device failed, err: %d", __func__, err);
		goto out_do_exit;
	}

	usb_set_intfdata(intf, usbtouch);

	if (usbtouch->type->irq_always) {	//usbtouch_dev_info對象存在irq_always方法
		/* this can't fail */
		usb_autopm_get_interface(intf);	//電源喚醒
		err = usb_submit_urb(usbtouch->irq, GFP_KERNEL);	//提交urb
		if (err) {
			usb_autopm_put_interface(intf);	//電源掛起
			err("%s - usb_submit_urb failed with result: %d",
			    __func__, err);
			goto out_unregister_input;
		}
	}

	return 0;

out_unregister_input:
	input_unregister_device(input_dev);
	input_dev = NULL;
out_do_exit:
	if (type->exit)
		type->exit(usbtouch);
out_free_urb:
	usb_free_urb(usbtouch->irq);
out_free_buffers:
	usbtouch_free_buffers(udev, usbtouch);
out_free:
	input_free_device(input_dev);
	kfree(usbtouch);
	return err;
}

錯中複雜的關係不用管,關鍵是

1.type = &usbtouch_dev_info[id->driver_info]; //根據id的driver_info信息獲取全局usbtouch_dev_info數組項

2.if (!type->process_pkt) //若usbtouch_dev_info不存在process_pkt方法
  type->process_pkt = usbtouch_process_pkt; //則默認設置爲usbtouch_process_pkt

3.申請的urb的回調函數是usbtouch_irq
4.if (type->init) { //usbtouch_dev_info對象存在初始化方法
  err = type->init(usbtouch); //則調用該初始化方法

usbtouch_dev_info是全局usbtouch_device_info數組

static struct usbtouch_device_info usbtouch_dev_info[] = {
#ifdef CONFIG_TOUCHSCREEN_USB_EGALAX
	[DEVTYPE_EGALAX] = {
		.min_xc		= 0x0,
		.max_xc		= 0x07ff,
		.min_yc		= 0x0,
		.max_yc		= 0x07ff,
		.rept_size	= 16,
		.process_pkt	= usbtouch_process_multi,
		.get_pkt_len	= egalax_get_pkt_len,
		.read_data	= egalax_read_data,
	},
#endif

#ifdef CONFIG_TOUCHSCREEN_USB_PANJIT
	[DEVTYPE_PANJIT] = {
		.min_xc		= 0x0,
		.max_xc		= 0x0fff,
		.min_yc		= 0x0,
		.max_yc		= 0x0fff,
		.rept_size	= 8,
		.read_data	= panjit_read_data,
	},
#endif

#ifdef CONFIG_TOUCHSCREEN_USB_3M
	[DEVTYPE_3M] = {
		.min_xc		= 0x0,
		.max_xc		= 0x4000,
		.min_yc		= 0x0,
		.max_yc		= 0x4000,
		.rept_size	= 11,
		.read_data	= mtouch_read_data,
		.init		= mtouch_init,
	},
#endif

#ifdef CONFIG_TOUCHSCREEN_USB_ITM
	[DEVTYPE_ITM] = {
		.min_xc		= 0x0,
		.max_xc		= 0x0fff,
		.min_yc		= 0x0,
		.max_yc		= 0x0fff,
		.max_press	= 0xff,
		.rept_size	= 8,
		.read_data	= itm_read_data,
	},
#endif

#ifdef CONFIG_TOUCHSCREEN_USB_ETURBO
	[DEVTYPE_ETURBO] = {
		.min_xc		= 0x0,
		.max_xc		= 0x07ff,
		.min_yc		= 0x0,
		.max_yc		= 0x07ff,
		.rept_size	= 8,
		.process_pkt	= usbtouch_process_multi,
		.get_pkt_len	= eturbo_get_pkt_len,
		.read_data	= eturbo_read_data,
	},
#endif

#ifdef CONFIG_TOUCHSCREEN_USB_GUNZE
	[DEVTYPE_GUNZE] = {
		.min_xc		= 0x0,
		.max_xc		= 0x0fff,
		.min_yc		= 0x0,
		.max_yc		= 0x0fff,
		.rept_size	= 4,
		.read_data	= gunze_read_data,
	},
#endif

#ifdef CONFIG_TOUCHSCREEN_USB_DMC_TSC10
	[DEVTYPE_DMC_TSC10] = {
		.min_xc		= 0x0,
		.max_xc		= 0x03ff,
		.min_yc		= 0x0,
		.max_yc		= 0x03ff,
		.rept_size	= 5,
		.init		= dmc_tsc10_init,
		.read_data	= dmc_tsc10_read_data,
	},
#endif

#ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH
	[DEVTYPE_IRTOUCH] = {
		.min_xc		= 0x0,
		.max_xc		= 0x0fff,
		.min_yc		= 0x0,
		.max_yc		= 0x0fff,
		.rept_size	= 8,
		.read_data	= irtouch_read_data,
	},
#endif

#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK
	[DEVTYPE_IDEALTEK] = {
		.min_xc		= 0x0,
		.max_xc		= 0x0fff,
		.min_yc		= 0x0,
		.max_yc		= 0x0fff,
		.rept_size	= 8,
		.process_pkt	= usbtouch_process_multi,
		.get_pkt_len	= idealtek_get_pkt_len,
		.read_data	= idealtek_read_data,
	},
#endif

#ifdef CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH
	[DEVTYPE_GENERAL_TOUCH] = {
		.min_xc		= 0x0,
		.max_xc		= 0x7fff,
		.min_yc		= 0x0,
		.max_yc		= 0x7fff,
		.rept_size	= 7,
		.read_data	= general_touch_read_data,
	},
#endif

#ifdef CONFIG_TOUCHSCREEN_USB_GOTOP
	[DEVTYPE_GOTOP] = {
		.min_xc		= 0x0,
		.max_xc		= 0x03ff,
		.min_yc		= 0x0,
		.max_yc		= 0x03ff,
		.rept_size	= 4,
		.read_data	= gotop_read_data,
	},
#endif

#ifdef CONFIG_TOUCHSCREEN_USB_JASTEC
	[DEVTYPE_JASTEC] = {
		.min_xc		= 0x0,
		.max_xc		= 0x0fff,
		.min_yc		= 0x0,
		.max_yc		= 0x0fff,
		.rept_size	= 4,
		.read_data	= jastec_read_data,
	},
#endif

#ifdef CONFIG_TOUCHSCREEN_USB_E2I
	[DEVTYPE_E2I] = {
		.min_xc		= 0x0,
		.max_xc		= 0x7fff,
		.min_yc		= 0x0,
		.max_yc		= 0x7fff,
		.rept_size	= 6,
		.init		= e2i_init,
		.read_data	= e2i_read_data,
	},
#endif

#ifdef CONFIG_TOUCHSCREEN_USB_ZYTRONIC
	[DEVTYPE_ZYTRONIC] = {
		.min_xc		= 0x0,
		.max_xc		= 0x03ff,
		.min_yc		= 0x0,
		.max_yc		= 0x03ff,
		.rept_size	= 5,
		.read_data	= zytronic_read_data,
		.irq_always     = true,
	},
#endif

#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC45USB
	[DEVTYPE_TC45USB] = {
		.min_xc		= 0x0,
		.max_xc		= 0x0fff,
		.min_yc		= 0x0,
		.max_yc		= 0x0fff,
		.rept_size	= 5,
		.read_data	= tc45usb_read_data,
	},
#endif

#ifdef CONFIG_TOUCHSCREEN_USB_NEXIO
	[DEVTYPE_NEXIO] = {
		.rept_size	= 1024,
		.irq_always	= true,
		.read_data	= nexio_read_data,
		.alloc		= nexio_alloc,
		.init		= nexio_init,
		.exit		= nexio_exit,
	},
#endif
};

由於我DIY了一個,所以後面要添加

	[DEVTYPE_HCTOUCH] = {
		.min_xc		= 0x0,	//最小x座標
		.max_xc		= 0x7fff,	//最大x座標
		.min_yc		= 0x0,	//最小y座標
		.max_yc		= 0x7fff,	//最大y座標
		.rept_size	= 7,	//還不知道是幹嘛用的
		.read_data	= hc_touch_read_data,	//關鍵的讀數據方法
	},

當觸摸屏幕的時候,usb會通過urb傳遞數據,緊接着肯定會調用usbtouch_irq啦

static void usbtouch_irq(struct urb *urb)
{
	struct usbtouch_usb *usbtouch = urb->context;
	int retval;

	switch (urb->status) {
	case 0:	//正常流程跳出switch語句
		/* success */
		break;
	case -ETIME:
		/* this urb is timing out */
		dbg("%s - urb timed out - was the device unplugged?",
		    __func__);
		return;
	case -ECONNRESET:
	case -ENOENT:
	case -ESHUTDOWN:
	case -EPIPE:
		/* this urb is terminated, clean up */
		dbg("%s - urb shutting down with status: %d",
		    __func__, urb->status);
		return;
	default:
		dbg("%s - nonzero urb status received: %d",
		    __func__, urb->status);
		goto exit;
	}
	//執行usbtouch_device_info對象的process_pkt方法
	usbtouch->type->process_pkt(usbtouch, usbtouch->data, urb->actual_length);

exit:
	usb_mark_last_busy(interface_to_usbdev(usbtouch->interface));
	retval = usb_submit_urb(urb, GFP_ATOMIC);
	if (retval)
		err("%s - usb_submit_urb failed with result: %d",
		    __func__, retval);
}

這裏的關鍵是會調用process_pkt方法也就是默認的usbtouch_process_pkt函數

static void usbtouch_process_pkt(struct usbtouch_usb *usbtouch,unsigned char *pkt, int len)	//默認的usb觸摸數據包處理函數
{
	struct usbtouch_device_info *type = usbtouch->type;	//獲取usbtouch_device_info對象

	if (!type->read_data(usbtouch, pkt))	//調用usbtouch_device_info對象的read_data方法
			return;

	input_report_key(usbtouch->input, BTN_TOUCH, usbtouch->touch);	//上報觸摸事件

	if (swap_xy) {	//豎屏模式
		input_report_abs(usbtouch->input, ABS_X, usbtouch->y);
		input_report_abs(usbtouch->input, ABS_Y, usbtouch->x);
	} else {
		input_report_abs(usbtouch->input, ABS_X, usbtouch->x);	//上報絕對座標X事件
		input_report_abs(usbtouch->input, ABS_Y, usbtouch->y);	//上報絕對座標Y事件
	}
	if (type->max_press)
		input_report_abs(usbtouch->input, ABS_PRESSURE, usbtouch->press);
	input_sync(usbtouch->input);	//同步輸入事件
}

這個函數主要是調用了usbtouch_device_info對象的read_data方法也就是上面提到的數組的read_data方法(hc_touch_read_data)

數據讀取完畢後上報觸摸事件,絕對XY座標事件,然後同步交由輸入子系統去處理座標等具體事項

hc_touch_read_data函數讀取usb接口傳遞過來的數據,該數據就包含了座標和觸摸信息,函數主要是對這些信息做下運算處理

static int hc_touch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
{
	dev->x = (pkt[2] << 8) | pkt[1];
	dev->y = (pkt[4] << 8) | pkt[3];
	dev->press = pkt[5] & 0xff;
	dev->touch = pkt[0] & 0x01;

	return 1;
}

這些處理的細節跟具體硬件廠商或者協議有關,調試可以將dev->x和dev->y打印出來
可能你加了打印之後觸摸屏幕壓根就沒有打印信息

那是因爲要打開設備,所以應用層也要有測試軟件,有界面的測試軟件最好

沒有也可以用下面這段代碼去簡單測試一下(來着網絡)

#include <stdio.h>
#include <linux/input.h>

static int event0_fd = -1;
struct input_event ev0[64];

static int handle_event0()
{
	int button = 0, realx=0, realy=0, i, rd;
	rd = read(event0_fd, ev0, sizeof(struct input_event)* 64);
	if(rd < sizeof(struct input_event)) return 0;
	for(i=0;i<rd/sizeof(struct input_event); i++)
	{
		if(EV_ABS == ev0[i].type)
		{
			if(ev0[i].code == 0) {
				realx = ev0[i].value;
			} else if(ev0[i].code == 1) {
				realy = ev0[i].value;
			}
		}
		printf("realx:%3d; realy:%3d\n",realx,realy);
		//printf("event(%d):type:%d; code:%3d; value:%3d; realx:%3d; realy:%3d\n",i,ev0[i].type,ev0[i].code,ev0[i].value,realx,realy);
		
	}
	return 1;
}


int main(void)
{
	int done = 1;
	event0_fd = open("/dev/input/event1",02);	//打開設備
	if(event0_fd <0) {
		printf("open input device error\n");
		return -1;
	}
	while (done)
	{
		//printf("begin handle_event0...\n");
		done = handle_event0();
		//printf("end handle_event0...\n");
	}
	if(event0_fd > 0)
	{
		close(event0_fd);
		event0_fd = -1;
	}
	return 0;
}


這段代碼打開的設備修改成你的設備路徑,插拔觸摸屏判斷哪個是你觸摸屏的設備
或者ls -l  /sys/class/input看信息結合插入設備的打印信息也可以判斷你的設備是哪個

lrwxrwxrwx    1 root     root            0 Mar 23 17:20 event0 -> ../../devices/platform/omap/ti81xx-usbss/musb-hdrc.0/usb1/1-1/1-1.1/1-1.1:1.0/input/input6/event0
lrwxrwxrwx    1 root     root            0 Mar 23 17:20 event1 -> ../../devices/platform/omap/ti81xx-usbss/musb-hdrc.0/usb1/1-1/1-1.1/1-1.1:1.1/input/input7/event1
lrwxrwxrwx    1 root     root            0 Mar 23 17:20 event2 -> ../../devices/platform/omap/ti81xx-usbss/musb-hdrc.0/usb1/1-1/1-1.3/1-1.3:1.0/input/input8/event2

再或者cat /proc/bus/input/devices也可以

I: Bus=0003 Vendor=0408 Product=3001 Version=0200
N: Name="HC HCTouch    "
P: Phys=usb-musb-hdrc.0-1.1/input0
S: Sysfs=/devices/platform/omap/ti81xx-usbss/musb-hdrc.0/usb1/1-1/1-1.1/1-1.1:1.0/input/input6
U: Uniq=
H: Handlers=mouse0 event0 
B: EV=b
B: KEY=400 0 0 0 0 0 0 0 0 0 0
B: ABS=3

I: Bus=0003 Vendor=0408 Product=3001 Version=0200
N: Name="HC HCTouch    "
P: Phys=usb-musb-hdrc.0-1.1/input0
S: Sysfs=/devices/platform/omap/ti81xx-usbss/musb-hdrc.0/usb1/1-1/1-1.1/1-1.1:1.1/input/input7
U: Uniq=
H: Handlers=mouse1 event1 
B: EV=b
B: KEY=400 0 0 0 0 0 0 0 0 0 0
B: ABS=3

這裏我的設備有兩個input是因爲我是2點的屏

還有一點要補充就是關於內核編譯選項的

Device Drivers  --->Input device support  --->

[*]   Touchscreens  ---> 

<*>   USB Touchscreen Driver  這個要選擇

 

 [*] HID Devices  --->    <*>   USB Human Interface Device (full HID) support 選中



 

 

 

發佈了85 篇原創文章 · 獲贊 12 · 訪問量 35萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章