source: mainline/uspace/drv/block/isa-ide/main.c@ c3d9aaf5

Last change on this file since c3d9aaf5 was 443695e, checked in by Jiri Svoboda <jiri@…>, 16 months ago

Basic PCI-IDE driver (no DMA support)

Also, make sure we avoid attaching ISA IDE and PCI IDE
at the same time. For simplicity, use ISA IDE on ISA systems
and PCI IDE on PCI-based systems.

  • Property mode set to 100644
File size: 9.1 KB
Line 
1/*
2 * Copyright (c) 2024 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - 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 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup isa-ide
30 * @{
31 */
32
33/** @file
34 */
35
36#include <assert.h>
37#include <stdio.h>
38#include <errno.h>
39#include <str_error.h>
40#include <ddf/driver.h>
41#include <ddf/log.h>
42#include <device/hw_res_parsed.h>
43
44#include "isa-ide.h"
45#include "main.h"
46
47static errno_t isa_ide_dev_add(ddf_dev_t *dev);
48static errno_t isa_ide_dev_remove(ddf_dev_t *dev);
49static errno_t isa_ide_dev_gone(ddf_dev_t *dev);
50static errno_t isa_ide_fun_online(ddf_fun_t *fun);
51static errno_t isa_ide_fun_offline(ddf_fun_t *fun);
52
53static void isa_ide_connection(ipc_call_t *, void *);
54
55static driver_ops_t driver_ops = {
56 .dev_add = &isa_ide_dev_add,
57 .dev_remove = &isa_ide_dev_remove,
58 .dev_gone = &isa_ide_dev_gone,
59 .fun_online = &isa_ide_fun_online,
60 .fun_offline = &isa_ide_fun_offline
61};
62
63static driver_t isa_ide_driver = {
64 .name = NAME,
65 .driver_ops = &driver_ops
66};
67
68static errno_t isa_ide_get_res(ddf_dev_t *dev, isa_ide_hwres_t *res)
69{
70 async_sess_t *parent_sess;
71 hw_res_list_parsed_t hw_res;
72 hw_res_flags_t flags;
73 errno_t rc;
74
75 parent_sess = ddf_dev_parent_sess_get(dev);
76 if (parent_sess == NULL)
77 return ENOMEM;
78
79 rc = hw_res_get_flags(parent_sess, &flags);
80 if (rc != EOK)
81 return rc;
82
83 /*
84 * Prevent attaching to the legacy ISA IDE register block
85 * on a system with PCI not to conflict with PCI IDE.
86 *
87 * XXX This is a simplification. If we had a PCI-based system without
88 * PCI-IDE or with PCI-IDE disabled and would still like to use
89 * an ISA IDE controller, this would prevent us from doing so.
90 */
91 if (flags & hwf_isa_bridge) {
92 ddf_msg(LVL_NOTE, "Will not attach to PCI/ISA bridge.");
93 return EIO;
94 }
95
96 hw_res_list_parsed_init(&hw_res);
97 rc = hw_res_get_list_parsed(parent_sess, &hw_res, 0);
98 if (rc != EOK)
99 return rc;
100
101 if (hw_res.io_ranges.count != 4) {
102 rc = EINVAL;
103 goto error;
104 }
105
106 /* I/O ranges */
107
108 addr_range_t *cmd1_rng = &hw_res.io_ranges.ranges[0];
109 addr_range_t *ctl1_rng = &hw_res.io_ranges.ranges[1];
110 addr_range_t *cmd2_rng = &hw_res.io_ranges.ranges[2];
111 addr_range_t *ctl2_rng = &hw_res.io_ranges.ranges[3];
112 res->cmd1 = RNGABS(*cmd1_rng);
113 res->ctl1 = RNGABS(*ctl1_rng);
114 res->cmd2 = RNGABS(*cmd2_rng);
115 res->ctl2 = RNGABS(*ctl2_rng);
116
117 if (RNGSZ(*ctl1_rng) < sizeof(ata_ctl_t)) {
118 rc = EINVAL;
119 goto error;
120 }
121
122 if (RNGSZ(*cmd1_rng) < sizeof(ata_cmd_t)) {
123 rc = EINVAL;
124 goto error;
125 }
126
127 if (RNGSZ(*ctl2_rng) < sizeof(ata_ctl_t)) {
128 rc = EINVAL;
129 goto error;
130 }
131
132 if (RNGSZ(*cmd2_rng) < sizeof(ata_cmd_t)) {
133 rc = EINVAL;
134 goto error;
135 }
136
137 /* IRQ */
138 if (hw_res.irqs.count > 0) {
139 res->irq1 = hw_res.irqs.irqs[0];
140 } else {
141 res->irq1 = -1;
142 }
143
144 if (hw_res.irqs.count > 1) {
145 res->irq2 = hw_res.irqs.irqs[1];
146 } else {
147 res->irq2 = -1;
148 }
149
150 return EOK;
151error:
152 hw_res_list_parsed_clean(&hw_res);
153 return rc;
154}
155
156/** Add new device
157 *
158 * @param dev New device
159 * @return EOK on success or an error code.
160 */
161static errno_t isa_ide_dev_add(ddf_dev_t *dev)
162{
163 isa_ide_ctrl_t *ctrl;
164 isa_ide_hwres_t res;
165 errno_t rc;
166
167 rc = isa_ide_get_res(dev, &res);
168 if (rc != EOK) {
169 ddf_msg(LVL_ERROR, "Invalid HW resource configuration.");
170 return EINVAL;
171 }
172
173 ctrl = ddf_dev_data_alloc(dev, sizeof(isa_ide_ctrl_t));
174 if (ctrl == NULL) {
175 ddf_msg(LVL_ERROR, "Failed allocating soft state.");
176 rc = ENOMEM;
177 goto error;
178 }
179
180 ctrl->dev = dev;
181
182 rc = isa_ide_channel_init(ctrl, &ctrl->channel[0], 0, &res);
183 if (rc == ENOENT)
184 goto error;
185
186 rc = isa_ide_channel_init(ctrl, &ctrl->channel[1], 1, &res);
187 if (rc == ENOENT)
188 goto error;
189
190 if (rc != EOK) {
191 ddf_msg(LVL_ERROR, "Failed initializing ATA controller.");
192 rc = EIO;
193 goto error;
194 }
195
196 return EOK;
197error:
198 return rc;
199}
200
201static char *isa_ide_fun_name(isa_ide_channel_t *chan, unsigned idx)
202{
203 char *fun_name;
204
205 if (asprintf(&fun_name, "c%ud%u", chan->chan_id, idx) < 0)
206 return NULL;
207
208 return fun_name;
209}
210
211errno_t isa_ide_fun_create(isa_ide_channel_t *chan, unsigned idx, void *charg)
212{
213 errno_t rc;
214 char *fun_name = NULL;
215 ddf_fun_t *fun = NULL;
216 isa_ide_fun_t *ifun = NULL;
217 bool bound = false;
218
219 fun_name = isa_ide_fun_name(chan, idx);
220 if (fun_name == NULL) {
221 ddf_msg(LVL_ERROR, "Out of memory.");
222 rc = ENOMEM;
223 goto error;
224 }
225
226 fun = ddf_fun_create(chan->ctrl->dev, fun_exposed, fun_name);
227 if (fun == NULL) {
228 ddf_msg(LVL_ERROR, "Failed creating DDF function.");
229 rc = ENOMEM;
230 goto error;
231 }
232
233 /* Allocate soft state */
234 ifun = ddf_fun_data_alloc(fun, sizeof(isa_ide_fun_t));
235 if (ifun == NULL) {
236 ddf_msg(LVL_ERROR, "Failed allocating softstate.");
237 rc = ENOMEM;
238 goto error;
239 }
240
241 ifun->fun = fun;
242 ifun->charg = charg;
243
244 /* Set up a connection handler. */
245 ddf_fun_set_conn_handler(fun, isa_ide_connection);
246
247 rc = ddf_fun_bind(fun);
248 if (rc != EOK) {
249 ddf_msg(LVL_ERROR, "Failed binding DDF function %s: %s",
250 fun_name, str_error(rc));
251 goto error;
252 }
253
254 bound = true;
255
256 rc = ddf_fun_add_to_category(fun, "disk");
257 if (rc != EOK) {
258 ddf_msg(LVL_ERROR, "Failed adding function %s to "
259 "category 'disk': %s", fun_name, str_error(rc));
260 goto error;
261 }
262
263 free(fun_name);
264 return EOK;
265error:
266 if (bound)
267 ddf_fun_unbind(fun);
268 if (fun != NULL)
269 ddf_fun_destroy(fun);
270 if (fun_name != NULL)
271 free(fun_name);
272
273 return rc;
274}
275
276errno_t isa_ide_fun_remove(isa_ide_channel_t *chan, unsigned idx)
277{
278 errno_t rc;
279 char *fun_name;
280 isa_ide_fun_t *ifun = chan->fun[idx];
281
282 fun_name = isa_ide_fun_name(chan, idx);
283 if (fun_name == NULL) {
284 ddf_msg(LVL_ERROR, "Out of memory.");
285 rc = ENOMEM;
286 goto error;
287 }
288
289 ddf_msg(LVL_DEBUG, "isa_ide_fun_remove(%p, '%s')", ifun, fun_name);
290 rc = ddf_fun_offline(ifun->fun);
291 if (rc != EOK) {
292 ddf_msg(LVL_ERROR, "Error offlining function '%s'.", fun_name);
293 goto error;
294 }
295
296 rc = ddf_fun_unbind(ifun->fun);
297 if (rc != EOK) {
298 ddf_msg(LVL_ERROR, "Failed unbinding function '%s'.", fun_name);
299 goto error;
300 }
301
302 ddf_fun_destroy(ifun->fun);
303 free(fun_name);
304 return EOK;
305error:
306 if (fun_name != NULL)
307 free(fun_name);
308 return rc;
309}
310
311errno_t isa_ide_fun_unbind(isa_ide_channel_t *chan, unsigned idx)
312{
313 errno_t rc;
314 char *fun_name;
315 isa_ide_fun_t *ifun = chan->fun[idx];
316
317 fun_name = isa_ide_fun_name(chan, idx);
318 if (fun_name == NULL) {
319 ddf_msg(LVL_ERROR, "Out of memory.");
320 rc = ENOMEM;
321 goto error;
322 }
323
324 ddf_msg(LVL_DEBUG, "isa_ide_fun_unbind(%p, '%s')", ifun, fun_name);
325 rc = ddf_fun_unbind(ifun->fun);
326 if (rc != EOK) {
327 ddf_msg(LVL_ERROR, "Failed unbinding function '%s'.", fun_name);
328 goto error;
329 }
330
331 ddf_fun_destroy(ifun->fun);
332 free(fun_name);
333 return EOK;
334error:
335 if (fun_name != NULL)
336 free(fun_name);
337 return rc;
338}
339
340static errno_t isa_ide_dev_remove(ddf_dev_t *dev)
341{
342 isa_ide_ctrl_t *ctrl = (isa_ide_ctrl_t *)ddf_dev_data_get(dev);
343 errno_t rc;
344
345 ddf_msg(LVL_DEBUG, "isa_ide_dev_remove(%p)", dev);
346
347 rc = isa_ide_channel_fini(&ctrl->channel[0]);
348 if (rc != EOK)
349 return rc;
350
351 rc = isa_ide_channel_fini(&ctrl->channel[1]);
352 if (rc != EOK)
353 return rc;
354
355 return EOK;
356}
357
358static errno_t isa_ide_dev_gone(ddf_dev_t *dev)
359{
360 isa_ide_ctrl_t *ctrl = (isa_ide_ctrl_t *)ddf_dev_data_get(dev);
361 errno_t rc;
362
363 ddf_msg(LVL_DEBUG, "isa_ide_dev_gone(%p)", dev);
364
365 rc = isa_ide_channel_fini(&ctrl->channel[0]);
366 if (rc != EOK)
367 return rc;
368
369 rc = isa_ide_channel_fini(&ctrl->channel[1]);
370 if (rc != EOK)
371 return rc;
372
373 return EOK;
374}
375
376static errno_t isa_ide_fun_online(ddf_fun_t *fun)
377{
378 ddf_msg(LVL_DEBUG, "isa_ide_fun_online()");
379 return ddf_fun_online(fun);
380}
381
382static errno_t isa_ide_fun_offline(ddf_fun_t *fun)
383{
384 ddf_msg(LVL_DEBUG, "isa_ide_fun_offline()");
385 return ddf_fun_offline(fun);
386}
387
388static void isa_ide_connection(ipc_call_t *icall, void *arg)
389{
390 isa_ide_fun_t *ifun;
391
392 ifun = (isa_ide_fun_t *) ddf_fun_data_get((ddf_fun_t *)arg);
393 ata_connection(icall, ifun->charg);
394}
395
396int main(int argc, char *argv[])
397{
398 printf(NAME ": HelenOS ISA IDE device driver\n");
399 ddf_log_init(NAME);
400 return ddf_driver_main(&isa_ide_driver);
401}
402
403/**
404 * @}
405 */
Note: See TracBrowser for help on using the repository browser.