xref: /freebsd/sys/dev/aac/aac_disk.c (revision 1d66272a85cde1c8a69c58f4b5dd649babd6eca6)
1 /*-
2  * Copyright (c) 2000 Michael Smith
3  * Copyright (c) 2000 BSDi
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  *	$FreeBSD$
28  */
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/kernel.h>
33 
34 #include <dev/aac/aac_compat.h>
35 #include <sys/bus.h>
36 #include <sys/conf.h>
37 #include <sys/devicestat.h>
38 #include <sys/disk.h>
39 
40 #include <machine/bus.h>
41 #include <sys/rman.h>
42 
43 #include <dev/aac/aacreg.h>
44 #include <dev/aac/aac_ioctl.h>
45 #include <dev/aac/aacvar.h>
46 
47 /*
48  * Interface to parent.
49  */
50 static int aac_disk_probe(device_t dev);
51 static int aac_disk_attach(device_t dev);
52 static int aac_disk_detach(device_t dev);
53 
54 /*
55  * Interface to the device switch.
56  */
57 static	d_open_t	aac_disk_open;
58 static	d_close_t	aac_disk_close;
59 static	d_strategy_t	aac_disk_strategy;
60 
61 #define AAC_DISK_CDEV_MAJOR	151
62 
63 static struct cdevsw aac_disk_cdevsw = {
64     /* open */		aac_disk_open,
65     /* close */		aac_disk_close,
66     /* read */		physread,
67     /* write */		physwrite,
68     /* ioctl */		noioctl,
69     /* poll */		nopoll,
70     /* mmap */		nommap,
71     /* strategy */	aac_disk_strategy,
72     /* name */ 		"aacd",
73     /* maj */		AAC_DISK_CDEV_MAJOR,
74     /* dump */		nodump,
75     /* psize */ 	nopsize,
76     /* flags */		D_DISK,
77     /* bmaj */		-1
78 };
79 
80 devclass_t		aac_disk_devclass;
81 static struct cdevsw	aac_disk_disk_cdevsw;
82 #ifdef FREEBSD_4
83 static int		disks_registered = 0;
84 #endif
85 
86 static device_method_t aac_disk_methods[] = {
87     DEVMETHOD(device_probe,	aac_disk_probe),
88     DEVMETHOD(device_attach,	aac_disk_attach),
89     DEVMETHOD(device_detach,	aac_disk_detach),
90     { 0, 0 }
91 };
92 
93 static driver_t aac_disk_driver = {
94     "aacd",
95     aac_disk_methods,
96     sizeof(struct aac_disk)
97 };
98 
99 DRIVER_MODULE(aacd, aac, aac_disk_driver, aac_disk_devclass, 0, 0);
100 
101 /********************************************************************************
102  * Handle open from generic layer.
103  *
104  * This is called by the diskslice code on first open in order to get the
105  * basic device geometry paramters.
106  */
107 static int
108 aac_disk_open(dev_t dev, int flags, int fmt, struct proc *p)
109 {
110     struct aac_disk	*sc = (struct aac_disk *)dev->si_drv1;
111     struct disklabel	*label;
112 
113     debug_called(4);
114 
115     if (sc == NULL)
116 	return (ENXIO);
117 
118     /* check that the controller is up and running */
119     if (sc->ad_controller->aac_state & AAC_STATE_SUSPEND)
120 	return(ENXIO);
121 
122     /* build synthetic label */
123     label = &sc->ad_disk.d_label;
124     bzero(label, sizeof(*label));
125     label->d_type = DTYPE_ESDI;
126     label->d_secsize    = AAC_BLOCK_SIZE;
127     label->d_nsectors   = sc->ad_sectors;
128     label->d_ntracks    = sc->ad_heads;
129     label->d_ncylinders = sc->ad_cylinders;
130     label->d_secpercyl  = sc->ad_sectors * sc->ad_heads;
131     label->d_secperunit = sc->ad_size;
132 
133     sc->ad_flags |= AAC_DISK_OPEN;
134     return (0);
135 }
136 
137 /********************************************************************************
138  * Handle last close of the disk device.
139  */
140 static int
141 aac_disk_close(dev_t dev, int flags, int fmt, struct proc *p)
142 {
143     struct aac_disk	*sc = (struct aac_disk *)dev->si_drv1;
144 
145     debug_called(4);
146 
147     if (sc == NULL)
148 	return (ENXIO);
149 
150     sc->ad_flags &= ~AAC_DISK_OPEN;
151     return (0);
152 }
153 
154 /********************************************************************************
155  * Handle an I/O request.
156  */
157 static void
158 aac_disk_strategy(struct bio *bp)
159 {
160     struct aac_disk	*sc = (struct aac_disk *)bp->bio_dev->si_drv1;
161 
162     debug_called(4);
163 
164     /* bogus disk? */
165     if (sc == NULL) {
166 	bp->bio_flags |= BIO_ERROR;
167 	bp->bio_error = EINVAL;
168 	biodone(bp);
169 	return;
170     }
171 
172     /* do-nothing operation? */
173     if (bp->bio_bcount == 0) {
174 	bp->bio_resid = bp->bio_bcount;
175 	biodone(bp);
176 	return;
177     }
178 
179     /* perform accounting */
180     devstat_start_transaction(&sc->ad_stats);
181 
182     /* pass the bio to the controller - it can work out who we are */
183     aac_submit_bio(bp);
184     return;
185 }
186 
187 /********************************************************************************
188  * Handle completion of an I/O request.
189  */
190 void
191 aac_biodone(struct bio *bp)
192 {
193     struct aac_disk	*sc = (struct aac_disk *)bp->bio_dev->si_drv1;
194 
195     debug_called(4);
196 
197     devstat_end_transaction_bio(&sc->ad_stats, bp);
198     if (bp->bio_flags & BIO_ERROR)
199 	diskerr(bp, (char *)bp->bio_driver1, 0, &sc->ad_label);
200     biodone(bp);
201 }
202 
203 /********************************************************************************
204  * Stub only.
205  */
206 static int
207 aac_disk_probe(device_t dev)
208 {
209 
210     debug_called(4);
211 
212     return (0);
213 }
214 
215 /********************************************************************************
216  * Attach a unit to the controller.
217  */
218 static int
219 aac_disk_attach(device_t dev)
220 {
221     struct aac_disk	*sc = (struct aac_disk *)device_get_softc(dev);
222     int			sgspace;
223     int			maxsg;
224 
225     debug_called(4);
226 
227     /* initialise our softc */
228     sc->ad_controller = (struct aac_softc *)device_get_softc(device_get_parent(dev));
229     sc->ad_container = device_get_ivars(dev);
230     sc->ad_dev = dev;
231 
232     /* require that extended translation be enabled - other drivers read the disk! */
233     sc->ad_size = sc->ad_container->co_mntobj.Capacity;
234     if (sc->ad_size >= (2 * 1024 * 1024)) {		/* 2GB */
235 	sc->ad_heads = 255;
236 	sc->ad_sectors = 63;
237     } else if (sc->ad_size >= (2 * 1024 * 1024)) {	/* 1GB */
238 	sc->ad_heads = 128;
239 	sc->ad_sectors = 32;
240     } else {
241 	sc->ad_heads = 64;
242 	sc->ad_sectors = 32;
243     }
244     sc->ad_cylinders = (sc->ad_size / (sc->ad_heads * sc->ad_sectors));
245 
246     device_printf(dev, "%uMB (%u sectors)\n",
247 		  sc->ad_size / ((1024 * 1024) / AAC_BLOCK_SIZE), sc->ad_size);
248 
249     devstat_add_entry(&sc->ad_stats, "aacd", device_get_unit(dev), AAC_BLOCK_SIZE,
250 		      DEVSTAT_NO_ORDERED_TAGS,
251 		      DEVSTAT_TYPE_STORARRAY | DEVSTAT_TYPE_IF_OTHER,
252 		      DEVSTAT_PRIORITY_ARRAY);
253 
254     /* attach a generic disk device to ourselves */
255     sc->ad_dev_t = disk_create(device_get_unit(dev), &sc->ad_disk, 0, &aac_disk_cdevsw, &aac_disk_disk_cdevsw);
256     sc->ad_dev_t->si_drv1 = sc;
257 #ifdef FREEBSD_4
258     disks_registered++;
259 #endif
260 
261     /*
262      * We can calculate the maximum number of s/g entries based on the size of the
263      * FIB and the command structures packed within it.
264      */
265     sgspace = (sizeof(struct aac_fib) - sizeof(struct aac_fib_header) -
266 	       imax(sizeof(struct aac_blockwrite), sizeof(struct aac_blockread)));
267     maxsg = (sgspace - sizeof(struct aac_sg_table)) / sizeof(struct aac_sg_entry);
268 
269     /* set the maximum I/O size to the theoretical worst maximum allowed by the S/G list size */
270     sc->ad_dev_t->si_iosize_max = (maxsg - 1) * PAGE_SIZE;
271 
272     return (0);
273 }
274 
275 /********************************************************************************
276  * Disconnect ourselves from the system.
277  */
278 static int
279 aac_disk_detach(device_t dev)
280 {
281     struct aac_disk *sc = (struct aac_disk *)device_get_softc(dev);
282 
283     debug_called(4);
284 
285     if (sc->ad_flags & AAC_DISK_OPEN)
286 	return(EBUSY);
287 
288     devstat_remove_entry(&sc->ad_stats);
289 #ifdef FREEBSD_4
290     if (--disks_registered == 0)
291 	cdevsw_remove(&aac_disk_disk_cdevsw);
292 #else
293     disk_destroy(sc->ad_dev_t);
294 #endif
295 
296     return(0);
297 }
298 
299