xref: /freebsd/sys/dev/usb/template/usb_template_phone.c (revision 71625ec9ad2a9bc8c09784fbd23b759830e0ee5f)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2014 Hans Petter Selasky
5  * Copyright (c) 2018 The FreeBSD Foundation
6  * All rights reserved.
7  *
8  * Portions of this software were developed by Edward Tomasz Napierala
9  * under sponsorship from the FreeBSD Foundation.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 /*
34  * This file contains the USB template for an USB phone device.
35  */
36 
37 #ifdef USB_GLOBAL_INCLUDE_FILE
38 #include USB_GLOBAL_INCLUDE_FILE
39 #else
40 #include <sys/stdint.h>
41 #include <sys/stddef.h>
42 #include <sys/param.h>
43 #include <sys/queue.h>
44 #include <sys/types.h>
45 #include <sys/systm.h>
46 #include <sys/kernel.h>
47 #include <sys/bus.h>
48 #include <sys/module.h>
49 #include <sys/lock.h>
50 #include <sys/mutex.h>
51 #include <sys/condvar.h>
52 #include <sys/sysctl.h>
53 #include <sys/sx.h>
54 #include <sys/unistd.h>
55 #include <sys/callout.h>
56 #include <sys/malloc.h>
57 #include <sys/priv.h>
58 
59 #include <dev/usb/usb.h>
60 #include <dev/usb/usbdi.h>
61 #include <dev/usb/usb_core.h>
62 #include <dev/usb/usb_cdc.h>
63 #include <dev/usb/usb_ioctl.h>
64 #include <dev/usb/usb_util.h>
65 
66 #include <dev/usb/template/usb_template.h>
67 #endif			/* USB_GLOBAL_INCLUDE_FILE */
68 
69 enum {
70 	PHONE_LANG_INDEX,
71 	PHONE_MIXER_INDEX,
72 	PHONE_RECORD_INDEX,
73 	PHONE_PLAYBACK_INDEX,
74 	PHONE_HID_INDEX,
75 	PHONE_MANUFACTURER_INDEX,
76 	PHONE_PRODUCT_INDEX,
77 	PHONE_SERIAL_NUMBER_INDEX,
78 	PHONE_MAX_INDEX,
79 };
80 
81 #define	PHONE_DEFAULT_VENDOR_ID		USB_TEMPLATE_VENDOR
82 #define	PHONE_DEFAULT_PRODUCT_ID	0x05dc
83 #define	PHONE_DEFAULT_MIXER		"Mixer interface"
84 #define	PHONE_DEFAULT_RECORD		"Record interface"
85 #define	PHONE_DEFAULT_PLAYBACK		"Playback interface"
86 #define	PHONE_DEFAULT_HID		"HID interface"
87 #define	PHONE_DEFAULT_MANUFACTURER	USB_TEMPLATE_MANUFACTURER
88 #define	PHONE_DEFAULT_PRODUCT		"USB Phone Device"
89 #define	PHONE_DEFAULT_SERIAL_NUMBER	"March 2008"
90 
91 static struct usb_string_descriptor	phone_mixer;
92 static struct usb_string_descriptor	phone_record;
93 static struct usb_string_descriptor	phone_playback;
94 static struct usb_string_descriptor	phone_hid;
95 static struct usb_string_descriptor	phone_manufacturer;
96 static struct usb_string_descriptor	phone_product;
97 static struct usb_string_descriptor	phone_serial_number;
98 
99 static struct sysctl_ctx_list		phone_ctx_list;
100 
101 /* prototypes */
102 
103 /*
104  * Phone Mixer description structures
105  *
106  * Some of the phone descriptors were dumped from no longer in
107  * production Yealink VOIP USB phone adapter:
108  */
109 static uint8_t phone_hid_descriptor[] = {
110 	0x05, 0x0b, 0x09, 0x01, 0xa1, 0x01, 0x05, 0x09,
111 	0x19, 0x01, 0x29, 0x3f, 0x15, 0x00, 0x25, 0x01,
112 	0x75, 0x01, 0x95, 0x80, 0x81, 0x00, 0x05, 0x08,
113 	0x19, 0x01, 0x29, 0x10, 0x15, 0x00, 0x25, 0x01,
114 	0x75, 0x01, 0x95, 0x80, 0x91, 0x00, 0xc0
115 };
116 
117 static const uint8_t phone_raw_desc_0[] = {
118 	0x0a, 0x24, 0x01, 0x00, 0x01, 0x4a, 0x00, 0x02,
119 	0x01, 0x02
120 };
121 
122 static const uint8_t phone_raw_desc_1[] = {
123 	0x0c, 0x24, 0x02, 0x01, 0x01, 0x02, 0x00, 0x01,
124 	0x00, 0x00, 0x00, 0x00
125 };
126 
127 static const uint8_t phone_raw_desc_2[] = {
128 	0x0c, 0x24, 0x02, 0x02, 0x01, 0x01, 0x00, 0x01,
129 	0x00, 0x00, 0x00, 0x00
130 };
131 
132 static const uint8_t phone_raw_desc_3[] = {
133 	0x09, 0x24, 0x03, 0x03, 0x01, 0x03, 0x00, 0x06,
134 	0x00
135 };
136 
137 static const uint8_t phone_raw_desc_4[] = {
138 	0x09, 0x24, 0x03, 0x04, 0x01, 0x01, 0x00, 0x05,
139 	0x00
140 };
141 
142 static const uint8_t phone_raw_desc_5[] = {
143 	0x0b, 0x24, 0x06, 0x05, 0x01, 0x02, 0x03, 0x00,
144 	0x03, 0x00, 0x00
145 };
146 
147 static const uint8_t phone_raw_desc_6[] = {
148 	0x0b, 0x24, 0x06, 0x06, 0x02, 0x02, 0x03, 0x00,
149 	0x03, 0x00, 0x00
150 };
151 
152 static const void *phone_raw_iface_0_desc[] = {
153 	phone_raw_desc_0,
154 	phone_raw_desc_1,
155 	phone_raw_desc_2,
156 	phone_raw_desc_3,
157 	phone_raw_desc_4,
158 	phone_raw_desc_5,
159 	phone_raw_desc_6,
160 	NULL,
161 };
162 
163 static const struct usb_temp_interface_desc phone_iface_0 = {
164 	.ppEndpoints = NULL,		/* no endpoints */
165 	.ppRawDesc = phone_raw_iface_0_desc,
166 	.bInterfaceClass = UICLASS_AUDIO,
167 	.bInterfaceSubClass = UISUBCLASS_AUDIOCONTROL,
168 	.bInterfaceProtocol = 0,
169 	.iInterface = PHONE_MIXER_INDEX,
170 };
171 
172 static const uint8_t phone_raw_desc_20[] = {
173 	0x07, 0x24, 0x01, 0x04, 0x01, 0x01, 0x00
174 };
175 
176 static const uint8_t phone_raw_desc_21[] = {
177 	0x0b, 0x24, 0x02, 0x01, 0x01, 0x02, 0x10, 0x01,
178 	/* 8kHz */
179 	0x40, 0x1f, 0x00
180 };
181 
182 static const uint8_t phone_raw_desc_22[] = {
183 	0x07, 0x25, 0x01, 0x00, 0x00, 0x00, 0x00
184 };
185 
186 static const void *phone_raw_iface_1_desc[] = {
187 	phone_raw_desc_20,
188 	phone_raw_desc_21,
189 	NULL,
190 };
191 
192 static const void *phone_raw_ep_1_desc[] = {
193 	phone_raw_desc_22,
194 	NULL,
195 };
196 
197 static const struct usb_temp_packet_size phone_isoc_mps = {
198 	.mps[USB_SPEED_FULL] = 0x10,
199 	.mps[USB_SPEED_HIGH] = 0x10,
200 };
201 
202 static const struct usb_temp_interval phone_isoc_interval = {
203 	.bInterval[USB_SPEED_FULL] = 1,	/* 1:1 */
204 	.bInterval[USB_SPEED_HIGH] = 4,	/* 1:8 */
205 };
206 
207 static const struct usb_temp_endpoint_desc phone_isoc_in_ep = {
208 	.ppRawDesc = phone_raw_ep_1_desc,
209 	.pPacketSize = &phone_isoc_mps,
210 	.pIntervals = &phone_isoc_interval,
211 	.bEndpointAddress = UE_DIR_IN,
212 	.bmAttributes = UE_ISOCHRONOUS,
213 };
214 
215 static const struct usb_temp_endpoint_desc *phone_iface_1_ep[] = {
216 	&phone_isoc_in_ep,
217 	NULL,
218 };
219 
220 static const struct usb_temp_interface_desc phone_iface_1_alt_0 = {
221 	.ppEndpoints = NULL,		/* no endpoints */
222 	.ppRawDesc = NULL,		/* no raw descriptors */
223 	.bInterfaceClass = UICLASS_AUDIO,
224 	.bInterfaceSubClass = UISUBCLASS_AUDIOSTREAM,
225 	.bInterfaceProtocol = 0,
226 	.iInterface = PHONE_PLAYBACK_INDEX,
227 };
228 
229 static const struct usb_temp_interface_desc phone_iface_1_alt_1 = {
230 	.ppEndpoints = phone_iface_1_ep,
231 	.ppRawDesc = phone_raw_iface_1_desc,
232 	.bInterfaceClass = UICLASS_AUDIO,
233 	.bInterfaceSubClass = UISUBCLASS_AUDIOSTREAM,
234 	.bInterfaceProtocol = 0,
235 	.iInterface = PHONE_PLAYBACK_INDEX,
236 	.isAltInterface = 1,		/* this is an alternate setting */
237 };
238 
239 static const uint8_t phone_raw_desc_30[] = {
240 	0x07, 0x24, 0x01, 0x02, 0x01, 0x01, 0x00
241 };
242 
243 static const uint8_t phone_raw_desc_31[] = {
244 	0x0b, 0x24, 0x02, 0x01, 0x01, 0x02, 0x10, 0x01,
245 	/* 8kHz */
246 	0x40, 0x1f, 0x00
247 };
248 
249 static const uint8_t phone_raw_desc_32[] = {
250 	0x07, 0x25, 0x01, 0x00, 0x00, 0x00, 0x00
251 };
252 
253 static const void *phone_raw_iface_2_desc[] = {
254 	phone_raw_desc_30,
255 	phone_raw_desc_31,
256 	NULL,
257 };
258 
259 static const void *phone_raw_ep_2_desc[] = {
260 	phone_raw_desc_32,
261 	NULL,
262 };
263 
264 static const struct usb_temp_endpoint_desc phone_isoc_out_ep = {
265 	.ppRawDesc = phone_raw_ep_2_desc,
266 	.pPacketSize = &phone_isoc_mps,
267 	.pIntervals = &phone_isoc_interval,
268 	.bEndpointAddress = UE_DIR_OUT,
269 	.bmAttributes = UE_ISOCHRONOUS,
270 };
271 
272 static const struct usb_temp_endpoint_desc *phone_iface_2_ep[] = {
273 	&phone_isoc_out_ep,
274 	NULL,
275 };
276 
277 static const struct usb_temp_interface_desc phone_iface_2_alt_0 = {
278 	.ppEndpoints = NULL,		/* no endpoints */
279 	.ppRawDesc = NULL,		/* no raw descriptors */
280 	.bInterfaceClass = UICLASS_AUDIO,
281 	.bInterfaceSubClass = UISUBCLASS_AUDIOSTREAM,
282 	.bInterfaceProtocol = 0,
283 	.iInterface = PHONE_RECORD_INDEX,
284 };
285 
286 static const struct usb_temp_interface_desc phone_iface_2_alt_1 = {
287 	.ppEndpoints = phone_iface_2_ep,
288 	.ppRawDesc = phone_raw_iface_2_desc,
289 	.bInterfaceClass = UICLASS_AUDIO,
290 	.bInterfaceSubClass = UISUBCLASS_AUDIOSTREAM,
291 	.bInterfaceProtocol = 0,
292 	.iInterface = PHONE_RECORD_INDEX,
293 	.isAltInterface = 1,		/* this is an alternate setting */
294 };
295 
296 static const uint8_t phone_hid_raw_desc_0[] = {
297 	0x09, 0x21, 0x00, 0x01, 0x00, 0x01, 0x22, sizeof(phone_hid_descriptor),
298 	0x00
299 };
300 
301 static const void *phone_hid_desc_0[] = {
302 	phone_hid_raw_desc_0,
303 	NULL,
304 };
305 
306 static const struct usb_temp_packet_size phone_hid_mps = {
307 	.mps[USB_SPEED_FULL] = 0x10,
308 	.mps[USB_SPEED_HIGH] = 0x10,
309 };
310 
311 static const struct usb_temp_interval phone_hid_interval = {
312 	.bInterval[USB_SPEED_FULL] = 2,		/* 2ms */
313 	.bInterval[USB_SPEED_HIGH] = 2,		/* 2ms */
314 };
315 
316 static const struct usb_temp_endpoint_desc phone_hid_in_ep = {
317 	.pPacketSize = &phone_hid_mps,
318 	.pIntervals = &phone_hid_interval,
319 	.bEndpointAddress = UE_DIR_IN,
320 	.bmAttributes = UE_INTERRUPT,
321 };
322 
323 static const struct usb_temp_endpoint_desc *phone_iface_3_ep[] = {
324 	&phone_hid_in_ep,
325 	NULL,
326 };
327 
328 static const struct usb_temp_interface_desc phone_iface_3 = {
329 	.ppEndpoints = phone_iface_3_ep,
330 	.ppRawDesc = phone_hid_desc_0,
331 	.bInterfaceClass = UICLASS_HID,
332 	.bInterfaceSubClass = 0,
333 	.bInterfaceProtocol = 0,
334 	.iInterface = PHONE_HID_INDEX,
335 };
336 
337 static const struct usb_temp_interface_desc *phone_interfaces[] = {
338 	&phone_iface_0,
339 	&phone_iface_1_alt_0,
340 	&phone_iface_1_alt_1,
341 	&phone_iface_2_alt_0,
342 	&phone_iface_2_alt_1,
343 	&phone_iface_3,
344 	NULL,
345 };
346 
347 static const struct usb_temp_config_desc phone_config_desc = {
348 	.ppIfaceDesc = phone_interfaces,
349 	.bmAttributes = 0,
350 	.bMaxPower = 0,
351 	.iConfiguration = PHONE_PRODUCT_INDEX,
352 };
353 
354 static const struct usb_temp_config_desc *phone_configs[] = {
355 	&phone_config_desc,
356 	NULL,
357 };
358 
359 static usb_temp_get_string_desc_t phone_get_string_desc;
360 static usb_temp_get_vendor_desc_t phone_get_vendor_desc;
361 
362 struct usb_temp_device_desc usb_template_phone = {
363 	.getStringDesc = &phone_get_string_desc,
364 	.getVendorDesc = &phone_get_vendor_desc,
365 	.ppConfigDesc = phone_configs,
366 	.idVendor = PHONE_DEFAULT_VENDOR_ID,
367 	.idProduct = PHONE_DEFAULT_PRODUCT_ID,
368 	.bcdDevice = 0x0100,
369 	.bDeviceClass = UDCLASS_IN_INTERFACE,
370 	.bDeviceSubClass = 0,
371 	.bDeviceProtocol = 0,
372 	.iManufacturer = PHONE_MANUFACTURER_INDEX,
373 	.iProduct = PHONE_PRODUCT_INDEX,
374 	.iSerialNumber = PHONE_SERIAL_NUMBER_INDEX,
375 };
376 
377 /*------------------------------------------------------------------------*
378  *      phone_get_vendor_desc
379  *
380  * Return values:
381  * NULL: Failure. No such vendor descriptor.
382  * Else: Success. Pointer to vendor descriptor is returned.
383  *------------------------------------------------------------------------*/
384 static const void *
phone_get_vendor_desc(const struct usb_device_request * req,uint16_t * plen)385 phone_get_vendor_desc(const struct usb_device_request *req, uint16_t *plen)
386 {
387 	if ((req->bmRequestType == 0x81) && (req->bRequest == 0x06) &&
388 	    (req->wValue[0] == 0x00) && (req->wValue[1] == 0x22) &&
389 	    (req->wIndex[1] == 0) && (req->wIndex[0] == 3 /* iface */)) {
390 		*plen = sizeof(phone_hid_descriptor);
391 		return (phone_hid_descriptor);
392 	}
393 	return (NULL);
394 }
395 
396 /*------------------------------------------------------------------------*
397  *	phone_get_string_desc
398  *
399  * Return values:
400  * NULL: Failure. No such string.
401  * Else: Success. Pointer to string descriptor is returned.
402  *------------------------------------------------------------------------*/
403 static const void *
phone_get_string_desc(uint16_t lang_id,uint8_t string_index)404 phone_get_string_desc(uint16_t lang_id, uint8_t string_index)
405 {
406 	static const void *ptr[PHONE_MAX_INDEX] = {
407 		[PHONE_LANG_INDEX] = &usb_string_lang_en,
408 		[PHONE_MIXER_INDEX] = &phone_mixer,
409 		[PHONE_RECORD_INDEX] = &phone_record,
410 		[PHONE_PLAYBACK_INDEX] = &phone_playback,
411 		[PHONE_HID_INDEX] = &phone_hid,
412 		[PHONE_MANUFACTURER_INDEX] = &phone_manufacturer,
413 		[PHONE_PRODUCT_INDEX] = &phone_product,
414 		[PHONE_SERIAL_NUMBER_INDEX] = &phone_serial_number,
415 	};
416 
417 	if (string_index == 0) {
418 		return (&usb_string_lang_en);
419 	}
420 	if (lang_id != 0x0409) {
421 		return (NULL);
422 	}
423 	if (string_index < PHONE_MAX_INDEX) {
424 		return (ptr[string_index]);
425 	}
426 	return (NULL);
427 }
428 
429 static void
phone_init(void * arg __unused)430 phone_init(void *arg __unused)
431 {
432 	struct sysctl_oid *parent;
433 	char parent_name[3];
434 
435 	usb_make_str_desc(&phone_mixer, sizeof(phone_mixer),
436 	    PHONE_DEFAULT_MIXER);
437 	usb_make_str_desc(&phone_record, sizeof(phone_record),
438 	    PHONE_DEFAULT_RECORD);
439 	usb_make_str_desc(&phone_playback, sizeof(phone_playback),
440 	    PHONE_DEFAULT_PLAYBACK);
441 	usb_make_str_desc(&phone_hid, sizeof(phone_hid),
442 	    PHONE_DEFAULT_HID);
443 	usb_make_str_desc(&phone_manufacturer, sizeof(phone_manufacturer),
444 	    PHONE_DEFAULT_MANUFACTURER);
445 	usb_make_str_desc(&phone_product, sizeof(phone_product),
446 	    PHONE_DEFAULT_PRODUCT);
447 	usb_make_str_desc(&phone_serial_number, sizeof(phone_serial_number),
448 	    PHONE_DEFAULT_SERIAL_NUMBER);
449 
450 	snprintf(parent_name, sizeof(parent_name), "%d", USB_TEMP_PHONE);
451 	sysctl_ctx_init(&phone_ctx_list);
452 
453 	parent = SYSCTL_ADD_NODE(&phone_ctx_list,
454 	    SYSCTL_STATIC_CHILDREN(_hw_usb_templates), OID_AUTO,
455 	    parent_name, CTLFLAG_RW | CTLFLAG_MPSAFE,
456 	    0, "USB Phone device side template");
457 	SYSCTL_ADD_U16(&phone_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO,
458 	    "vendor_id", CTLFLAG_RWTUN,
459 	    &usb_template_cdce.idVendor, 1, "Vendor identifier");
460 	SYSCTL_ADD_U16(&phone_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO,
461 	    "product_id", CTLFLAG_RWTUN,
462 	    &usb_template_cdce.idProduct, 1, "Product identifier");
463 #if 0
464 	SYSCTL_ADD_PROC(&phone_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO,
465 	    "mixer", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
466 	    &phone_mixer, sizeof(phone_mixer), usb_temp_sysctl,
467 	    "A", "Mixer interface string");
468 	SYSCTL_ADD_PROC(&phone_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO,
469 	    "record", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
470 	    &phone_record, sizeof(phone_record), usb_temp_sysctl,
471 	    "A", "Record interface string");
472 	SYSCTL_ADD_PROC(&phone_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO,
473 	    "playback", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
474 	    &phone_playback, sizeof(phone_playback), usb_temp_sysctl,
475 	    "A", "Playback interface string");
476 	SYSCTL_ADD_PROC(&phone_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO,
477 	    "hid", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
478 	    &phone_hid, sizeof(phone_hid), usb_temp_sysctl,
479 	    "A", "HID interface string");
480 #endif
481 	SYSCTL_ADD_PROC(&phone_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO,
482 	    "manufacturer", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
483 	    &phone_manufacturer, sizeof(phone_manufacturer), usb_temp_sysctl,
484 	    "A", "Manufacturer string");
485 	SYSCTL_ADD_PROC(&phone_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO,
486 	    "product", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
487 	    &phone_product, sizeof(phone_product), usb_temp_sysctl,
488 	    "A", "Product string");
489 	SYSCTL_ADD_PROC(&phone_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO,
490 	    "serial_number", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
491 	    &phone_serial_number, sizeof(phone_serial_number), usb_temp_sysctl,
492 	    "A", "Serial number string");
493 }
494 
495 static void
phone_uninit(void * arg __unused)496 phone_uninit(void *arg __unused)
497 {
498 
499 	sysctl_ctx_free(&phone_ctx_list);
500 }
501 
502 SYSINIT(phone_init, SI_SUB_LOCK, SI_ORDER_FIRST, phone_init, NULL);
503 SYSUNINIT(phone_uninit, SI_SUB_LOCK, SI_ORDER_FIRST, phone_uninit, NULL);
504