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