1223467Shselasky#include <sys/cdefs.h>
2223467Shselasky__FBSDID("$FreeBSD$");
3223467Shselasky
4223467Shselasky/*-
5223467Shselasky * Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
6223467Shselasky *
7223467Shselasky * Redistribution and use in source and binary forms, with or without
8223467Shselasky * modification, are permitted provided that the following conditions
9223467Shselasky * are met:
10223467Shselasky * 1. Redistributions of source code must retain the above copyright
11223467Shselasky *    notice, this list of conditions and the following disclaimer.
12223467Shselasky * 2. Redistributions in binary form must reproduce the above copyright
13223467Shselasky *    notice, this list of conditions and the following disclaimer in the
14223467Shselasky *    documentation and/or other materials provided with the distribution.
15223467Shselasky *
16223467Shselasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17223467Shselasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18223467Shselasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19223467Shselasky * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20223467Shselasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21223467Shselasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22223467Shselasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23223467Shselasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24223467Shselasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25223467Shselasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26223467Shselasky * SUCH DAMAGE.
27223467Shselasky */
28223467Shselasky
29223467Shselasky/*
30223467Shselasky * This file contains the USB template for an USB Modem Device.
31223467Shselasky */
32223467Shselasky
33223467Shselasky#include <sys/stdint.h>
34223467Shselasky#include <sys/stddef.h>
35223467Shselasky#include <sys/param.h>
36223467Shselasky#include <sys/queue.h>
37223467Shselasky#include <sys/types.h>
38223467Shselasky#include <sys/systm.h>
39223467Shselasky#include <sys/kernel.h>
40223467Shselasky#include <sys/bus.h>
41223467Shselasky#include <sys/module.h>
42223467Shselasky#include <sys/lock.h>
43223467Shselasky#include <sys/mutex.h>
44223467Shselasky#include <sys/condvar.h>
45223467Shselasky#include <sys/sysctl.h>
46223467Shselasky#include <sys/sx.h>
47223467Shselasky#include <sys/unistd.h>
48223467Shselasky#include <sys/callout.h>
49223467Shselasky#include <sys/malloc.h>
50223467Shselasky#include <sys/priv.h>
51223467Shselasky
52223467Shselasky#include <dev/usb/usb.h>
53223467Shselasky#include <dev/usb/usbdi.h>
54223467Shselasky#include <dev/usb/usb_cdc.h>
55223467Shselasky
56223467Shselasky#include <dev/usb/template/usb_template.h>
57223467Shselasky
58223467Shselaskyenum {
59223467Shselasky	INDEX_LANG,
60223467Shselasky	INDEX_MODEM,
61223467Shselasky	INDEX_PRODUCT,
62223467Shselasky	INDEX_MAX,
63223467Shselasky};
64223467Shselasky
65223467Shselasky#define	STRING_LANG \
66223467Shselasky  0x09, 0x04,				/* American English */
67223467Shselasky
68223467Shselasky#define	STRING_PRODUCT \
69223467Shselasky  'M', 0, 'o', 0, 'd', 0, 'e', 0, 'm', 0, ' ', 0, \
70223467Shselasky  'T', 0, 'e', 0, 's', 0, 't', 0, ' ', 0, \
71223467Shselasky  'D', 0, 'e', 0, 'v', 0, 'i', 0, 'c', 0, 'e', 0, ' ', 0,
72223467Shselasky
73223467Shselasky#define	STRING_MODEM \
74223467Shselasky  'M', 0, 'o', 0, 'd', 0, 'e', 0, 'm', 0, ' ', 0, \
75223467Shselasky  'i', 0, 'n', 0, 't', 0, 'e', 0, 'r', 0, 'f', 0, 'a', 0, 'c', 0, 'e', 0,
76223467Shselasky
77223467Shselasky/* make the real string descriptors */
78223467Shselasky
79223467ShselaskyUSB_MAKE_STRING_DESC(STRING_LANG, string_lang);
80223467ShselaskyUSB_MAKE_STRING_DESC(STRING_MODEM, string_modem);
81223467ShselaskyUSB_MAKE_STRING_DESC(STRING_PRODUCT, string_product);
82223467Shselasky
83223467Shselasky#define	MODEM_IFACE_0 0
84223467Shselasky#define	MODEM_IFACE_1 1
85223467Shselasky
86223467Shselasky/* prototypes */
87223467Shselasky
88223467Shselaskystatic const struct usb_temp_packet_size modem_bulk_mps = {
89223467Shselasky	.mps[USB_SPEED_LOW] = 8,
90223467Shselasky	.mps[USB_SPEED_FULL] = 64,
91223467Shselasky	.mps[USB_SPEED_HIGH] = 512,
92223467Shselasky};
93223467Shselasky
94223467Shselaskystatic const struct usb_temp_packet_size modem_intr_mps = {
95223467Shselasky	.mps[USB_SPEED_LOW] = 8,
96223467Shselasky	.mps[USB_SPEED_FULL] = 8,
97223467Shselasky	.mps[USB_SPEED_HIGH] = 8,
98223467Shselasky};
99223467Shselasky
100223467Shselaskystatic const struct usb_temp_interval modem_intr_interval = {
101229103Shselasky	.bInterval[USB_SPEED_LOW] = 8,	/* 8ms */
102229103Shselasky	.bInterval[USB_SPEED_FULL] = 8,	/* 8ms */
103229103Shselasky	.bInterval[USB_SPEED_HIGH] = 7,	/* 8ms */
104223467Shselasky};
105223467Shselasky
106223467Shselaskystatic const struct usb_temp_endpoint_desc modem_ep_0 = {
107223467Shselasky	.pPacketSize = &modem_intr_mps,
108223467Shselasky	.pIntervals = &modem_intr_interval,
109223467Shselasky	.bEndpointAddress = UE_DIR_IN,
110223467Shselasky	.bmAttributes = UE_INTERRUPT,
111223467Shselasky};
112223467Shselasky
113223467Shselaskystatic const struct usb_temp_endpoint_desc modem_ep_1 = {
114223467Shselasky	.pPacketSize = &modem_bulk_mps,
115223467Shselasky	.bEndpointAddress = UE_DIR_OUT,
116223467Shselasky	.bmAttributes = UE_BULK,
117223467Shselasky};
118223467Shselasky
119223467Shselaskystatic const struct usb_temp_endpoint_desc modem_ep_2 = {
120223467Shselasky	.pPacketSize = &modem_bulk_mps,
121223467Shselasky	.bEndpointAddress = UE_DIR_IN,
122223467Shselasky	.bmAttributes = UE_BULK,
123223467Shselasky};
124223467Shselasky
125223467Shselaskystatic const struct usb_temp_endpoint_desc *modem_iface_0_ep[] = {
126223467Shselasky	&modem_ep_0,
127223467Shselasky	NULL,
128223467Shselasky};
129223467Shselasky
130223467Shselaskystatic const struct usb_temp_endpoint_desc *modem_iface_1_ep[] = {
131223467Shselasky	&modem_ep_1,
132223467Shselasky	&modem_ep_2,
133223467Shselasky	NULL,
134223467Shselasky};
135223467Shselasky
136223467Shselaskystatic const uint8_t modem_raw_desc_0[] = {
137223467Shselasky	0x05, 0x24, 0x00, 0x10, 0x01
138223467Shselasky};
139223467Shselasky
140223467Shselaskystatic const uint8_t modem_raw_desc_1[] = {
141223467Shselasky	0x05, 0x24, 0x06, MODEM_IFACE_0, MODEM_IFACE_1
142223467Shselasky};
143223467Shselasky
144223467Shselaskystatic const uint8_t modem_raw_desc_2[] = {
145223467Shselasky	0x05, 0x24, 0x01, 0x03, MODEM_IFACE_1
146223467Shselasky};
147223467Shselasky
148223467Shselaskystatic const uint8_t modem_raw_desc_3[] = {
149223467Shselasky	0x04, 0x24, 0x02, 0x07
150223467Shselasky};
151223467Shselasky
152223467Shselaskystatic const void *modem_iface_0_desc[] = {
153223467Shselasky	&modem_raw_desc_0,
154223467Shselasky	&modem_raw_desc_1,
155223467Shselasky	&modem_raw_desc_2,
156223467Shselasky	&modem_raw_desc_3,
157223467Shselasky	NULL,
158223467Shselasky};
159223467Shselasky
160223467Shselaskystatic const struct usb_temp_interface_desc modem_iface_0 = {
161223467Shselasky	.ppRawDesc = modem_iface_0_desc,
162223467Shselasky	.ppEndpoints = modem_iface_0_ep,
163223467Shselasky	.bInterfaceClass = 2,
164223467Shselasky	.bInterfaceSubClass = 2,
165223467Shselasky	.bInterfaceProtocol = 1,
166223467Shselasky	.iInterface = INDEX_MODEM,
167223467Shselasky};
168223467Shselasky
169223467Shselaskystatic const struct usb_temp_interface_desc modem_iface_1 = {
170223467Shselasky	.ppEndpoints = modem_iface_1_ep,
171223467Shselasky	.bInterfaceClass = 10,
172223467Shselasky	.bInterfaceSubClass = 0,
173223467Shselasky	.bInterfaceProtocol = 0,
174223467Shselasky	.iInterface = INDEX_MODEM,
175223467Shselasky};
176223467Shselasky
177223467Shselaskystatic const struct usb_temp_interface_desc *modem_interfaces[] = {
178223467Shselasky	&modem_iface_0,
179223467Shselasky	&modem_iface_1,
180223467Shselasky	NULL,
181223467Shselasky};
182223467Shselasky
183223467Shselaskystatic const struct usb_temp_config_desc modem_config_desc = {
184223467Shselasky	.ppIfaceDesc = modem_interfaces,
185223467Shselasky	.bmAttributes = UC_BUS_POWERED,
186223467Shselasky	.bMaxPower = 25,		/* 50 mA */
187223467Shselasky	.iConfiguration = INDEX_PRODUCT,
188223467Shselasky};
189223467Shselasky
190223467Shselaskystatic const struct usb_temp_config_desc *modem_configs[] = {
191223467Shselasky	&modem_config_desc,
192223467Shselasky	NULL,
193223467Shselasky};
194223467Shselasky
195223467Shselaskystatic usb_temp_get_string_desc_t modem_get_string_desc;
196223467Shselaskystatic usb_temp_get_vendor_desc_t modem_get_vendor_desc;
197223467Shselasky
198223467Shselaskyconst struct usb_temp_device_desc usb_template_modem = {
199223467Shselasky	.getStringDesc = &modem_get_string_desc,
200223467Shselasky	.getVendorDesc = &modem_get_vendor_desc,
201223467Shselasky	.ppConfigDesc = modem_configs,
202223467Shselasky	.idVendor = USB_TEMPLATE_VENDOR,
203223467Shselasky	.idProduct = 0x000E,
204223467Shselasky	.bcdDevice = 0x0100,
205223467Shselasky	.bDeviceClass = UDCLASS_COMM,
206223467Shselasky	.bDeviceSubClass = 0,
207223467Shselasky	.bDeviceProtocol = 0,
208223467Shselasky	.iManufacturer = 0,
209223467Shselasky	.iProduct = INDEX_PRODUCT,
210223467Shselasky	.iSerialNumber = 0,
211223467Shselasky};
212223467Shselasky
213223467Shselasky/*------------------------------------------------------------------------*
214223467Shselasky *      modem_get_vendor_desc
215223467Shselasky *
216223467Shselasky * Return values:
217223467Shselasky * NULL: Failure. No such vendor descriptor.
218223467Shselasky * Else: Success. Pointer to vendor descriptor is returned.
219223467Shselasky *------------------------------------------------------------------------*/
220223467Shselaskystatic const void *
221223467Shselaskymodem_get_vendor_desc(const struct usb_device_request *req, uint16_t *plen)
222223467Shselasky{
223223467Shselasky	return (NULL);
224223467Shselasky}
225223467Shselasky
226223467Shselasky/*------------------------------------------------------------------------*
227223467Shselasky *	modem_get_string_desc
228223467Shselasky *
229223467Shselasky * Return values:
230223467Shselasky * NULL: Failure. No such string.
231223467Shselasky * Else: Success. Pointer to string descriptor is returned.
232223467Shselasky *------------------------------------------------------------------------*/
233223467Shselaskystatic const void *
234223467Shselaskymodem_get_string_desc(uint16_t lang_id, uint8_t string_index)
235223467Shselasky{
236223467Shselasky	static const void *ptr[INDEX_MAX] = {
237223467Shselasky		[INDEX_LANG] = &string_lang,
238223467Shselasky		[INDEX_MODEM] = &string_modem,
239223467Shselasky		[INDEX_PRODUCT] = &string_product,
240223467Shselasky	};
241223467Shselasky
242223467Shselasky	if (string_index == 0) {
243223467Shselasky		return (&string_lang);
244223467Shselasky	}
245223467Shselasky	if (lang_id != 0x0409) {
246223467Shselasky		return (NULL);
247223467Shselasky	}
248223467Shselasky	if (string_index < INDEX_MAX) {
249223467Shselasky		return (ptr[string_index]);
250223467Shselasky	}
251223467Shselasky	return (NULL);
252223467Shselasky}
253