leave out debug message
[minix3.git] / drivers / libdriver / driver.c
blob6f28bb2b920ed18a71a629b8155f50646506b404
1 /* This file contains device independent device driver interface.
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)
10 * The drivers support the following operations (using message format m2):
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 * | DEV_*_S | variants using safecopies of above |
33 * ----------------------------------------------------------------
35 * The file contains one entry point:
37 * driver_task: called by the device dependent task entry
41 #include "../drivers.h"
42 #include <sys/ioc_disk.h>
43 #include <minix/mq.h>
44 #include "driver.h"
46 #if (CHIP == INTEL)
48 #if USE_EXTRA_DMA_BUF && DMA_BUF_SIZE < 2048
49 /* A bit extra scratch for the Adaptec driver. */
50 #define BUF_EXTRA (2048 - DMA_BUF_SIZE)
51 #else
52 #define BUF_EXTRA 0
53 #endif
55 /* Claim space for variables. */
56 PRIVATE u8_t buffer[(unsigned) 2 * DMA_BUF_SIZE + BUF_EXTRA];
57 u8_t *tmp_buf; /* the DMA buffer eventually */
58 phys_bytes tmp_phys; /* phys address of DMA buffer */
60 #else /* CHIP != INTEL */
62 /* Claim space for variables. */
63 u8_t tmp_buf[DMA_BUF_SIZE]; /* the DMA buffer */
64 phys_bytes tmp_phys; /* phys address of DMA buffer */
66 #endif /* CHIP != INTEL */
68 FORWARD _PROTOTYPE( void init_buffer, (void) );
69 FORWARD _PROTOTYPE( int do_rdwt, (struct driver *dr, message *mp, int safe) );
70 FORWARD _PROTOTYPE( int do_vrdwt, (struct driver *dr, message *mp, int safe) );
72 int device_caller;
73 PRIVATE mq_t *queue_head = NULL;
75 /*===========================================================================*
76 * driver_task *
77 *===========================================================================*/
78 PUBLIC void driver_task(dp)
79 struct driver *dp; /* Device dependent entry points. */
81 /* Main program of any device driver task. */
83 int r, proc_nr;
84 message mess;
86 /* Init MQ library. */
87 mq_init();
89 /* Get a DMA buffer. */
90 init_buffer();
92 /* Here is the main loop of the disk task. It waits for a message, carries
93 * it out, and sends a reply.
95 while (TRUE) {
96 /* Any queued messages? Oldest are at the head. */
97 if(queue_head) {
98 mq_t *mq;
99 mq = queue_head;
100 memcpy(&mess, &mq->mq_mess, sizeof(mess));
101 queue_head = queue_head->mq_next;
102 mq_free(mq);
103 } else {
104 int s;
105 /* Wait for a request to read or write a disk block. */
106 if ((s=receive(ANY, &mess)) != OK)
107 panic((*dp->dr_name)(),"receive() failed", s);
110 device_caller = mess.m_source;
111 proc_nr = mess.IO_ENDPT;
113 /* Now carry out the work. */
114 switch(mess.m_type) {
115 case DEV_OPEN: r = (*dp->dr_open)(dp, &mess); break;
116 case DEV_CLOSE: r = (*dp->dr_close)(dp, &mess); break;
117 #ifdef DEV_IOCTL
118 case DEV_IOCTL: r = (*dp->dr_ioctl)(dp, &mess, 0); break;
119 #endif
120 case DEV_IOCTL_S: r = (*dp->dr_ioctl)(dp, &mess, 1); break;
121 case CANCEL: r = (*dp->dr_cancel)(dp, &mess);break;
122 case DEV_SELECT: r = (*dp->dr_select)(dp, &mess);break;
123 #ifdef DEV_READ
124 case DEV_READ:
125 case DEV_WRITE: r = do_rdwt(dp, &mess, 0); break;
126 #endif
127 case DEV_READ_S:
128 case DEV_WRITE_S: r = do_rdwt(dp, &mess, 1); break;
129 #ifdef DEV_GATHER
130 case DEV_GATHER:
131 case DEV_SCATTER: r = do_vrdwt(dp, &mess, 0); break;
132 #endif
133 case DEV_GATHER_S:
134 case DEV_SCATTER_S: r = do_vrdwt(dp, &mess, 1); break;
136 case HARD_INT: /* leftover interrupt or expired timer. */
137 if(dp->dr_hw_int) {
138 (*dp->dr_hw_int)(dp, &mess);
140 continue;
141 case PROC_EVENT:
142 case SYS_SIG: (*dp->dr_signal)(dp, &mess);
143 continue; /* don't reply */
144 case SYN_ALARM: (*dp->dr_alarm)(dp, &mess);
145 continue; /* don't reply */
146 case DEV_PING: notify(mess.m_source);
147 continue;
148 default:
149 if(dp->dr_other)
150 r = (*dp->dr_other)(dp, &mess, 0);
151 else
152 r = EINVAL;
153 break;
156 /* Clean up leftover state. */
157 (*dp->dr_cleanup)();
159 /* Finally, prepare and send the reply message. */
160 if (r != EDONTREPLY) {
161 mess.m_type = TASK_REPLY;
162 mess.REP_ENDPT = proc_nr;
163 /* Status is # of bytes transferred or error code. */
164 mess.REP_STATUS = r;
165 send(device_caller, &mess);
171 /*===========================================================================*
172 * init_buffer *
173 *===========================================================================*/
174 PRIVATE void init_buffer()
176 /* Select a buffer that can safely be used for DMA transfers. It may also
177 * be used to read partition tables and such. Its absolute address is
178 * 'tmp_phys', the normal address is 'tmp_buf'.
181 #if (CHIP == INTEL)
182 unsigned left;
184 tmp_buf = buffer;
185 sys_umap(SELF, D, (vir_bytes)buffer, (phys_bytes)sizeof(buffer), &tmp_phys);
187 if ((left = dma_bytes_left(tmp_phys)) < DMA_BUF_SIZE) {
188 /* First half of buffer crosses a 64K boundary, can't DMA into that */
189 tmp_buf += left;
190 tmp_phys += left;
192 #endif /* CHIP == INTEL */
195 /*===========================================================================*
196 * do_rdwt *
197 *===========================================================================*/
198 PRIVATE int do_rdwt(dp, mp, safe)
199 struct driver *dp; /* device dependent entry points */
200 message *mp; /* pointer to read or write message */
201 int safe; /* use safecopies? */
203 /* Carry out a single read or write request. */
204 iovec_t iovec1;
205 int r, opcode;
206 phys_bytes phys_addr;
207 u64_t position;
209 /* Disk address? Address and length of the user buffer? */
210 if (mp->COUNT < 0) return(EINVAL);
212 /* Check the user buffer (not relevant for safe copies). */
213 if(!safe) {
214 sys_umap(mp->IO_ENDPT, D, (vir_bytes) mp->ADDRESS, mp->COUNT, &phys_addr);
215 if (phys_addr == 0) return(EFAULT);
218 /* Prepare for I/O. */
219 if ((*dp->dr_prepare)(mp->DEVICE) == NIL_DEV) return(ENXIO);
221 /* Create a one element scatter/gather vector for the buffer. */
223 #ifdef DEV_READ
224 mp->m_type == DEV_READ ||
225 #endif
226 mp->m_type == DEV_READ_S) opcode = DEV_GATHER_S;
227 else opcode = DEV_SCATTER_S;
229 iovec1.iov_addr = (vir_bytes) mp->ADDRESS;
230 iovec1.iov_size = mp->COUNT;
232 /* Transfer bytes from/to the device. */
233 position= make64(mp->POSITION, mp->HIGHPOS);
234 r = (*dp->dr_transfer)(mp->IO_ENDPT, opcode, position, &iovec1, 1, safe);
236 /* Return the number of bytes transferred or an error code. */
237 return(r == OK ? (mp->COUNT - iovec1.iov_size) : r);
240 /*==========================================================================*
241 * do_vrdwt *
242 *==========================================================================*/
243 PRIVATE int do_vrdwt(dp, mp, safe)
244 struct driver *dp; /* device dependent entry points */
245 message *mp; /* pointer to read or write message */
246 int safe; /* use safecopies? */
248 /* Carry out an device read or write to/from a vector of user addresses.
249 * The "user addresses" are assumed to be safe, i.e. FS transferring to/from
250 * its own buffers, so they are not checked.
252 static iovec_t iovec[NR_IOREQS];
253 iovec_t *iov;
254 phys_bytes iovec_size;
255 unsigned nr_req;
256 int r, j, opcode;
257 u64_t position;
259 nr_req = mp->COUNT; /* Length of I/O vector */
262 /* Copy the vector from the caller to kernel space. */
263 if (nr_req > NR_IOREQS) nr_req = NR_IOREQS;
264 iovec_size = (phys_bytes) (nr_req * sizeof(iovec[0]));
266 if(safe) {
267 if (OK != sys_safecopyfrom(mp->m_source, (vir_bytes) mp->IO_GRANT,
268 0, (vir_bytes) iovec, iovec_size, D)) {
269 panic((*dp->dr_name)(),"bad (safe) I/O vector by", mp->m_source);
271 } else {
272 if (OK != sys_datacopy(mp->m_source, (vir_bytes) mp->ADDRESS,
273 SELF, (vir_bytes) iovec, iovec_size)) {
274 panic((*dp->dr_name)(),"bad I/O vector by", mp->m_source);
278 iov = iovec;
281 /* Prepare for I/O. */
282 if ((*dp->dr_prepare)(mp->DEVICE) == NIL_DEV) return(ENXIO);
284 /* Transfer bytes from/to the device. */
285 opcode = mp->m_type;
286 position= make64(mp->POSITION, mp->HIGHPOS);
287 r = (*dp->dr_transfer)(mp->IO_ENDPT, opcode, position, iov,
288 nr_req, safe);
290 /* Copy the I/O vector back to the caller. */
291 if(safe) {
292 if (OK != sys_safecopyto(mp->m_source, (vir_bytes) mp->IO_GRANT,
293 0, (vir_bytes) iovec, iovec_size, D)) {
294 panic((*dp->dr_name)(),"couldn't return I/O vector", mp->m_source);
296 } else {
297 sys_datacopy(SELF, (vir_bytes) iovec,
298 mp->m_source, (vir_bytes) mp->ADDRESS, iovec_size);
301 return(r);
304 /*===========================================================================*
305 * no_name *
306 *===========================================================================*/
307 PUBLIC char *no_name()
309 /* Use this default name if there is no specific name for the device. This was
310 * originally done by fetching the name from the task table for this process:
311 * "return(tasktab[proc_number(proc_ptr) + NR_TASKS].name);", but currently a
312 * real "noname" is returned. Perhaps, some system information service can be
313 * queried for a name at a later time.
315 static char name[] = "noname";
316 return name;
319 /*============================================================================*
320 * do_nop *
321 *============================================================================*/
322 PUBLIC int do_nop(dp, mp)
323 struct driver *dp;
324 message *mp;
326 /* Nothing there, or nothing to do. */
328 switch (mp->m_type) {
329 case DEV_OPEN: return(ENODEV);
330 case DEV_CLOSE: return(OK);
331 case DEV_IOCTL_S:
332 #ifdef DEV_IOCTL
333 case DEV_IOCTL: return(ENOTTY);
334 #endif
335 default: printf("nop: ignoring code %d\n", mp->m_type); return(EIO);
339 /*============================================================================*
340 * nop_ioctl *
341 *============================================================================*/
342 PUBLIC int nop_ioctl(dp, mp, safe)
343 struct driver *dp;
344 message *mp;
345 int safe;
347 return(ENOTTY);
350 /*============================================================================*
351 * nop_signal *
352 *============================================================================*/
353 PUBLIC void nop_signal(dp, mp)
354 struct driver *dp;
355 message *mp;
357 /* Default action for signal is to ignore. */
360 /*============================================================================*
361 * nop_alarm *
362 *============================================================================*/
363 PUBLIC void nop_alarm(dp, mp)
364 struct driver *dp;
365 message *mp;
367 /* Ignore the leftover alarm. */
370 /*===========================================================================*
371 * nop_prepare *
372 *===========================================================================*/
373 PUBLIC struct device *nop_prepare(device)
375 /* Nothing to prepare for. */
376 return(NIL_DEV);
379 /*===========================================================================*
380 * nop_cleanup *
381 *===========================================================================*/
382 PUBLIC void nop_cleanup()
384 /* Nothing to clean up. */
387 /*===========================================================================*
388 * nop_cancel *
389 *===========================================================================*/
390 PUBLIC int nop_cancel(struct driver *dr, message *m)
392 /* Nothing to do for cancel. */
393 return(OK);
396 /*===========================================================================*
397 * nop_select *
398 *===========================================================================*/
399 PUBLIC int nop_select(struct driver *dr, message *m)
401 /* Nothing to do for select. */
402 return(OK);
405 /*============================================================================*
406 * do_diocntl *
407 *============================================================================*/
408 PUBLIC int do_diocntl(dp, mp, safe)
409 struct driver *dp;
410 message *mp; /* pointer to ioctl request */
411 int safe; /* addresses or grants? */
413 /* Carry out a partition setting/getting request. */
414 struct device *dv;
415 struct partition entry;
416 int s;
418 if (mp->REQUEST != DIOCSETP && mp->REQUEST != DIOCGETP) {
419 if(dp->dr_other) {
420 return dp->dr_other(dp, mp, safe);
421 } else return(ENOTTY);
424 /* Decode the message parameters. */
425 if ((dv = (*dp->dr_prepare)(mp->DEVICE)) == NIL_DEV) return(ENXIO);
427 if (mp->REQUEST == DIOCSETP) {
428 /* Copy just this one partition table entry. */
429 if(safe) {
430 s=sys_safecopyfrom(mp->IO_ENDPT, (vir_bytes) mp->IO_GRANT,
431 0, (vir_bytes) &entry, sizeof(entry), D);
432 } else{
433 s=sys_datacopy(mp->IO_ENDPT, (vir_bytes) mp->ADDRESS,
434 SELF, (vir_bytes) &entry, sizeof(entry));
436 if(s != OK)
437 return s;
438 dv->dv_base = entry.base;
439 dv->dv_size = entry.size;
440 } else {
441 /* Return a partition table entry and the geometry of the drive. */
442 entry.base = dv->dv_base;
443 entry.size = dv->dv_size;
444 (*dp->dr_geometry)(&entry);
445 if(safe) {
446 s=sys_safecopyto(mp->IO_ENDPT, (vir_bytes) mp->IO_GRANT,
447 0, (vir_bytes) &entry, sizeof(entry), D);
448 } else {
449 s=sys_datacopy(SELF, (vir_bytes) &entry,
450 mp->IO_ENDPT, (vir_bytes) mp->ADDRESS, sizeof(entry));
452 if (OK != s)
453 return s;
455 return(OK);
458 /*===========================================================================*
459 * mq_queue *
460 *===========================================================================*/
461 PUBLIC int mq_queue(message *m)
463 mq_t *mq, *mi;
465 if(!(mq = mq_get()))
466 panic("libdriver","mq_queue: mq_get failed", NO_NUM);
467 memcpy(&mq->mq_mess, m, sizeof(mq->mq_mess));
468 mq->mq_next = NULL;
469 if(!queue_head) {
470 queue_head = mq;
471 } else {
472 for(mi = queue_head; mi->mq_next; mi = mi->mq_next)
474 mi->mq_next = mq;
477 return OK;