xref: /freebsd/sys/dev/usb/template/usb_template_mtp.c (revision 23ab08715cab2828477cf8f75d25787ea283e771)
1d2b99310SHans Petter Selasky /* $FreeBSD$ */
202ac6454SAndrew Thompson /*-
35d38a4d4SEd Schouten  * Copyright (c) 2008 Hans Petter Selasky <hselasky@FreeBSD.org>
402ac6454SAndrew Thompson  * All rights reserved.
502ac6454SAndrew Thompson  *
602ac6454SAndrew Thompson  * Redistribution and use in source and binary forms, with or without
702ac6454SAndrew Thompson  * modification, are permitted provided that the following conditions
802ac6454SAndrew Thompson  * are met:
902ac6454SAndrew Thompson  * 1. Redistributions of source code must retain the above copyright
1002ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer.
1102ac6454SAndrew Thompson  * 2. Redistributions in binary form must reproduce the above copyright
1202ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer in the
1302ac6454SAndrew Thompson  *    documentation and/or other materials provided with the distribution.
1402ac6454SAndrew Thompson  *
1502ac6454SAndrew Thompson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1602ac6454SAndrew Thompson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1702ac6454SAndrew Thompson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1802ac6454SAndrew Thompson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1902ac6454SAndrew Thompson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2002ac6454SAndrew Thompson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2102ac6454SAndrew Thompson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2202ac6454SAndrew Thompson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2302ac6454SAndrew Thompson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2402ac6454SAndrew Thompson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2502ac6454SAndrew Thompson  * SUCH DAMAGE.
2602ac6454SAndrew Thompson  */
2702ac6454SAndrew Thompson 
2802ac6454SAndrew Thompson /*
2902ac6454SAndrew Thompson  * This file contains the USB templates for an USB Message Transfer
3002ac6454SAndrew Thompson  * Protocol device.
3102ac6454SAndrew Thompson  *
3202ac6454SAndrew Thompson  * NOTE: It is common practice that MTP devices use some dummy
3302ac6454SAndrew Thompson  * descriptor cludges to be automatically detected by the host
3402ac6454SAndrew Thompson  * operating system. These descriptors are documented in the LibMTP
3502ac6454SAndrew Thompson  * library at sourceforge.net. The alternative is to supply the host
3602ac6454SAndrew Thompson  * operating system the VID and PID of your device.
3702ac6454SAndrew Thompson  */
3802ac6454SAndrew Thompson 
39d2b99310SHans Petter Selasky #ifdef USB_GLOBAL_INCLUDE_FILE
40d2b99310SHans Petter Selasky #include USB_GLOBAL_INCLUDE_FILE
41d2b99310SHans Petter Selasky #else
42ed6d949aSAndrew Thompson #include <sys/stdint.h>
43ed6d949aSAndrew Thompson #include <sys/stddef.h>
44ed6d949aSAndrew Thompson #include <sys/param.h>
45ed6d949aSAndrew Thompson #include <sys/queue.h>
46ed6d949aSAndrew Thompson #include <sys/types.h>
47ed6d949aSAndrew Thompson #include <sys/systm.h>
48ed6d949aSAndrew Thompson #include <sys/kernel.h>
49ed6d949aSAndrew Thompson #include <sys/bus.h>
50ed6d949aSAndrew Thompson #include <sys/module.h>
51ed6d949aSAndrew Thompson #include <sys/lock.h>
52ed6d949aSAndrew Thompson #include <sys/mutex.h>
53ed6d949aSAndrew Thompson #include <sys/condvar.h>
54ed6d949aSAndrew Thompson #include <sys/sysctl.h>
55ed6d949aSAndrew Thompson #include <sys/sx.h>
56ed6d949aSAndrew Thompson #include <sys/unistd.h>
57ed6d949aSAndrew Thompson #include <sys/callout.h>
58ed6d949aSAndrew Thompson #include <sys/malloc.h>
59ed6d949aSAndrew Thompson #include <sys/priv.h>
60ed6d949aSAndrew Thompson 
6102ac6454SAndrew Thompson #include <dev/usb/usb.h>
62ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h>
63*23ab0871SHans Petter Selasky #include <dev/usb/usb_core.h>
64*23ab0871SHans Petter Selasky 
6502ac6454SAndrew Thompson #include <dev/usb/template/usb_template.h>
66d2b99310SHans Petter Selasky #endif			/* USB_GLOBAL_INCLUDE_FILE */
6702ac6454SAndrew Thompson 
6802ac6454SAndrew Thompson #define	MTP_BREQUEST 0x08
6902ac6454SAndrew Thompson 
7002ac6454SAndrew Thompson enum {
7102ac6454SAndrew Thompson 	STRING_LANG_INDEX,
7202ac6454SAndrew Thompson 	STRING_MTP_DATA_INDEX,
7302ac6454SAndrew Thompson 	STRING_MTP_CONFIG_INDEX,
7402ac6454SAndrew Thompson 	STRING_MTP_VENDOR_INDEX,
7502ac6454SAndrew Thompson 	STRING_MTP_PRODUCT_INDEX,
7602ac6454SAndrew Thompson 	STRING_MTP_SERIAL_INDEX,
7702ac6454SAndrew Thompson 	STRING_MTP_MAX,
7802ac6454SAndrew Thompson };
7902ac6454SAndrew Thompson 
8002ac6454SAndrew Thompson #define	STRING_MTP_DATA	\
8102ac6454SAndrew Thompson   'U', 0, 'S', 0, 'B', 0, ' ', 0, \
8202ac6454SAndrew Thompson   'M', 0, 'T', 0, 'P', 0, \
8302ac6454SAndrew Thompson   ' ', 0, 'I', 0, 'n', 0, 't', 0, \
8402ac6454SAndrew Thompson   'e', 0, 'r', 0, 'f', 0, 'a', 0, \
8502ac6454SAndrew Thompson   'c', 0, 'e', 0,
8602ac6454SAndrew Thompson 
8702ac6454SAndrew Thompson #define	STRING_MTP_CONFIG \
8802ac6454SAndrew Thompson   'D', 0, 'e', 0, 'f', 0, 'a', 0, \
8902ac6454SAndrew Thompson   'u', 0, 'l', 0, 't', 0, ' ', 0, \
9002ac6454SAndrew Thompson   'c', 0, 'o', 0, 'n', 0, 'f', 0, \
9102ac6454SAndrew Thompson   'i', 0, 'g', 0,
9202ac6454SAndrew Thompson 
9302ac6454SAndrew Thompson #define	STRING_MTP_VENDOR \
9402ac6454SAndrew Thompson   'F', 0, 'r', 0, 'e', 0, 'e', 0, \
9502ac6454SAndrew Thompson   'B', 0, 'S', 0, 'D', 0, ' ', 0, \
9602ac6454SAndrew Thompson   'f', 0, 'o', 0, 'u', 0, 'n', 0, \
9702ac6454SAndrew Thompson   'd', 0, 'a', 0, 't', 0, 'i', 0, \
9802ac6454SAndrew Thompson   'o', 0, 'n', 0,
9902ac6454SAndrew Thompson 
10002ac6454SAndrew Thompson #define	STRING_MTP_PRODUCT \
10102ac6454SAndrew Thompson   'U', 0, 'S', 0, 'B', 0, ' ', 0, \
10202ac6454SAndrew Thompson   'M', 0, 'T', 0, 'P', 0,
10302ac6454SAndrew Thompson 
10402ac6454SAndrew Thompson #define	STRING_MTP_SERIAL \
10502ac6454SAndrew Thompson   'J', 0, 'u', 0, 'n', 0, 'e', 0, \
10602ac6454SAndrew Thompson   ' ', 0, '2', 0, '0', 0, '0', 0, \
10702ac6454SAndrew Thompson   '8', 0,
10802ac6454SAndrew Thompson 
10902ac6454SAndrew Thompson /* make the real string descriptors */
11002ac6454SAndrew Thompson 
11102ac6454SAndrew Thompson USB_MAKE_STRING_DESC(STRING_MTP_DATA, string_mtp_data);
11202ac6454SAndrew Thompson USB_MAKE_STRING_DESC(STRING_MTP_CONFIG, string_mtp_config);
11302ac6454SAndrew Thompson USB_MAKE_STRING_DESC(STRING_MTP_VENDOR, string_mtp_vendor);
11402ac6454SAndrew Thompson USB_MAKE_STRING_DESC(STRING_MTP_PRODUCT, string_mtp_product);
11502ac6454SAndrew Thompson USB_MAKE_STRING_DESC(STRING_MTP_SERIAL, string_mtp_serial);
11602ac6454SAndrew Thompson 
11702ac6454SAndrew Thompson /* prototypes */
11802ac6454SAndrew Thompson 
119a593f6b8SAndrew Thompson static usb_temp_get_string_desc_t mtp_get_string_desc;
120a593f6b8SAndrew Thompson static usb_temp_get_vendor_desc_t mtp_get_vendor_desc;
12102ac6454SAndrew Thompson 
122760bc48eSAndrew Thompson static const struct usb_temp_packet_size bulk_mps = {
12302ac6454SAndrew Thompson 	.mps[USB_SPEED_FULL] = 64,
12402ac6454SAndrew Thompson 	.mps[USB_SPEED_HIGH] = 512,
12502ac6454SAndrew Thompson };
12602ac6454SAndrew Thompson 
127760bc48eSAndrew Thompson static const struct usb_temp_packet_size intr_mps = {
12802ac6454SAndrew Thompson 	.mps[USB_SPEED_FULL] = 64,
12902ac6454SAndrew Thompson 	.mps[USB_SPEED_HIGH] = 64,
13002ac6454SAndrew Thompson };
13102ac6454SAndrew Thompson 
132760bc48eSAndrew Thompson static const struct usb_temp_endpoint_desc bulk_out_ep = {
13302ac6454SAndrew Thompson 	.pPacketSize = &bulk_mps,
13402ac6454SAndrew Thompson #ifdef USB_HIP_OUT_EP_0
13502ac6454SAndrew Thompson 	.bEndpointAddress = USB_HIP_OUT_EP_0,
13602ac6454SAndrew Thompson #else
13702ac6454SAndrew Thompson 	.bEndpointAddress = UE_DIR_OUT,
13802ac6454SAndrew Thompson #endif
13902ac6454SAndrew Thompson 	.bmAttributes = UE_BULK,
14002ac6454SAndrew Thompson };
14102ac6454SAndrew Thompson 
142760bc48eSAndrew Thompson static const struct usb_temp_endpoint_desc intr_in_ep = {
14302ac6454SAndrew Thompson 	.pPacketSize = &intr_mps,
14402ac6454SAndrew Thompson 	.bEndpointAddress = UE_DIR_IN,
14502ac6454SAndrew Thompson 	.bmAttributes = UE_INTERRUPT,
14602ac6454SAndrew Thompson };
14702ac6454SAndrew Thompson 
148760bc48eSAndrew Thompson static const struct usb_temp_endpoint_desc bulk_in_ep = {
14902ac6454SAndrew Thompson 	.pPacketSize = &bulk_mps,
15002ac6454SAndrew Thompson #ifdef USB_HIP_IN_EP_0
15102ac6454SAndrew Thompson 	.bEndpointAddress = USB_HIP_IN_EP_0,
15202ac6454SAndrew Thompson #else
15302ac6454SAndrew Thompson 	.bEndpointAddress = UE_DIR_IN,
15402ac6454SAndrew Thompson #endif
15502ac6454SAndrew Thompson 	.bmAttributes = UE_BULK,
15602ac6454SAndrew Thompson };
15702ac6454SAndrew Thompson 
158760bc48eSAndrew Thompson static const struct usb_temp_endpoint_desc *mtp_data_endpoints[] = {
15902ac6454SAndrew Thompson 	&bulk_in_ep,
16002ac6454SAndrew Thompson 	&bulk_out_ep,
16102ac6454SAndrew Thompson 	&intr_in_ep,
16202ac6454SAndrew Thompson 	NULL,
16302ac6454SAndrew Thompson };
16402ac6454SAndrew Thompson 
165760bc48eSAndrew Thompson static const struct usb_temp_interface_desc mtp_data_interface = {
16602ac6454SAndrew Thompson 	.ppEndpoints = mtp_data_endpoints,
16702ac6454SAndrew Thompson 	.bInterfaceClass = UICLASS_IMAGE,
16802ac6454SAndrew Thompson 	.bInterfaceSubClass = UISUBCLASS_SIC,	/* Still Image Class */
16902ac6454SAndrew Thompson 	.bInterfaceProtocol = 1,	/* PIMA 15740 */
17002ac6454SAndrew Thompson 	.iInterface = STRING_MTP_DATA_INDEX,
17102ac6454SAndrew Thompson };
17202ac6454SAndrew Thompson 
173760bc48eSAndrew Thompson static const struct usb_temp_interface_desc *mtp_interfaces[] = {
17402ac6454SAndrew Thompson 	&mtp_data_interface,
17502ac6454SAndrew Thompson 	NULL,
17602ac6454SAndrew Thompson };
17702ac6454SAndrew Thompson 
178760bc48eSAndrew Thompson static const struct usb_temp_config_desc mtp_config_desc = {
17902ac6454SAndrew Thompson 	.ppIfaceDesc = mtp_interfaces,
18002ac6454SAndrew Thompson 	.bmAttributes = UC_BUS_POWERED,
18102ac6454SAndrew Thompson 	.bMaxPower = 25,		/* 50 mA */
18202ac6454SAndrew Thompson 	.iConfiguration = STRING_MTP_CONFIG_INDEX,
18302ac6454SAndrew Thompson };
18402ac6454SAndrew Thompson 
185760bc48eSAndrew Thompson static const struct usb_temp_config_desc *mtp_configs[] = {
18602ac6454SAndrew Thompson 	&mtp_config_desc,
18702ac6454SAndrew Thompson 	NULL,
18802ac6454SAndrew Thompson };
18902ac6454SAndrew Thompson 
190a593f6b8SAndrew Thompson const struct usb_temp_device_desc usb_template_mtp = {
19102ac6454SAndrew Thompson 	.getStringDesc = &mtp_get_string_desc,
19202ac6454SAndrew Thompson 	.getVendorDesc = &mtp_get_vendor_desc,
19302ac6454SAndrew Thompson 	.ppConfigDesc = mtp_configs,
194399e6543SHans Petter Selasky 	.idVendor = USB_TEMPLATE_VENDOR,
195399e6543SHans Petter Selasky 	.idProduct = 0x0011,
19602ac6454SAndrew Thompson 	.bcdDevice = 0x0100,
19702ac6454SAndrew Thompson 	.bDeviceClass = 0,
19802ac6454SAndrew Thompson 	.bDeviceSubClass = 0,
19902ac6454SAndrew Thompson 	.bDeviceProtocol = 0,
20002ac6454SAndrew Thompson 	.iManufacturer = STRING_MTP_VENDOR_INDEX,
20102ac6454SAndrew Thompson 	.iProduct = STRING_MTP_PRODUCT_INDEX,
20202ac6454SAndrew Thompson 	.iSerialNumber = STRING_MTP_SERIAL_INDEX,
20302ac6454SAndrew Thompson };
20402ac6454SAndrew Thompson 
20502ac6454SAndrew Thompson /*------------------------------------------------------------------------*
20602ac6454SAndrew Thompson  *	mtp_get_vendor_desc
20702ac6454SAndrew Thompson  *
20802ac6454SAndrew Thompson  * Return values:
20902ac6454SAndrew Thompson  * NULL: Failure. No such vendor descriptor.
21002ac6454SAndrew Thompson  * Else: Success. Pointer to vendor descriptor is returned.
21102ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
21202ac6454SAndrew Thompson static const void *
2139a8e0122SAndrew Thompson mtp_get_vendor_desc(const struct usb_device_request *req, uint16_t *plen)
21402ac6454SAndrew Thompson {
21502ac6454SAndrew Thompson 	static const uint8_t dummy_desc[0x28] = {
21602ac6454SAndrew Thompson 		0x28, 0, 0, 0, 0, 1, 4, 0,
21702ac6454SAndrew Thompson 		1, 0, 0, 0, 0, 0, 0, 0,
21802ac6454SAndrew Thompson 		0, 1, 0x4D, 0x54, 0x50, 0, 0, 0,
21902ac6454SAndrew Thompson 		0, 0, 0, 0, 0, 0, 0, 0,
22002ac6454SAndrew Thompson 		0, 0, 0, 0, 0, 0, 0, 0,
22102ac6454SAndrew Thompson 	};
22202ac6454SAndrew Thompson 
22302ac6454SAndrew Thompson 	if ((req->bmRequestType == UT_READ_VENDOR_DEVICE) &&
22402ac6454SAndrew Thompson 	    (req->bRequest == MTP_BREQUEST) && (req->wValue[0] == 0) &&
22502ac6454SAndrew Thompson 	    (req->wValue[1] == 0) && (req->wIndex[1] == 0) &&
22602ac6454SAndrew Thompson 	    ((req->wIndex[0] == 4) || (req->wIndex[0] == 5))) {
22702ac6454SAndrew Thompson 		/*
22802ac6454SAndrew Thompson 		 * By returning this descriptor LibMTP will
22902ac6454SAndrew Thompson 		 * automatically pickup our device.
23002ac6454SAndrew Thompson 		 */
23102ac6454SAndrew Thompson 		return (dummy_desc);
23202ac6454SAndrew Thompson 	}
23302ac6454SAndrew Thompson 	return (NULL);
23402ac6454SAndrew Thompson }
23502ac6454SAndrew Thompson 
23602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
23702ac6454SAndrew Thompson  *	mtp_get_string_desc
23802ac6454SAndrew Thompson  *
23902ac6454SAndrew Thompson  * Return values:
24002ac6454SAndrew Thompson  * NULL: Failure. No such string.
24102ac6454SAndrew Thompson  * Else: Success. Pointer to string descriptor is returned.
24202ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
24302ac6454SAndrew Thompson static const void *
24402ac6454SAndrew Thompson mtp_get_string_desc(uint16_t lang_id, uint8_t string_index)
24502ac6454SAndrew Thompson {
24602ac6454SAndrew Thompson 	static const void *ptr[STRING_MTP_MAX] = {
247*23ab0871SHans Petter Selasky 		[STRING_LANG_INDEX] = &usb_string_lang_en,
24802ac6454SAndrew Thompson 		[STRING_MTP_DATA_INDEX] = &string_mtp_data,
24902ac6454SAndrew Thompson 		[STRING_MTP_CONFIG_INDEX] = &string_mtp_config,
25002ac6454SAndrew Thompson 		[STRING_MTP_VENDOR_INDEX] = &string_mtp_vendor,
25102ac6454SAndrew Thompson 		[STRING_MTP_PRODUCT_INDEX] = &string_mtp_product,
25202ac6454SAndrew Thompson 		[STRING_MTP_SERIAL_INDEX] = &string_mtp_serial,
25302ac6454SAndrew Thompson 	};
25402ac6454SAndrew Thompson 
25502ac6454SAndrew Thompson 	static const uint8_t dummy_desc[0x12] = {
25602ac6454SAndrew Thompson 		0x12, 0x03, 0x4D, 0x00, 0x53, 0x00, 0x46, 0x00,
25702ac6454SAndrew Thompson 		0x54, 0x00, 0x31, 0x00, 0x30, 0x00, 0x30, 0x00,
25802ac6454SAndrew Thompson 		MTP_BREQUEST, 0x00,
25902ac6454SAndrew Thompson 	};
26002ac6454SAndrew Thompson 
26102ac6454SAndrew Thompson 	if (string_index == 0xEE) {
26202ac6454SAndrew Thompson 		/*
26302ac6454SAndrew Thompson 		 * By returning this string LibMTP will automatically
26402ac6454SAndrew Thompson 		 * pickup our device.
26502ac6454SAndrew Thompson 		 */
26602ac6454SAndrew Thompson 		return (dummy_desc);
26702ac6454SAndrew Thompson 	}
26802ac6454SAndrew Thompson 	if (string_index == 0) {
269*23ab0871SHans Petter Selasky 		return (&usb_string_lang_en);
27002ac6454SAndrew Thompson 	}
27102ac6454SAndrew Thompson 	if (lang_id != 0x0409) {
27202ac6454SAndrew Thompson 		return (NULL);
27302ac6454SAndrew Thompson 	}
27402ac6454SAndrew Thompson 	if (string_index < STRING_MTP_MAX) {
27502ac6454SAndrew Thompson 		return (ptr[string_index]);
27602ac6454SAndrew Thompson 	}
27702ac6454SAndrew Thompson 	return (NULL);
27802ac6454SAndrew Thompson }
279