1 /* This file contains the device independent block driver interface.
3 * Block drivers support the following requests. Message format m10 is used.
4 * Field names are prefixed with BDEV_. Separate field names are used for the
5 * "access" and "request" fields.
7 * m_type MINOR COUNT GRANT FLAGS ID POS_LO POS_HI
8 * +--------------+--------+----------+-------+-------+------+------+------+
9 * | BDEV_OPEN | minor | access | | | id | | |
10 * |--------------+--------+----------+-------+-------+------+------+------|
11 * | BDEV_CLOSE | minor | | | | id | | |
12 * |--------------+--------+----------+-------+-------+------+------+------|
13 * | BDEV_READ | minor | bytes | grant | flags | id | position |
14 * |--------------+--------+----------+-------+-------+------+------+------|
15 * | BDEV_WRITE | minor | bytes | grant | flags | id | position |
16 * |--------------+--------+----------+-------+-------+------+------+------|
17 * | BDEV_GATHER | minor | elements | grant | flags | id | position |
18 * |--------------+--------+----------+-------+-------+------+------+------|
19 * | BDEV_SCATTER | minor | elements | grant | flags | id | position |
20 * |--------------+--------+----------+-------+-------+------+------+------|
21 * | BDEV_IOCTL | minor | request | grant | flags | id | | |
22 * -------------------------------------------------------------------------
24 * The following reply message is used for all requests.
27 * +--------------+--------+----------+-------+-------+------+------+------+
28 * | BDEV_REPLY | status | | | | id | | |
29 * -------------------------------------------------------------------------
32 * Oct 16, 2011 split character and block protocol (D.C. van Moolenbroek)
33 * Aug 27, 2011 move common functions into driver.c (A. Welzel)
34 * Jul 25, 2005 added SYS_SIG type for signals (Jorrit N. Herder)
35 * Sep 15, 2004 added SYN_ALARM type for timeouts (Jorrit N. Herder)
36 * Jul 23, 2004 removed kernel dependencies (Jorrit N. Herder)
37 * Apr 02, 1992 constructed from AT wini and floppy driver (Kees J. Bot)
40 #include <minix/drivers.h>
41 #include <minix/blockdriver.h>
43 #include <sys/ioc_block.h>
44 #include <sys/ioc_disk.h>
50 /* Management data for opened devices. */
51 static int open_devs
[MAX_NR_OPEN_DEVICES
];
52 static int next_open_devs_slot
= 0;
54 /*===========================================================================*
56 *===========================================================================*/
57 static void clear_open_devs(void)
59 /* Reset the set of previously opened minor devices. */
60 next_open_devs_slot
= 0;
63 /*===========================================================================*
65 *===========================================================================*/
66 static int is_open_dev(int device
)
68 /* Check whether the given minor device has previously been opened. */
71 for (i
= 0; i
< next_open_devs_slot
; i
++)
72 if (open_devs
[i
] == device
)
78 /*===========================================================================*
80 *===========================================================================*/
81 static void set_open_dev(int device
)
83 /* Mark the given minor device as having been opened. */
85 if (next_open_devs_slot
>= MAX_NR_OPEN_DEVICES
)
86 panic("out of slots for open devices");
88 open_devs
[next_open_devs_slot
] = device
;
89 next_open_devs_slot
++;
92 /*===========================================================================*
93 * blockdriver_announce *
94 *===========================================================================*/
95 void blockdriver_announce(int type
)
97 /* Announce we are up after a fresh start or a restart. */
99 char key
[DS_MAX_KEYLEN
];
100 char label
[DS_MAX_KEYLEN
];
101 char *driver_prefix
= "drv.blk.";
103 /* Callers are allowed to use sendrec to communicate with drivers.
104 * For this reason, there may blocked callers when a driver restarts.
105 * Ask the kernel to unblock them (if any). Note that most block drivers
106 * will not restart statefully, and thus will skip this code.
108 if (type
== SEF_INIT_RESTART
) {
110 if ((r
= sys_statectl(SYS_STATE_CLEAR_IPC_REFS
)) != OK
)
111 panic("blockdriver_init: sys_statectl failed: %d", r
);
115 /* Publish a driver up event. */
116 if ((r
= ds_retrieve_label_name(label
, getprocnr())) != OK
)
117 panic("blockdriver_init: unable to get own label: %d", r
);
119 snprintf(key
, DS_MAX_KEYLEN
, "%s%s", driver_prefix
, label
);
120 if ((r
= ds_publish_u32(key
, DS_DRIVER_UP
, DSF_OVERWRITE
)) != OK
)
121 panic("blockdriver_init: unable to publish driver up event: %d", r
);
123 /* Expect an open for any device before serving regular driver requests. */
126 /* Initialize or reset the message queue. */
130 /*===========================================================================*
131 * blockdriver_reply *
132 *===========================================================================*/
133 void blockdriver_reply(message
*m_ptr
, int ipc_status
, int reply
)
135 /* Reply to a block request sent to the driver. */
140 if (reply
== EDONTREPLY
)
143 caller_e
= m_ptr
->m_source
;
146 memset(m_ptr
, 0, sizeof(*m_ptr
));
148 m_ptr
->m_type
= BDEV_REPLY
;
149 m_ptr
->BDEV_STATUS
= reply
;
152 /* If we would block sending the message, send it asynchronously. The NOREPLY
153 * flag is set because the caller may also issue a SENDREC (mixing sync and
154 * async comm), and the asynchronous reply could otherwise end up satisfying
155 * the SENDREC's receive part, after which our next SENDNB call would fail.
157 if (IPC_STATUS_CALL(ipc_status
) == SENDREC
)
158 r
= sendnb(caller_e
, m_ptr
);
160 r
= asynsend3(caller_e
, m_ptr
, AMF_NOREPLY
);
163 printf("blockdriver_reply: unable to send reply to %d: %d\n",
167 /*===========================================================================*
169 *===========================================================================*/
170 static int do_open(struct blockdriver
*bdp
, message
*mp
)
172 /* Open a minor device. */
174 return (*bdp
->bdr_open
)(mp
->BDEV_MINOR
, mp
->BDEV_ACCESS
);
177 /*===========================================================================*
179 *===========================================================================*/
180 static int do_close(struct blockdriver
*bdp
, message
*mp
)
182 /* Close a minor device. */
184 return (*bdp
->bdr_close
)(mp
->BDEV_MINOR
);
187 /*===========================================================================*
189 *===========================================================================*/
190 static int do_rdwt(struct blockdriver
*bdp
, message
*mp
)
192 /* Carry out a single read or write request. */
198 /* Disk address? Address and length of the user buffer? */
199 if (mp
->BDEV_COUNT
< 0) return EINVAL
;
201 /* Create a one element scatter/gather vector for the buffer. */
202 iovec1
.iov_addr
= mp
->BDEV_GRANT
;
203 iovec1
.iov_size
= mp
->BDEV_COUNT
;
205 /* Transfer bytes from/to the device. */
206 do_write
= (mp
->m_type
== BDEV_WRITE
);
207 position
= make64(mp
->BDEV_POS_LO
, mp
->BDEV_POS_HI
);
209 r
= (*bdp
->bdr_transfer
)(mp
->BDEV_MINOR
, do_write
, position
, mp
->m_source
,
210 &iovec1
, 1, mp
->BDEV_FLAGS
);
212 /* Return the number of bytes transferred or an error code. */
216 /*===========================================================================*
218 *===========================================================================*/
219 static int do_vrdwt(struct blockdriver
*bdp
, message
*mp
, thread_id_t id
)
221 /* Carry out an device read or write to/from a vector of buffers. */
222 iovec_t iovec
[NR_IOREQS
];
228 /* Copy the vector from the caller to kernel space. */
229 nr_req
= mp
->BDEV_COUNT
; /* Length of I/O vector */
230 if (nr_req
> NR_IOREQS
) nr_req
= NR_IOREQS
;
232 if (OK
!= sys_safecopyfrom(mp
->m_source
, (vir_bytes
) mp
->BDEV_GRANT
,
233 0, (vir_bytes
) iovec
, nr_req
* sizeof(iovec
[0]))) {
234 printf("blockdriver: bad I/O vector by: %d\n", mp
->m_source
);
238 /* Check for overflow condition, and update the size for block tracing. */
239 for (i
= size
= 0; i
< nr_req
; i
++) {
240 if ((ssize_t
) (size
+ iovec
[i
].iov_size
) < size
) return EINVAL
;
241 size
+= iovec
[i
].iov_size
;
244 trace_setsize(id
, size
);
246 /* Transfer bytes from/to the device. */
247 do_write
= (mp
->m_type
== BDEV_SCATTER
);
248 position
= make64(mp
->BDEV_POS_LO
, mp
->BDEV_POS_HI
);
250 r
= (*bdp
->bdr_transfer
)(mp
->BDEV_MINOR
, do_write
, position
, mp
->m_source
,
251 iovec
, nr_req
, mp
->BDEV_FLAGS
);
253 /* Return the number of bytes transferred or an error code. */
257 /*===========================================================================*
259 *===========================================================================*/
260 static int do_dioctl(struct blockdriver
*bdp
, dev_t minor
,
261 unsigned int request
, endpoint_t endpt
, cp_grant_id_t grant
)
263 /* Carry out a disk-specific I/O control request. */
265 struct partition entry
;
270 /* Copy just this one partition table entry. */
271 r
= sys_safecopyfrom(endpt
, grant
, 0, (vir_bytes
) &entry
,
276 if ((dv
= (*bdp
->bdr_part
)(minor
)) == NULL
)
278 dv
->dv_base
= entry
.base
;
279 dv
->dv_size
= entry
.size
;
284 /* Return a partition table entry and the geometry of the drive. */
285 if ((dv
= (*bdp
->bdr_part
)(minor
)) == NULL
)
287 entry
.base
= dv
->dv_base
;
288 entry
.size
= dv
->dv_size
;
289 if (bdp
->bdr_geometry
) {
290 (*bdp
->bdr_geometry
)(minor
, &entry
);
292 /* The driver doesn't care -- make up fake geometry. */
293 entry
.cylinders
= div64u(entry
.size
, SECTOR_SIZE
);
298 r
= sys_safecopyto(endpt
, grant
, 0, (vir_bytes
) &entry
, sizeof(entry
));
306 /*===========================================================================*
308 *===========================================================================*/
309 static int do_ioctl(struct blockdriver
*bdp
, message
*mp
)
311 /* Carry out an I/O control request. We forward block trace control requests
312 * to the tracing module, and handle setting/getting partitions when the driver
313 * has specified that it is a disk driver.
316 unsigned int request
;
320 minor
= mp
->BDEV_MINOR
;
321 request
= mp
->BDEV_REQUEST
;
322 grant
= mp
->BDEV_GRANT
;
328 /* Block trace control. */
329 r
= trace_ctl(minor
, request
, mp
->m_source
, grant
);
335 /* Handle disk-specific IOCTLs only for disk-type drivers. */
336 if (bdp
->bdr_type
== BLOCKDRIVER_TYPE_DISK
) {
337 /* Disk partition control. */
338 r
= do_dioctl(bdp
, minor
, request
, mp
->m_source
, grant
);
346 r
= (*bdp
->bdr_ioctl
)(minor
, request
, mp
->m_source
, grant
);
354 /*===========================================================================*
355 * blockdriver_handle_notify *
356 *===========================================================================*/
357 void blockdriver_handle_notify(struct blockdriver
*bdp
, message
*m_ptr
)
359 /* Take care of the notifications (interrupt and clock messages) by calling
360 * the appropiate callback functions. Never send a reply.
363 /* Call the appropriate function for this notification. */
364 switch (_ENDPOINT_P(m_ptr
->m_source
)) {
367 (*bdp
->bdr_intr
)(m_ptr
->NOTIFY_ARG
);
372 (*bdp
->bdr_alarm
)(m_ptr
->NOTIFY_TIMESTAMP
);
377 (void) (*bdp
->bdr_other
)(m_ptr
);
381 /*===========================================================================*
382 * blockdriver_handle_request *
383 *===========================================================================*/
384 int blockdriver_handle_request(struct blockdriver
*bdp
, message
*m_ptr
,
387 /* Call the appropiate driver function, based on the type of request. Return
388 * the result code that is to be sent back to the caller, or EDONTREPLY if no
389 * reply is to be sent.
393 /* We might get spurious requests if the driver has been restarted. Deny any
394 * requests on devices that have not previously been opened, signaling the
395 * caller that something went wrong.
397 if (IS_BDEV_RQ(m_ptr
->m_type
) && !is_open_dev(m_ptr
->BDEV_MINOR
)) {
398 /* Reply ERESTART to spurious requests for unopened devices. */
399 if (m_ptr
->m_type
!= BDEV_OPEN
)
402 /* Mark the device as opened otherwise. */
403 set_open_dev(m_ptr
->BDEV_MINOR
);
406 trace_start(id
, m_ptr
);
408 /* Call the appropriate function(s) for this request. */
409 switch (m_ptr
->m_type
) {
410 case BDEV_OPEN
: r
= do_open(bdp
, m_ptr
); break;
411 case BDEV_CLOSE
: r
= do_close(bdp
, m_ptr
); break;
413 case BDEV_WRITE
: r
= do_rdwt(bdp
, m_ptr
); break;
415 case BDEV_SCATTER
: r
= do_vrdwt(bdp
, m_ptr
, id
); break;
416 case BDEV_IOCTL
: r
= do_ioctl(bdp
, m_ptr
); break;
419 r
= bdp
->bdr_other(m_ptr
);
424 /* Let the driver perform any cleanup. */
425 if (bdp
->bdr_cleanup
!= NULL
)
426 (*bdp
->bdr_cleanup
)();