mmcsd.c revision 184034
1163516Simp/*-
2163516Simp * Copyright (c) 2006 Bernd Walter.  All rights reserved.
3163516Simp * Copyright (c) 2006 M. Warner Losh.  All rights reserved.
4163516Simp *
5163516Simp * Redistribution and use in source and binary forms, with or without
6163516Simp * modification, are permitted provided that the following conditions
7163516Simp * are met:
8163516Simp * 1. Redistributions of source code must retain the above copyright
9163516Simp *    notice, this list of conditions and the following disclaimer.
10163516Simp * 2. Redistributions in binary form must reproduce the above copyright
11163516Simp *    notice, this list of conditions and the following disclaimer in the
12163516Simp *    documentation and/or other materials provided with the distribution.
13163516Simp *
14163516Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15163516Simp * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16163516Simp * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17163516Simp * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18163516Simp * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19163516Simp * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20163516Simp * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21163516Simp * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22163516Simp * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23163516Simp * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24170002Simp *
25170002Simp * Portions of this software may have been developed with reference to
26170002Simp * the SD Simplified Specification.  The following disclaimer may apply:
27170002Simp *
28170002Simp * The following conditions apply to the release of the simplified
29170002Simp * specification ("Simplified Specification") by the SD Card Association and
30170002Simp * the SD Group. The Simplified Specification is a subset of the complete SD
31170002Simp * Specification which is owned by the SD Card Association and the SD
32170002Simp * Group. This Simplified Specification is provided on a non-confidential
33170002Simp * basis subject to the disclaimers below. Any implementation of the
34170002Simp * Simplified Specification may require a license from the SD Card
35170002Simp * Association, SD Group, SD-3C LLC or other third parties.
36170002Simp *
37170002Simp * Disclaimers:
38170002Simp *
39170002Simp * The information contained in the Simplified Specification is presented only
40170002Simp * as a standard specification for SD Cards and SD Host/Ancillary products and
41170002Simp * is provided "AS-IS" without any representations or warranties of any
42170002Simp * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD
43170002Simp * Card Association for any damages, any infringements of patents or other
44170002Simp * right of the SD Group, SD-3C LLC, the SD Card Association or any third
45170002Simp * parties, which may result from its use. No license is granted by
46170002Simp * implication, estoppel or otherwise under any patent or other rights of the
47170002Simp * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing
48170002Simp * herein shall be construed as an obligation by the SD Group, the SD-3C LLC
49170002Simp * or the SD Card Association to disclose or distribute any technical
50170002Simp * information, know-how or other confidential information to any third party.
51163516Simp */
52163516Simp
53163516Simp#include <sys/cdefs.h>
54163516Simp__FBSDID("$FreeBSD: head/sys/dev/mmc/mmcsd.c 184034 2008-10-18 22:22:25Z mav $");
55163516Simp
56163516Simp#include <sys/param.h>
57163516Simp#include <sys/systm.h>
58163516Simp#include <sys/bio.h>
59163516Simp#include <sys/bus.h>
60163516Simp#include <sys/conf.h>
61163516Simp#include <sys/kernel.h>
62163516Simp#include <sys/kthread.h>
63163516Simp#include <sys/lock.h>
64163516Simp#include <sys/malloc.h>
65163516Simp#include <sys/module.h>
66163516Simp#include <sys/mutex.h>
67163516Simp#include <geom/geom_disk.h>
68163516Simp
69163516Simp#include <dev/mmc/mmcvar.h>
70163516Simp#include <dev/mmc/mmcreg.h>
71163516Simp
72163516Simp#include "mmcbus_if.h"
73163516Simp
74163516Simpstruct mmcsd_softc {
75163516Simp	device_t dev;
76163516Simp	struct mtx sc_mtx;
77163516Simp	struct disk *disk;
78163516Simp	struct proc *p;
79163516Simp	struct bio_queue_head bio_queue;
80184034Smav	daddr_t eblock, eend;	/* Range remaining after the last erase. */
81169567Simp	int running;
82163516Simp};
83163516Simp
84183480Simp#define	MULTI_BLOCK_BROKEN
85163516Simp
86163516Simp/* bus entry points */
87163516Simpstatic int mmcsd_probe(device_t dev);
88163516Simpstatic int mmcsd_attach(device_t dev);
89163516Simpstatic int mmcsd_detach(device_t dev);
90163516Simp
91163516Simp/* disk routines */
92163516Simpstatic int mmcsd_open(struct disk *dp);
93163516Simpstatic int mmcsd_close(struct disk *dp);
94163516Simpstatic void mmcsd_strategy(struct bio *bp);
95163516Simpstatic void mmcsd_task(void *arg);
96163516Simp
97183774Simpstatic const char *mmcsd_card_name(device_t dev);
98183774Simpstatic int mmcsd_bus_bit_width(device_t dev);
99183774Simp
100163516Simp#define MMCSD_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
101163516Simp#define	MMCSD_UNLOCK(_sc)	mtx_unlock(&(_sc)->sc_mtx)
102163516Simp#define MMCSD_LOCK_INIT(_sc) \
103163516Simp	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \
104163516Simp	    "mmcsd", MTX_DEF)
105163516Simp#define MMCSD_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->sc_mtx);
106163516Simp#define MMCSD_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED);
107163516Simp#define MMCSD_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
108163516Simp
109163516Simpstatic int
110163516Simpmmcsd_probe(device_t dev)
111163516Simp{
112163516Simp
113183704Smav	device_quiet(dev);
114183480Simp	device_set_desc(dev, "MMC/SD Memory Card");
115163516Simp	return (0);
116163516Simp}
117163516Simp
118163516Simpstatic int
119163516Simpmmcsd_attach(device_t dev)
120163516Simp{
121163516Simp	struct mmcsd_softc *sc;
122183774Simp	struct disk *d;
123183774Simp	intmax_t mb;
124183774Simp	char unit;
125163516Simp
126163516Simp	sc = device_get_softc(dev);
127163516Simp	sc->dev = dev;
128163516Simp	MMCSD_LOCK_INIT(sc);
129163516Simp
130183774Simp	d = sc->disk = disk_alloc();
131183774Simp	d->d_open = mmcsd_open;
132183774Simp	d->d_close = mmcsd_close;
133183774Simp	d->d_strategy = mmcsd_strategy;
134183774Simp	// d->d_dump = mmcsd_dump;	Need polling mmc layer
135183774Simp	d->d_name = "mmcsd";
136183774Simp	d->d_drv1 = sc;
137184033Smav	d->d_maxsize = 4*1024*1024;	/* Maximum defined SD card AU size. */
138183774Simp	d->d_sectorsize = mmc_get_sector_size(dev);
139183774Simp	d->d_mediasize = mmc_get_media_size(dev) * d->d_sectorsize;
140183774Simp	d->d_unit = device_get_unit(dev);
141184033Smav	d->d_flags = DISKFLAG_CANDELETE;
142183774Simp	/*
143183774Simp	 * Display in most natural units.  There's no cards < 1MB.
144183774Simp	 * The SD standard goes to 2GiB, but the data format supports
145183774Simp	 * up to 4GiB and some card makers push it up to this limit.
146183774Simp	 * The SDHC standard only goes to 32GiB (the data format in
147183774Simp	 * SDHC is good to 2TiB however, which isn't too ugly at
148183774Simp	 * 2048GiBm, so we note it in passing here and don't add the
149183774Simp	 * code to print TiB).
150183774Simp	 */
151183774Simp	mb = d->d_mediasize >> 20;	/* 1MiB == 1 << 20 */
152183774Simp	unit = 'M';
153183805Smav	if (mb >= 10240) {		/* 1GiB = 1024 MiB */
154183774Simp		unit = 'G';
155183774Simp		mb /= 1024;
156183774Simp	}
157183774Simp	device_printf(dev, "%ju%cB <%s Memory Card>%s at %s %dMHz/%dbit\n",
158183774Simp	    mb, unit, mmcsd_card_name(dev),
159183774Simp	    mmc_get_read_only(dev) ? " (read-only)" : "",
160183774Simp	    device_get_nameunit(device_get_parent(dev)),
161183774Simp	    mmc_get_tran_speed(dev) / 1000000, mmcsd_bus_bit_width(dev));
162183774Simp	disk_create(d, DISK_VERSION);
163163516Simp	bioq_init(&sc->bio_queue);
164169567Simp
165169567Simp	sc->running = 1;
166184034Smav	sc->eblock = sc->eend = 0;
167172836Sjulian	kproc_create(&mmcsd_task, sc, &sc->p, 0, 0, "task: mmc/sd card");
168163516Simp
169163516Simp	return (0);
170163516Simp}
171163516Simp
172163516Simpstatic int
173163516Simpmmcsd_detach(device_t dev)
174163516Simp{
175169567Simp	struct mmcsd_softc *sc = device_get_softc(dev);
176169567Simp
177169567Simp	/* kill thread */
178169567Simp	MMCSD_LOCK(sc);
179169567Simp	sc->running = 0;
180169567Simp	wakeup(sc);
181169567Simp	MMCSD_UNLOCK(sc);
182169567Simp
183169567Simp	/* wait for thread to finish.  XXX probably want timeout.  -sorbo */
184169567Simp	MMCSD_LOCK(sc);
185169567Simp	while (sc->running != -1)
186169567Simp		msleep(sc, &sc->sc_mtx, PRIBIO, "detach", 0);
187169567Simp	MMCSD_UNLOCK(sc);
188169567Simp
189169567Simp	/* kill disk */
190169567Simp	disk_destroy(sc->disk);
191169567Simp	/* XXX destroy anything in queue */
192169567Simp
193169567Simp	MMCSD_LOCK_DESTROY(sc);
194169567Simp
195183467Simp	return (0);
196163516Simp}
197163516Simp
198163516Simpstatic int
199163516Simpmmcsd_open(struct disk *dp)
200163516Simp{
201183467Simp	return (0);
202163516Simp}
203163516Simp
204163516Simpstatic int
205163516Simpmmcsd_close(struct disk *dp)
206163516Simp{
207183467Simp	return (0);
208163516Simp}
209163516Simp
210163516Simpstatic void
211163516Simpmmcsd_strategy(struct bio *bp)
212163516Simp{
213163516Simp	struct mmcsd_softc *sc;
214163516Simp
215163516Simp	sc = (struct mmcsd_softc *)bp->bio_disk->d_drv1;
216163516Simp	MMCSD_LOCK(sc);
217163516Simp	bioq_disksort(&sc->bio_queue, bp);
218163516Simp	wakeup(sc);
219163516Simp	MMCSD_UNLOCK(sc);
220163516Simp}
221163516Simp
222184033Smavstatic daddr_t
223184033Smavmmcsd_rw(struct mmcsd_softc *sc, struct bio *bp)
224184033Smav{
225184033Smav	daddr_t block, end;
226184033Smav	struct mmc_command cmd;
227184033Smav	struct mmc_command stop;
228184033Smav	struct mmc_request req;
229184033Smav	struct mmc_data data;
230184033Smav	device_t dev = sc->dev;
231184033Smav	int sz = sc->disk->d_sectorsize;
232184033Smav
233184033Smav	block = bp->bio_pblkno;
234184033Smav	end = bp->bio_pblkno + (bp->bio_bcount / sz);
235184033Smav	while (block < end) {
236184033Smav		char *vaddr = bp->bio_data +
237184033Smav		    (block - bp->bio_pblkno) * sz;
238184033Smav		int numblocks;
239184033Smav#ifdef MULTI_BLOCK
240184033Smav		numblocks = end - block;
241184033Smav#else
242184033Smav		numblocks = 1;
243184033Smav#endif
244184033Smav		memset(&req, 0, sizeof(req));
245184033Smav    		memset(&cmd, 0, sizeof(cmd));
246184033Smav		memset(&stop, 0, sizeof(stop));
247184033Smav		req.cmd = &cmd;
248184033Smav		cmd.data = &data;
249184033Smav		if (bp->bio_cmd == BIO_READ) {
250184033Smav			if (numblocks > 1)
251184033Smav				cmd.opcode = MMC_READ_MULTIPLE_BLOCK;
252184033Smav			else
253184033Smav				cmd.opcode = MMC_READ_SINGLE_BLOCK;
254184033Smav		} else {
255184033Smav			if (numblocks > 1)
256184033Smav				cmd.opcode = MMC_WRITE_MULTIPLE_BLOCK;
257184033Smav			else
258184033Smav				cmd.opcode = MMC_WRITE_BLOCK;
259184033Smav		}
260184033Smav		cmd.arg = block;
261184033Smav		if (!mmc_get_high_cap(dev))
262184033Smav			cmd.arg <<= 9;
263184033Smav		cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
264184033Smav		data.data = vaddr;
265184033Smav		data.mrq = &req;
266184033Smav		if (bp->bio_cmd == BIO_READ)
267184033Smav			data.flags = MMC_DATA_READ;
268184033Smav		else
269184033Smav			data.flags = MMC_DATA_WRITE;
270184033Smav		data.len = numblocks * sz;
271184033Smav		if (numblocks > 1) {
272184033Smav			data.flags |= MMC_DATA_MULTI;
273184033Smav			stop.opcode = MMC_STOP_TRANSMISSION;
274184033Smav			stop.arg = 0;
275184033Smav			stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
276184033Smav			req.stop = &stop;
277184033Smav		}
278184033Smav//		printf("Len %d  %lld-%lld flags %#x sz %d\n",
279184033Smav//		    (int)data.len, (long long)block, (long long)end, data.flags, sz);
280184033Smav		MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev,
281184033Smav		    &req);
282184033Smav		if (req.cmd->error != MMC_ERR_NONE)
283184033Smav			break;
284184033Smav		block += numblocks;
285184033Smav	}
286184033Smav	return (block);
287184033Smav}
288184033Smav
289184033Smavstatic daddr_t
290184033Smavmmcsd_delete(struct mmcsd_softc *sc, struct bio *bp)
291184033Smav{
292184033Smav	daddr_t block, end, start, stop;
293184033Smav	struct mmc_command cmd;
294184033Smav	struct mmc_request req;
295184033Smav	device_t dev = sc->dev;
296184033Smav	int sz = sc->disk->d_sectorsize;
297184033Smav	int erase_sector;
298184033Smav
299184033Smav	block = bp->bio_pblkno;
300184033Smav	end = bp->bio_pblkno + (bp->bio_bcount / sz);
301184034Smav	/* Coalesce with part remaining from previous request. */
302184034Smav	if (block > sc->eblock && block <= sc->eend)
303184034Smav		block = sc->eblock;
304184034Smav	if (end >= sc->eblock && end < sc->eend)
305184034Smav		end = sc->eend;
306184033Smav	/* Safe round to the erase sector boundaries. */
307184033Smav	erase_sector = mmc_get_erase_sector(dev);
308184033Smav	start = block + erase_sector - 1;	 /* Round up. */
309184033Smav	start -= start % erase_sector;
310184033Smav	stop = end;				/* Round down. */
311184033Smav	stop -= end % erase_sector;
312184034Smav	/* We can't erase area smaller then sector, store it for later. */
313184034Smav	if (start >= stop) {
314184034Smav		sc->eblock = block;
315184034Smav		sc->eend = end;
316184033Smav		return (end);
317184034Smav	}
318184033Smav
319184033Smav	/* Set erase start position. */
320184033Smav	memset(&req, 0, sizeof(req));
321184033Smav	memset(&cmd, 0, sizeof(cmd));
322184033Smav	req.cmd = &cmd;
323184033Smav	if (mmc_get_card_type(dev) == mode_sd)
324184033Smav		cmd.opcode = SD_ERASE_WR_BLK_START;
325184033Smav	else
326184033Smav		cmd.opcode = MMC_ERASE_GROUP_START;
327184033Smav	cmd.arg = start;
328184033Smav	if (!mmc_get_high_cap(dev))
329184033Smav		cmd.arg <<= 9;
330184033Smav	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
331184033Smav	MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev,
332184033Smav	    &req);
333184033Smav	if (req.cmd->error != MMC_ERR_NONE) {
334184033Smav	    printf("erase err1: %d\n", req.cmd->error);
335184033Smav	    return (block);
336184033Smav	}
337184033Smav	/* Set erase stop position. */
338184033Smav	memset(&req, 0, sizeof(req));
339184033Smav	memset(&cmd, 0, sizeof(cmd));
340184033Smav	req.cmd = &cmd;
341184033Smav	if (mmc_get_card_type(dev) == mode_sd)
342184033Smav		cmd.opcode = SD_ERASE_WR_BLK_END;
343184033Smav	else
344184033Smav		cmd.opcode = MMC_ERASE_GROUP_END;
345184033Smav	cmd.arg = stop;
346184033Smav	if (!mmc_get_high_cap(dev))
347184033Smav		cmd.arg <<= 9;
348184033Smav	cmd.arg--;
349184033Smav	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
350184033Smav	MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev,
351184033Smav	    &req);
352184033Smav	if (req.cmd->error != MMC_ERR_NONE) {
353184033Smav	    printf("erase err2: %d\n", req.cmd->error);
354184033Smav	    return (block);
355184033Smav	}
356184033Smav	/* Erase range. */
357184033Smav	memset(&req, 0, sizeof(req));
358184033Smav	memset(&cmd, 0, sizeof(cmd));
359184033Smav	req.cmd = &cmd;
360184033Smav	cmd.opcode = MMC_ERASE;
361184033Smav	cmd.arg = 0;
362184033Smav	cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
363184033Smav	MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev,
364184033Smav	    &req);
365184033Smav	if (req.cmd->error != MMC_ERR_NONE) {
366184033Smav	    printf("erase err3 %d\n", req.cmd->error);
367184033Smav	    return (block);
368184033Smav	}
369184034Smav	/* Store one of remaining parts for the next call. */
370184034Smav	if (bp->bio_pblkno >= sc->eblock || block == start) {
371184034Smav		sc->eblock = stop;	/* Predict next forward. */
372184034Smav		sc->eend = end;
373184034Smav	} else {
374184034Smav		sc->eblock = block;	/* Predict next backward. */
375184034Smav		sc->eend = start;
376184034Smav	}
377184033Smav	return (end);
378184033Smav}
379184033Smav
380163516Simpstatic void
381163516Simpmmcsd_task(void *arg)
382163516Simp{
383163516Simp	struct mmcsd_softc *sc = (struct mmcsd_softc*)arg;
384163516Simp	struct bio *bp;
385163516Simp	int sz;
386163516Simp	daddr_t block, end;
387163516Simp	device_t dev;
388163516Simp
389163516Simp	dev = sc->dev;
390169567Simp	while (sc->running) {
391163516Simp		MMCSD_LOCK(sc);
392163516Simp		do {
393163516Simp			bp = bioq_first(&sc->bio_queue);
394163516Simp			if (bp == NULL)
395163516Simp				msleep(sc, &sc->sc_mtx, PRIBIO, "jobqueue", 0);
396169567Simp		} while (bp == NULL && sc->running);
397169567Simp		if (bp)
398169567Simp			bioq_remove(&sc->bio_queue, bp);
399163516Simp		MMCSD_UNLOCK(sc);
400169567Simp		if (!sc->running)
401169567Simp			break;
402183448Simp		if (bp->bio_cmd != BIO_READ && mmc_get_read_only(dev)) {
403183448Simp			bp->bio_error = EROFS;
404183448Simp			bp->bio_resid = bp->bio_bcount;
405183448Simp			bp->bio_flags |= BIO_ERROR;
406183448Simp			biodone(bp);
407183448Simp			continue;
408183448Simp		}
409163516Simp		MMCBUS_ACQUIRE_BUS(device_get_parent(dev), dev);
410163516Simp		sz = sc->disk->d_sectorsize;
411184033Smav		block = bp->bio_pblkno;
412163516Simp		end = bp->bio_pblkno + (bp->bio_bcount / sz);
413184033Smav		if (bp->bio_cmd == BIO_READ || bp->bio_cmd == BIO_WRITE) {
414184034Smav			/* Access to the remaining erase block obsoletes it. */
415184034Smav			if (block < sc->eend && end > sc->eblock)
416184034Smav				sc->eblock = sc->eend = 0;
417184033Smav			block = mmcsd_rw(sc, bp);
418184033Smav		} else if (bp->bio_cmd == BIO_DELETE) {
419184033Smav			block = mmcsd_delete(sc, bp);
420163516Simp		}
421163516Simp		MMCBUS_RELEASE_BUS(device_get_parent(dev), dev);
422183480Simp		if (block < end) {
423183480Simp			bp->bio_error = EIO;
424183480Simp			bp->bio_resid = (end - block) * sz;
425183480Simp			bp->bio_flags |= BIO_ERROR;
426183480Simp		}
427163516Simp		biodone(bp);
428163516Simp	}
429169567Simp
430169567Simp	/* tell parent we're done */
431169567Simp	MMCSD_LOCK(sc);
432169567Simp	sc->running = -1;
433169567Simp	wakeup(sc);
434169567Simp	MMCSD_UNLOCK(sc);
435169567Simp
436172836Sjulian	kproc_exit(0);
437163516Simp}
438163516Simp
439183774Simpstatic const char *
440183774Simpmmcsd_card_name(device_t dev)
441183774Simp{
442183774Simp	if (mmc_get_card_type(dev) == mode_mmc)
443183774Simp		return ("MMC");
444183774Simp	if (mmc_get_high_cap(dev))
445183774Simp		return ("SDHC");
446183774Simp	return ("SD");
447183774Simp}
448183774Simp
449183774Simpstatic int
450183774Simpmmcsd_bus_bit_width(device_t dev)
451183774Simp{
452183774Simp	if (mmc_get_bus_width(dev) == bus_width_1)
453183774Simp		return (1);
454183774Simp	if (mmc_get_bus_width(dev) == bus_width_4)
455183774Simp		return (4);
456183774Simp	return (8);
457183774Simp}
458183774Simp
459163516Simpstatic device_method_t mmcsd_methods[] = {
460163516Simp	DEVMETHOD(device_probe, mmcsd_probe),
461163516Simp	DEVMETHOD(device_attach, mmcsd_attach),
462163516Simp	DEVMETHOD(device_detach, mmcsd_detach),
463163516Simp	{0, 0},
464163516Simp};
465163516Simp
466163516Simpstatic driver_t mmcsd_driver = {
467163516Simp	"mmcsd",
468163516Simp	mmcsd_methods,
469163516Simp	sizeof(struct mmcsd_softc),
470163516Simp};
471163516Simpstatic devclass_t mmcsd_devclass;
472163516Simp
473163516SimpDRIVER_MODULE(mmcsd, mmc, mmcsd_driver, mmcsd_devclass, 0, 0);
474