1 | /* This file contains device independent device driver interface.
|
---|
2 | *
|
---|
3 | * Changes:
|
---|
4 | * Jul 25, 2005 added SYS_SIG type for signals (Jorrit N. Herder)
|
---|
5 | * Sep 15, 2004 added SYN_ALARM type for timeouts (Jorrit N. Herder)
|
---|
6 | * Jul 23, 2004 removed kernel dependencies (Jorrit N. Herder)
|
---|
7 | * Apr 02, 1992 constructed from AT wini and floppy driver (Kees J. Bot)
|
---|
8 | *
|
---|
9 | *
|
---|
10 | * The drivers support the following operations (using message format m2):
|
---|
11 | *
|
---|
12 | * m_type DEVICE IO_ENDPT COUNT POSITION ADRRESS
|
---|
13 | * ----------------------------------------------------------------
|
---|
14 | * | DEV_OPEN | device | proc nr | | | |
|
---|
15 | * |------------+---------+---------+---------+---------+---------|
|
---|
16 | * | DEV_CLOSE | device | proc nr | | | |
|
---|
17 | * |------------+---------+---------+---------+---------+---------|
|
---|
18 | * | DEV_READ | device | proc nr | bytes | offset | buf ptr |
|
---|
19 | * |------------+---------+---------+---------+---------+---------|
|
---|
20 | * | DEV_WRITE | device | proc nr | bytes | offset | buf ptr |
|
---|
21 | * |------------+---------+---------+---------+---------+---------|
|
---|
22 | * | DEV_GATHER | device | proc nr | iov len | offset | iov ptr |
|
---|
23 | * |------------+---------+---------+---------+---------+---------|
|
---|
24 | * | DEV_SCATTER| device | proc nr | iov len | offset | iov ptr |
|
---|
25 | * |------------+---------+---------+---------+---------+---------|
|
---|
26 | * | DEV_IOCTL | device | proc nr |func code| | buf ptr |
|
---|
27 | * |------------+---------+---------+---------+---------+---------|
|
---|
28 | * | CANCEL | device | proc nr | r/w | | |
|
---|
29 | * |------------+---------+---------+---------+---------+---------|
|
---|
30 | * | HARD_STOP | | | | | |
|
---|
31 | * ----------------------------------------------------------------
|
---|
32 | *
|
---|
33 | * The file contains one entry point:
|
---|
34 | *
|
---|
35 | * driver_task: called by the device dependent task entry
|
---|
36 | */
|
---|
37 |
|
---|
38 | #include "../drivers.h"
|
---|
39 | #include <sys/ioc_disk.h>
|
---|
40 | #include "driver.h"
|
---|
41 |
|
---|
42 | #if (CHIP == INTEL)
|
---|
43 |
|
---|
44 | #if USE_EXTRA_DMA_BUF && DMA_BUF_SIZE < 2048
|
---|
45 | /* A bit extra scratch for the Adaptec driver. */
|
---|
46 | #define BUF_EXTRA (2048 - DMA_BUF_SIZE)
|
---|
47 | #else
|
---|
48 | #define BUF_EXTRA 0
|
---|
49 | #endif
|
---|
50 |
|
---|
51 | /* Claim space for variables. */
|
---|
52 | PRIVATE u8_t buffer[(unsigned) 2 * DMA_BUF_SIZE + BUF_EXTRA];
|
---|
53 | u8_t *tmp_buf; /* the DMA buffer eventually */
|
---|
54 | phys_bytes tmp_phys; /* phys address of DMA buffer */
|
---|
55 |
|
---|
56 | #else /* CHIP != INTEL */
|
---|
57 |
|
---|
58 | /* Claim space for variables. */
|
---|
59 | u8_t tmp_buf[DMA_BUF_SIZE]; /* the DMA buffer */
|
---|
60 | phys_bytes tmp_phys; /* phys address of DMA buffer */
|
---|
61 |
|
---|
62 | #endif /* CHIP != INTEL */
|
---|
63 |
|
---|
64 | FORWARD _PROTOTYPE( void init_buffer, (void) );
|
---|
65 | FORWARD _PROTOTYPE( int do_rdwt, (struct driver *dr, message *mp) );
|
---|
66 | FORWARD _PROTOTYPE( int do_vrdwt, (struct driver *dr, message *mp) );
|
---|
67 |
|
---|
68 | int device_caller;
|
---|
69 |
|
---|
70 | /*===========================================================================*
|
---|
71 | * driver_task *
|
---|
72 | *===========================================================================*/
|
---|
73 | PUBLIC void driver_task(dp)
|
---|
74 | struct driver *dp; /* Device dependent entry points. */
|
---|
75 | {
|
---|
76 | /* Main program of any device driver task. */
|
---|
77 |
|
---|
78 | int r, proc_nr;
|
---|
79 | message mess;
|
---|
80 |
|
---|
81 | /* Get a DMA buffer. */
|
---|
82 | init_buffer();
|
---|
83 |
|
---|
84 | /* Here is the main loop of the disk task. It waits for a message, carries
|
---|
85 | * it out, and sends a reply.
|
---|
86 | */
|
---|
87 | while (TRUE) {
|
---|
88 |
|
---|
89 | /* Wait for a request to read or write a disk block. */
|
---|
90 | if (receive(ANY, &mess) != OK) continue;
|
---|
91 |
|
---|
92 | device_caller = mess.m_source;
|
---|
93 | proc_nr = mess.IO_ENDPT;
|
---|
94 |
|
---|
95 | /* Now carry out the work. */
|
---|
96 | switch(mess.m_type) {
|
---|
97 | case DEV_OPEN: r = (*dp->dr_open)(dp, &mess); break;
|
---|
98 | case DEV_CLOSE: r = (*dp->dr_close)(dp, &mess); break;
|
---|
99 | case DEV_IOCTL: r = (*dp->dr_ioctl)(dp, &mess); break;
|
---|
100 | case CANCEL: r = (*dp->dr_cancel)(dp, &mess);break;
|
---|
101 | case DEV_SELECT: r = (*dp->dr_select)(dp, &mess);break;
|
---|
102 | case DEV_READ:
|
---|
103 | case DEV_WRITE: r = do_rdwt(dp, &mess); break;
|
---|
104 | case DEV_GATHER:
|
---|
105 | case DEV_SCATTER: r = do_vrdwt(dp, &mess); break;
|
---|
106 |
|
---|
107 | case HARD_INT: /* leftover interrupt or expired timer. */
|
---|
108 | if(dp->dr_hw_int) {
|
---|
109 | (*dp->dr_hw_int)(dp, &mess);
|
---|
110 | }
|
---|
111 | continue;
|
---|
112 | case PROC_EVENT:
|
---|
113 | case SYS_SIG: (*dp->dr_signal)(dp, &mess);
|
---|
114 | continue; /* don't reply */
|
---|
115 | case SYN_ALARM: (*dp->dr_alarm)(dp, &mess);
|
---|
116 | continue; /* don't reply */
|
---|
117 | case DEV_PING: notify(mess.m_source);
|
---|
118 | continue;
|
---|
119 | default:
|
---|
120 | if(dp->dr_other)
|
---|
121 | r = (*dp->dr_other)(dp, &mess);
|
---|
122 | else
|
---|
123 | r = EINVAL;
|
---|
124 | break;
|
---|
125 | }
|
---|
126 |
|
---|
127 | /* Clean up leftover state. */
|
---|
128 | (*dp->dr_cleanup)();
|
---|
129 |
|
---|
130 | /* Finally, prepare and send the reply message. */
|
---|
131 | if (r != EDONTREPLY) {
|
---|
132 | mess.m_type = TASK_REPLY;
|
---|
133 | mess.REP_ENDPT = proc_nr;
|
---|
134 | /* Status is # of bytes transferred or error code. */
|
---|
135 | mess.REP_STATUS = r;
|
---|
136 | send(device_caller, &mess);
|
---|
137 | }
|
---|
138 | }
|
---|
139 | }
|
---|
140 |
|
---|
141 |
|
---|
142 | /*===========================================================================*
|
---|
143 | * init_buffer *
|
---|
144 | *===========================================================================*/
|
---|
145 | PRIVATE void init_buffer()
|
---|
146 | {
|
---|
147 | /* Select a buffer that can safely be used for DMA transfers. It may also
|
---|
148 | * be used to read partition tables and such. Its absolute address is
|
---|
149 | * 'tmp_phys', the normal address is 'tmp_buf'.
|
---|
150 | */
|
---|
151 |
|
---|
152 | #if (CHIP == INTEL)
|
---|
153 | unsigned left;
|
---|
154 |
|
---|
155 | tmp_buf = buffer;
|
---|
156 | sys_umap(SELF, D, (vir_bytes)buffer, (phys_bytes)sizeof(buffer), &tmp_phys);
|
---|
157 |
|
---|
158 | if ((left = dma_bytes_left(tmp_phys)) < DMA_BUF_SIZE) {
|
---|
159 | /* First half of buffer crosses a 64K boundary, can't DMA into that */
|
---|
160 | tmp_buf += left;
|
---|
161 | tmp_phys += left;
|
---|
162 | }
|
---|
163 | #endif /* CHIP == INTEL */
|
---|
164 | }
|
---|
165 |
|
---|
166 | /*===========================================================================*
|
---|
167 | * do_rdwt *
|
---|
168 | *===========================================================================*/
|
---|
169 | PRIVATE int do_rdwt(dp, mp)
|
---|
170 | struct driver *dp; /* device dependent entry points */
|
---|
171 | message *mp; /* pointer to read or write message */
|
---|
172 | {
|
---|
173 | /* Carry out a single read or write request. */
|
---|
174 | iovec_t iovec1;
|
---|
175 | int r, opcode;
|
---|
176 | phys_bytes phys_addr;
|
---|
177 |
|
---|
178 | /* Disk address? Address and length of the user buffer? */
|
---|
179 | if (mp->COUNT < 0) return(EINVAL);
|
---|
180 |
|
---|
181 | /* Check the user buffer. */
|
---|
182 | sys_umap(mp->IO_ENDPT, D, (vir_bytes) mp->ADDRESS, mp->COUNT, &phys_addr);
|
---|
183 | if (phys_addr == 0) return(EFAULT);
|
---|
184 |
|
---|
185 | /* Prepare for I/O. */
|
---|
186 | if ((*dp->dr_prepare)(mp->DEVICE) == NIL_DEV) return(ENXIO);
|
---|
187 |
|
---|
188 | /* Create a one element scatter/gather vector for the buffer. */
|
---|
189 | opcode = mp->m_type == DEV_READ ? DEV_GATHER : DEV_SCATTER;
|
---|
190 | iovec1.iov_addr = (vir_bytes) mp->ADDRESS;
|
---|
191 | iovec1.iov_size = mp->COUNT;
|
---|
192 |
|
---|
193 | /* Transfer bytes from/to the device. */
|
---|
194 | r = (*dp->dr_transfer)(mp->IO_ENDPT, opcode, mp->POSITION, &iovec1, 1);
|
---|
195 |
|
---|
196 | /* Return the number of bytes transferred or an error code. */
|
---|
197 | return(r == OK ? (mp->COUNT - iovec1.iov_size) : r);
|
---|
198 | }
|
---|
199 |
|
---|
200 | /*==========================================================================*
|
---|
201 | * do_vrdwt *
|
---|
202 | *==========================================================================*/
|
---|
203 | PRIVATE int do_vrdwt(dp, mp)
|
---|
204 | struct driver *dp; /* device dependent entry points */
|
---|
205 | message *mp; /* pointer to read or write message */
|
---|
206 | {
|
---|
207 | /* Carry out an device read or write to/from a vector of user addresses.
|
---|
208 | * The "user addresses" are assumed to be safe, i.e. FS transferring to/from
|
---|
209 | * its own buffers, so they are not checked.
|
---|
210 | */
|
---|
211 | static iovec_t iovec[NR_IOREQS];
|
---|
212 | iovec_t *iov;
|
---|
213 | phys_bytes iovec_size;
|
---|
214 | unsigned nr_req;
|
---|
215 | int r;
|
---|
216 |
|
---|
217 | nr_req = mp->COUNT; /* Length of I/O vector */
|
---|
218 |
|
---|
219 | #if 0
|
---|
220 | if (mp->m_source < 0) {
|
---|
221 | /* Called by a task, no need to copy vector. */
|
---|
222 | iov = (iovec_t *) mp->ADDRESS;
|
---|
223 | } else
|
---|
224 | #endif
|
---|
225 | {
|
---|
226 | /* Copy the vector from the caller to kernel space. */
|
---|
227 | if (nr_req > NR_IOREQS) nr_req = NR_IOREQS;
|
---|
228 | iovec_size = (phys_bytes) (nr_req * sizeof(iovec[0]));
|
---|
229 |
|
---|
230 | if (OK != sys_datacopy(mp->m_source, (vir_bytes) mp->ADDRESS,
|
---|
231 | SELF, (vir_bytes) iovec, iovec_size))
|
---|
232 | panic((*dp->dr_name)(),"bad I/O vector by", mp->m_source);
|
---|
233 | iov = iovec;
|
---|
234 | }
|
---|
235 |
|
---|
236 | /* Prepare for I/O. */
|
---|
237 | if ((*dp->dr_prepare)(mp->DEVICE) == NIL_DEV) return(ENXIO);
|
---|
238 |
|
---|
239 | /* Transfer bytes from/to the device. */
|
---|
240 | r = (*dp->dr_transfer)(mp->IO_ENDPT, mp->m_type, mp->POSITION, iov, nr_req);
|
---|
241 |
|
---|
242 | /* Copy the I/O vector back to the caller. */
|
---|
243 | #if 0
|
---|
244 | if (mp->m_source >= 0) {
|
---|
245 | #endif
|
---|
246 | sys_datacopy(SELF, (vir_bytes) iovec,
|
---|
247 | mp->m_source, (vir_bytes) mp->ADDRESS, iovec_size);
|
---|
248 | return(r);
|
---|
249 | }
|
---|
250 |
|
---|
251 | /*===========================================================================*
|
---|
252 | * no_name *
|
---|
253 | *===========================================================================*/
|
---|
254 | PUBLIC char *no_name()
|
---|
255 | {
|
---|
256 | /* Use this default name if there is no specific name for the device. This was
|
---|
257 | * originally done by fetching the name from the task table for this process:
|
---|
258 | * "return(tasktab[proc_number(proc_ptr) + NR_TASKS].name);", but currently a
|
---|
259 | * real "noname" is returned. Perhaps, some system information service can be
|
---|
260 | * queried for a name at a later time.
|
---|
261 | */
|
---|
262 | static char name[] = "noname";
|
---|
263 | return name;
|
---|
264 | }
|
---|
265 |
|
---|
266 | /*============================================================================*
|
---|
267 | * do_nop *
|
---|
268 | *============================================================================*/
|
---|
269 | PUBLIC int do_nop(dp, mp)
|
---|
270 | struct driver *dp;
|
---|
271 | message *mp;
|
---|
272 | {
|
---|
273 | /* Nothing there, or nothing to do. */
|
---|
274 |
|
---|
275 | switch (mp->m_type) {
|
---|
276 | case DEV_OPEN: return(ENODEV);
|
---|
277 | case DEV_CLOSE: return(OK);
|
---|
278 | case DEV_IOCTL: return(ENOTTY);
|
---|
279 | default: return(EIO);
|
---|
280 | }
|
---|
281 | }
|
---|
282 |
|
---|
283 | /*============================================================================*
|
---|
284 | * nop_signal *
|
---|
285 | *============================================================================*/
|
---|
286 | PUBLIC void nop_signal(dp, mp)
|
---|
287 | struct driver *dp;
|
---|
288 | message *mp;
|
---|
289 | {
|
---|
290 | /* Default action for signal is to ignore. */
|
---|
291 | }
|
---|
292 |
|
---|
293 | /*============================================================================*
|
---|
294 | * nop_alarm *
|
---|
295 | *============================================================================*/
|
---|
296 | PUBLIC void nop_alarm(dp, mp)
|
---|
297 | struct driver *dp;
|
---|
298 | message *mp;
|
---|
299 | {
|
---|
300 | /* Ignore the leftover alarm. */
|
---|
301 | }
|
---|
302 |
|
---|
303 | /*===========================================================================*
|
---|
304 | * nop_prepare *
|
---|
305 | *===========================================================================*/
|
---|
306 | PUBLIC struct device *nop_prepare(device)
|
---|
307 | {
|
---|
308 | /* Nothing to prepare for. */
|
---|
309 | return(NIL_DEV);
|
---|
310 | }
|
---|
311 |
|
---|
312 | /*===========================================================================*
|
---|
313 | * nop_cleanup *
|
---|
314 | *===========================================================================*/
|
---|
315 | PUBLIC void nop_cleanup()
|
---|
316 | {
|
---|
317 | /* Nothing to clean up. */
|
---|
318 | }
|
---|
319 |
|
---|
320 | /*===========================================================================*
|
---|
321 | * nop_cancel *
|
---|
322 | *===========================================================================*/
|
---|
323 | PUBLIC int nop_cancel(struct driver *dr, message *m)
|
---|
324 | {
|
---|
325 | /* Nothing to do for cancel. */
|
---|
326 | return(OK);
|
---|
327 | }
|
---|
328 |
|
---|
329 | /*===========================================================================*
|
---|
330 | * nop_select *
|
---|
331 | *===========================================================================*/
|
---|
332 | PUBLIC int nop_select(struct driver *dr, message *m)
|
---|
333 | {
|
---|
334 | /* Nothing to do for select. */
|
---|
335 | return(OK);
|
---|
336 | }
|
---|
337 |
|
---|
338 | /*============================================================================*
|
---|
339 | * do_diocntl *
|
---|
340 | *============================================================================*/
|
---|
341 | PUBLIC int do_diocntl(dp, mp)
|
---|
342 | struct driver *dp;
|
---|
343 | message *mp; /* pointer to ioctl request */
|
---|
344 | {
|
---|
345 | /* Carry out a partition setting/getting request. */
|
---|
346 | struct device *dv;
|
---|
347 | struct partition entry;
|
---|
348 | int s;
|
---|
349 |
|
---|
350 | if (mp->REQUEST != DIOCSETP && mp->REQUEST != DIOCGETP) {
|
---|
351 | if(dp->dr_other) {
|
---|
352 | return dp->dr_other(dp, mp);
|
---|
353 | } else return(ENOTTY);
|
---|
354 | }
|
---|
355 |
|
---|
356 | /* Decode the message parameters. */
|
---|
357 | if ((dv = (*dp->dr_prepare)(mp->DEVICE)) == NIL_DEV) return(ENXIO);
|
---|
358 |
|
---|
359 | if (mp->REQUEST == DIOCSETP) {
|
---|
360 | /* Copy just this one partition table entry. */
|
---|
361 | if (OK != (s=sys_datacopy(mp->IO_ENDPT, (vir_bytes) mp->ADDRESS,
|
---|
362 | SELF, (vir_bytes) &entry, sizeof(entry))))
|
---|
363 | return s;
|
---|
364 | dv->dv_base = entry.base;
|
---|
365 | dv->dv_size = entry.size;
|
---|
366 | } else {
|
---|
367 | /* Return a partition table entry and the geometry of the drive. */
|
---|
368 | entry.base = dv->dv_base;
|
---|
369 | entry.size = dv->dv_size;
|
---|
370 | (*dp->dr_geometry)(&entry);
|
---|
371 | if (OK != (s=sys_datacopy(SELF, (vir_bytes) &entry,
|
---|
372 | mp->IO_ENDPT, (vir_bytes) mp->ADDRESS, sizeof(entry))))
|
---|
373 | return s;
|
---|
374 | }
|
---|
375 | return(OK);
|
---|
376 | }
|
---|