Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/usb/template/usb_template_phone.c
/* $FreeBSD$ */ | /* $FreeBSD$ */ | ||||
/*- | /*- | ||||
* Copyright (c) 2014 Hans Petter Selasky. All rights reserved. | * Copyright (c) 2014 Hans Petter Selasky | ||||
* Copyright (c) 2018 The FreeBSD Foundation | |||||
* All rights reserved. | |||||
* | * | ||||
* Portions of this software were developed by Edward Tomasz Napierala | |||||
* under sponsorship from the FreeBSD Foundation. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
* modification, are permitted provided that the following conditions | * modification, are permitted provided that the following conditions | ||||
* are met: | * are met: | ||||
* 1. Redistributions of source code must retain the above copyright | * 1. Redistributions of source code must retain the above copyright | ||||
* notice, this list of conditions and the following disclaimer. | * notice, this list of conditions and the following disclaimer. | ||||
* 2. Redistributions in binary form must reproduce the above copyright | * 2. Redistributions in binary form must reproduce the above copyright | ||||
* notice, this list of conditions and the following disclaimer in the | * notice, this list of conditions and the following disclaimer in the | ||||
* documentation and/or other materials provided with the distribution. | * documentation and/or other materials provided with the distribution. | ||||
Show All 36 Lines | |||||
#include <sys/callout.h> | #include <sys/callout.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/priv.h> | #include <sys/priv.h> | ||||
#include <dev/usb/usb.h> | #include <dev/usb/usb.h> | ||||
#include <dev/usb/usbdi.h> | #include <dev/usb/usbdi.h> | ||||
#include <dev/usb/usb_core.h> | #include <dev/usb/usb_core.h> | ||||
#include <dev/usb/usb_cdc.h> | #include <dev/usb/usb_cdc.h> | ||||
#include <dev/usb/usb_ioctl.h> | |||||
#include <dev/usb/usb_util.h> | |||||
#include <dev/usb/template/usb_template.h> | #include <dev/usb/template/usb_template.h> | ||||
#endif /* USB_GLOBAL_INCLUDE_FILE */ | #endif /* USB_GLOBAL_INCLUDE_FILE */ | ||||
enum { | enum { | ||||
INDEX_PHONE_LANG, | PHONE_LANG_INDEX, | ||||
INDEX_PHONE_MIXER, | PHONE_MIXER_INDEX, | ||||
INDEX_PHONE_RECORD, | PHONE_RECORD_INDEX, | ||||
INDEX_PHONE_PLAYBACK, | PHONE_PLAYBACK_INDEX, | ||||
INDEX_PHONE_PRODUCT, | PHONE_PRODUCT_INDEX, | ||||
INDEX_PHONE_HID, | PHONE_HID_INDEX, | ||||
INDEX_PHONE_MAX, | PHONE_MAX_INDEX, | ||||
}; | }; | ||||
#define STRING_PHONE_PRODUCT \ | #define PHONE_DEFAULT_MIXER "Mixer interface" | ||||
"U\0S\0B\0 \0P\0h\0o\0n\0e\0 \0D\0e\0v\0i\0c\0e" | #define PHONE_DEFAULT_RECORD "Record interface" | ||||
#define PHONE_DEFAULT_PLAYBACK "Playback interface" | |||||
#define PHONE_DEFAULT_PRODUCT "USB Phone Device" | |||||
#define PHONE_DEFAULT_HID "HID interface" | |||||
#define STRING_PHONE_MIXER \ | static struct usb_string_descriptor phone_mixer; | ||||
"M\0i\0x\0e\0r\0 \0i\0n\0t\0e\0r\0f\0a\0c\0e" | static struct usb_string_descriptor phone_record; | ||||
static struct usb_string_descriptor phone_playback; | |||||
static struct usb_string_descriptor phone_product; | |||||
static struct usb_string_descriptor phone_hid; | |||||
#define STRING_PHONE_RECORD \ | static struct sysctl_ctx_list phone_ctx_list; | ||||
"R\0e\0c\0o\0r\0d\0 \0i\0n\0t\0e\0r\0f\0a\0c\0e" | |||||
#define STRING_PHONE_PLAYBACK \ | |||||
"P\0l\0a\0y\0b\0a\0c\0k\0 \0i\0n\0t\0e\0r\0f\0a\0c\0e" | |||||
#define STRING_PHONE_HID \ | |||||
"H\0I\0D\0 \0i\0n\0t\0e\0r\0f\0a\0c\0e" | |||||
/* make the real string descriptors */ | |||||
USB_MAKE_STRING_DESC(STRING_PHONE_MIXER, string_phone_mixer); | |||||
USB_MAKE_STRING_DESC(STRING_PHONE_RECORD, string_phone_record); | |||||
USB_MAKE_STRING_DESC(STRING_PHONE_PLAYBACK, string_phone_playback); | |||||
USB_MAKE_STRING_DESC(STRING_PHONE_PRODUCT, string_phone_product); | |||||
USB_MAKE_STRING_DESC(STRING_PHONE_HID, string_phone_hid); | |||||
/* prototypes */ | /* prototypes */ | ||||
/* | /* | ||||
* Phone Mixer description structures | * Phone Mixer description structures | ||||
* | * | ||||
* Some of the phone descriptors were dumped from no longer in | * Some of the phone descriptors were dumped from no longer in | ||||
* production Yealink VOIP USB phone adapter: | * production Yealink VOIP USB phone adapter: | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | |||||
}; | }; | ||||
static const struct usb_temp_interface_desc phone_iface_0 = { | static const struct usb_temp_interface_desc phone_iface_0 = { | ||||
.ppEndpoints = NULL, /* no endpoints */ | .ppEndpoints = NULL, /* no endpoints */ | ||||
.ppRawDesc = phone_raw_iface_0_desc, | .ppRawDesc = phone_raw_iface_0_desc, | ||||
.bInterfaceClass = UICLASS_AUDIO, | .bInterfaceClass = UICLASS_AUDIO, | ||||
.bInterfaceSubClass = UISUBCLASS_AUDIOCONTROL, | .bInterfaceSubClass = UISUBCLASS_AUDIOCONTROL, | ||||
.bInterfaceProtocol = 0, | .bInterfaceProtocol = 0, | ||||
.iInterface = INDEX_PHONE_MIXER, | .iInterface = PHONE_MIXER_INDEX, | ||||
}; | }; | ||||
static const uint8_t phone_raw_desc_20[] = { | static const uint8_t phone_raw_desc_20[] = { | ||||
0x07, 0x24, 0x01, 0x04, 0x01, 0x01, 0x00 | 0x07, 0x24, 0x01, 0x04, 0x01, 0x01, 0x00 | ||||
}; | }; | ||||
static const uint8_t phone_raw_desc_21[] = { | static const uint8_t phone_raw_desc_21[] = { | ||||
0x0b, 0x24, 0x02, 0x01, 0x01, 0x02, 0x10, 0x01, | 0x0b, 0x24, 0x02, 0x01, 0x01, 0x02, 0x10, 0x01, | ||||
Show All 40 Lines | |||||
}; | }; | ||||
static const struct usb_temp_interface_desc phone_iface_1_alt_0 = { | static const struct usb_temp_interface_desc phone_iface_1_alt_0 = { | ||||
.ppEndpoints = NULL, /* no endpoints */ | .ppEndpoints = NULL, /* no endpoints */ | ||||
.ppRawDesc = NULL, /* no raw descriptors */ | .ppRawDesc = NULL, /* no raw descriptors */ | ||||
.bInterfaceClass = UICLASS_AUDIO, | .bInterfaceClass = UICLASS_AUDIO, | ||||
.bInterfaceSubClass = UISUBCLASS_AUDIOSTREAM, | .bInterfaceSubClass = UISUBCLASS_AUDIOSTREAM, | ||||
.bInterfaceProtocol = 0, | .bInterfaceProtocol = 0, | ||||
.iInterface = INDEX_PHONE_PLAYBACK, | .iInterface = PHONE_PLAYBACK_INDEX, | ||||
}; | }; | ||||
static const struct usb_temp_interface_desc phone_iface_1_alt_1 = { | static const struct usb_temp_interface_desc phone_iface_1_alt_1 = { | ||||
.ppEndpoints = phone_iface_1_ep, | .ppEndpoints = phone_iface_1_ep, | ||||
.ppRawDesc = phone_raw_iface_1_desc, | .ppRawDesc = phone_raw_iface_1_desc, | ||||
.bInterfaceClass = UICLASS_AUDIO, | .bInterfaceClass = UICLASS_AUDIO, | ||||
.bInterfaceSubClass = UISUBCLASS_AUDIOSTREAM, | .bInterfaceSubClass = UISUBCLASS_AUDIOSTREAM, | ||||
.bInterfaceProtocol = 0, | .bInterfaceProtocol = 0, | ||||
.iInterface = INDEX_PHONE_PLAYBACK, | .iInterface = PHONE_PLAYBACK_INDEX, | ||||
.isAltInterface = 1, /* this is an alternate setting */ | .isAltInterface = 1, /* this is an alternate setting */ | ||||
}; | }; | ||||
static const uint8_t phone_raw_desc_30[] = { | static const uint8_t phone_raw_desc_30[] = { | ||||
0x07, 0x24, 0x01, 0x02, 0x01, 0x01, 0x00 | 0x07, 0x24, 0x01, 0x02, 0x01, 0x01, 0x00 | ||||
}; | }; | ||||
static const uint8_t phone_raw_desc_31[] = { | static const uint8_t phone_raw_desc_31[] = { | ||||
Show All 31 Lines | |||||
}; | }; | ||||
static const struct usb_temp_interface_desc phone_iface_2_alt_0 = { | static const struct usb_temp_interface_desc phone_iface_2_alt_0 = { | ||||
.ppEndpoints = NULL, /* no endpoints */ | .ppEndpoints = NULL, /* no endpoints */ | ||||
.ppRawDesc = NULL, /* no raw descriptors */ | .ppRawDesc = NULL, /* no raw descriptors */ | ||||
.bInterfaceClass = UICLASS_AUDIO, | .bInterfaceClass = UICLASS_AUDIO, | ||||
.bInterfaceSubClass = UISUBCLASS_AUDIOSTREAM, | .bInterfaceSubClass = UISUBCLASS_AUDIOSTREAM, | ||||
.bInterfaceProtocol = 0, | .bInterfaceProtocol = 0, | ||||
.iInterface = INDEX_PHONE_RECORD, | .iInterface = PHONE_RECORD_INDEX, | ||||
}; | }; | ||||
static const struct usb_temp_interface_desc phone_iface_2_alt_1 = { | static const struct usb_temp_interface_desc phone_iface_2_alt_1 = { | ||||
.ppEndpoints = phone_iface_2_ep, | .ppEndpoints = phone_iface_2_ep, | ||||
.ppRawDesc = phone_raw_iface_2_desc, | .ppRawDesc = phone_raw_iface_2_desc, | ||||
.bInterfaceClass = UICLASS_AUDIO, | .bInterfaceClass = UICLASS_AUDIO, | ||||
.bInterfaceSubClass = UISUBCLASS_AUDIOSTREAM, | .bInterfaceSubClass = UISUBCLASS_AUDIOSTREAM, | ||||
.bInterfaceProtocol = 0, | .bInterfaceProtocol = 0, | ||||
.iInterface = INDEX_PHONE_RECORD, | .iInterface = PHONE_RECORD_INDEX, | ||||
.isAltInterface = 1, /* this is an alternate setting */ | .isAltInterface = 1, /* this is an alternate setting */ | ||||
}; | }; | ||||
static const uint8_t phone_hid_raw_desc_0[] = { | static const uint8_t phone_hid_raw_desc_0[] = { | ||||
0x09, 0x21, 0x00, 0x01, 0x00, 0x01, 0x22, sizeof(phone_hid_descriptor), | 0x09, 0x21, 0x00, 0x01, 0x00, 0x01, 0x22, sizeof(phone_hid_descriptor), | ||||
0x00 | 0x00 | ||||
}; | }; | ||||
Show All 25 Lines | |||||
}; | }; | ||||
static const struct usb_temp_interface_desc phone_iface_3 = { | static const struct usb_temp_interface_desc phone_iface_3 = { | ||||
.ppEndpoints = phone_iface_3_ep, | .ppEndpoints = phone_iface_3_ep, | ||||
.ppRawDesc = phone_hid_desc_0, | .ppRawDesc = phone_hid_desc_0, | ||||
.bInterfaceClass = UICLASS_HID, | .bInterfaceClass = UICLASS_HID, | ||||
.bInterfaceSubClass = 0, | .bInterfaceSubClass = 0, | ||||
.bInterfaceProtocol = 0, | .bInterfaceProtocol = 0, | ||||
.iInterface = INDEX_PHONE_HID, | .iInterface = PHONE_HID_INDEX, | ||||
}; | }; | ||||
static const struct usb_temp_interface_desc *phone_interfaces[] = { | static const struct usb_temp_interface_desc *phone_interfaces[] = { | ||||
&phone_iface_0, | &phone_iface_0, | ||||
&phone_iface_1_alt_0, | &phone_iface_1_alt_0, | ||||
&phone_iface_1_alt_1, | &phone_iface_1_alt_1, | ||||
&phone_iface_2_alt_0, | &phone_iface_2_alt_0, | ||||
&phone_iface_2_alt_1, | &phone_iface_2_alt_1, | ||||
&phone_iface_3, | &phone_iface_3, | ||||
NULL, | NULL, | ||||
}; | }; | ||||
static const struct usb_temp_config_desc phone_config_desc = { | static const struct usb_temp_config_desc phone_config_desc = { | ||||
.ppIfaceDesc = phone_interfaces, | .ppIfaceDesc = phone_interfaces, | ||||
.bmAttributes = UC_BUS_POWERED, | .bmAttributes = UC_BUS_POWERED, | ||||
.bMaxPower = 25, /* 50 mA */ | .bMaxPower = 25, /* 50 mA */ | ||||
.iConfiguration = INDEX_PHONE_PRODUCT, | .iConfiguration = PHONE_PRODUCT_INDEX, | ||||
}; | }; | ||||
static const struct usb_temp_config_desc *phone_configs[] = { | static const struct usb_temp_config_desc *phone_configs[] = { | ||||
&phone_config_desc, | &phone_config_desc, | ||||
NULL, | NULL, | ||||
}; | }; | ||||
static usb_temp_get_string_desc_t phone_get_string_desc; | static usb_temp_get_string_desc_t phone_get_string_desc; | ||||
static usb_temp_get_vendor_desc_t phone_get_vendor_desc; | static usb_temp_get_vendor_desc_t phone_get_vendor_desc; | ||||
const struct usb_temp_device_desc usb_template_phone = { | struct usb_temp_device_desc usb_template_phone = { | ||||
.getStringDesc = &phone_get_string_desc, | .getStringDesc = &phone_get_string_desc, | ||||
.getVendorDesc = &phone_get_vendor_desc, | .getVendorDesc = &phone_get_vendor_desc, | ||||
.ppConfigDesc = phone_configs, | .ppConfigDesc = phone_configs, | ||||
.idVendor = USB_TEMPLATE_VENDOR, | .idVendor = USB_TEMPLATE_VENDOR, | ||||
.idProduct = 0xb001, | .idProduct = 0xb001, | ||||
.bcdDevice = 0x0100, | .bcdDevice = 0x0100, | ||||
.bDeviceClass = UDCLASS_IN_INTERFACE, | .bDeviceClass = UDCLASS_IN_INTERFACE, | ||||
.bDeviceSubClass = 0, | .bDeviceSubClass = 0, | ||||
.bDeviceProtocol = 0, | .bDeviceProtocol = 0, | ||||
.iManufacturer = 0, | .iManufacturer = 0, | ||||
.iProduct = INDEX_PHONE_PRODUCT, | .iProduct = PHONE_PRODUCT_INDEX, | ||||
.iSerialNumber = 0, | .iSerialNumber = 0, | ||||
}; | }; | ||||
/*------------------------------------------------------------------------* | /*------------------------------------------------------------------------* | ||||
* phone_get_vendor_desc | * phone_get_vendor_desc | ||||
* | * | ||||
* Return values: | * Return values: | ||||
* NULL: Failure. No such vendor descriptor. | * NULL: Failure. No such vendor descriptor. | ||||
Show All 17 Lines | |||||
* | * | ||||
* Return values: | * Return values: | ||||
* NULL: Failure. No such string. | * NULL: Failure. No such string. | ||||
* Else: Success. Pointer to string descriptor is returned. | * Else: Success. Pointer to string descriptor is returned. | ||||
*------------------------------------------------------------------------*/ | *------------------------------------------------------------------------*/ | ||||
static const void * | static const void * | ||||
phone_get_string_desc(uint16_t lang_id, uint8_t string_index) | phone_get_string_desc(uint16_t lang_id, uint8_t string_index) | ||||
{ | { | ||||
static const void *ptr[INDEX_PHONE_MAX] = { | static const void *ptr[PHONE_MAX_INDEX] = { | ||||
[INDEX_PHONE_LANG] = &usb_string_lang_en, | [PHONE_LANG_INDEX] = &usb_string_lang_en, | ||||
[INDEX_PHONE_MIXER] = &string_phone_mixer, | [PHONE_MIXER_INDEX] = &phone_mixer, | ||||
[INDEX_PHONE_RECORD] = &string_phone_record, | [PHONE_RECORD_INDEX] = &phone_record, | ||||
[INDEX_PHONE_PLAYBACK] = &string_phone_playback, | [PHONE_PLAYBACK_INDEX] = &phone_playback, | ||||
[INDEX_PHONE_PRODUCT] = &string_phone_product, | [PHONE_PRODUCT_INDEX] = &phone_product, | ||||
[INDEX_PHONE_HID] = &string_phone_hid, | [PHONE_HID_INDEX] = &phone_hid, | ||||
}; | }; | ||||
if (string_index == 0) { | if (string_index == 0) { | ||||
return (&usb_string_lang_en); | return (&usb_string_lang_en); | ||||
} | } | ||||
if (lang_id != 0x0409) { | if (lang_id != 0x0409) { | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
if (string_index < INDEX_PHONE_MAX) { | if (string_index < PHONE_MAX_INDEX) { | ||||
return (ptr[string_index]); | return (ptr[string_index]); | ||||
} | } | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
static void | |||||
phone_init(void *arg __unused) | |||||
{ | |||||
struct sysctl_oid *parent; | |||||
char parent_name[3]; | |||||
usb_make_str_desc(&phone_mixer, sizeof(phone_mixer), | |||||
PHONE_DEFAULT_MIXER); | |||||
usb_make_str_desc(&phone_record, sizeof(phone_record), | |||||
PHONE_DEFAULT_RECORD); | |||||
usb_make_str_desc(&phone_playback, sizeof(phone_playback), | |||||
PHONE_DEFAULT_PLAYBACK); | |||||
usb_make_str_desc(&phone_product, sizeof(phone_product), | |||||
PHONE_DEFAULT_PRODUCT); | |||||
usb_make_str_desc(&phone_hid, sizeof(phone_hid), | |||||
PHONE_DEFAULT_HID); | |||||
snprintf(parent_name, sizeof(parent_name), "%d", USB_TEMP_PHONE); | |||||
sysctl_ctx_init(&phone_ctx_list); | |||||
parent = SYSCTL_ADD_NODE(&phone_ctx_list, | |||||
SYSCTL_STATIC_CHILDREN(_hw_usb_templates), OID_AUTO, | |||||
parent_name, CTLFLAG_RW, | |||||
0, "USB Phone device side template"); | |||||
SYSCTL_ADD_U16(&phone_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO, | |||||
"vendor_id", CTLFLAG_RWTUN, | |||||
&usb_template_cdce.idVendor, 1, "Vendor identifier"); | |||||
SYSCTL_ADD_U16(&phone_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO, | |||||
"product_id", CTLFLAG_RWTUN, | |||||
&usb_template_cdce.idProduct, 1, "Product identifier"); | |||||
#if 0 | |||||
SYSCTL_ADD_PROC(&phone_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO, | |||||
"mixer", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, | |||||
&phone_mixer, sizeof(phone_mixer), usb_temp_sysctl, | |||||
"A", "Mixer interface string"); | |||||
SYSCTL_ADD_PROC(&phone_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO, | |||||
"record", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, | |||||
&phone_record, sizeof(phone_record), usb_temp_sysctl, | |||||
"A", "Record interface string"); | |||||
SYSCTL_ADD_PROC(&phone_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO, | |||||
"playback", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, | |||||
&phone_playback, sizeof(phone_playback), usb_temp_sysctl, | |||||
"A", "Playback interface string"); | |||||
#endif | |||||
SYSCTL_ADD_PROC(&phone_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO, | |||||
"product", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, | |||||
&phone_product, sizeof(phone_product), usb_temp_sysctl, | |||||
"A", "Product string"); | |||||
SYSCTL_ADD_PROC(&phone_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO, | |||||
"hid", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, | |||||
&phone_hid, sizeof(phone_hid), usb_temp_sysctl, | |||||
"A", "HID interface string"); | |||||
} | |||||
static void | |||||
phone_uninit(void *arg __unused) | |||||
{ | |||||
sysctl_ctx_free(&phone_ctx_list); | |||||
} | |||||
SYSINIT(phone_init, SI_SUB_LOCK, SI_ORDER_FIRST, phone_init, NULL); | |||||
SYSUNINIT(phone_init, SI_SUB_LOCK, SI_ORDER_FIRST, phone_uninit, NULL); |