2 * Copyright (c) 2024 Jiri Svoboda
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
29 /** @addtogroup pci-ide
39 #include <str_error.h>
40 #include <ddf/driver.h>
42 #include <device/hw_res_parsed.h>
45 #include "pci-ide_hw.h"
48 static errno_t
pci_ide_dev_add(ddf_dev_t
*dev
);
49 static errno_t
pci_ide_dev_remove(ddf_dev_t
*dev
);
50 static errno_t
pci_ide_dev_gone(ddf_dev_t
*dev
);
51 static errno_t
pci_ide_fun_online(ddf_fun_t
*fun
);
52 static errno_t
pci_ide_fun_offline(ddf_fun_t
*fun
);
54 static void pci_ide_connection(ipc_call_t
*, void *);
56 static driver_ops_t driver_ops
= {
57 .dev_add
= &pci_ide_dev_add
,
58 .dev_remove
= &pci_ide_dev_remove
,
59 .dev_gone
= &pci_ide_dev_gone
,
60 .fun_online
= &pci_ide_fun_online
,
61 .fun_offline
= &pci_ide_fun_offline
64 static driver_t pci_ide_driver
= {
66 .driver_ops
= &driver_ops
69 static errno_t
pci_ide_get_res(ddf_dev_t
*dev
, pci_ide_hwres_t
*res
)
71 async_sess_t
*parent_sess
;
72 hw_res_list_parsed_t hw_res
;
75 parent_sess
= ddf_dev_parent_sess_get(dev
);
76 if (parent_sess
== NULL
)
79 hw_res_list_parsed_init(&hw_res
);
80 rc
= hw_res_get_list_parsed(parent_sess
, &hw_res
, 0);
84 if (hw_res
.io_ranges
.count
!= 1) {
89 /* Legacty ISA I/O ranges are fixed */
91 res
->cmd1
= pci_ide_ata_cmd_p
;
92 res
->ctl1
= pci_ide_ata_ctl_p
;
93 res
->cmd2
= pci_ide_ata_cmd_s
;
94 res
->ctl2
= pci_ide_ata_ctl_s
;
97 addr_range_t
*bmregs_rng
= &hw_res
.io_ranges
.ranges
[0];
98 res
->bmregs
= RNGABS(*bmregs_rng
);
100 ddf_msg(LVL_NOTE
, "sizes: %zu", RNGSZ(*bmregs_rng
));
102 if (RNGSZ(*bmregs_rng
) < sizeof(pci_ide_regs_t
)) {
108 if (hw_res
.irqs
.count
> 0) {
109 res
->irq1
= hw_res
.irqs
.irqs
[0];
114 if (hw_res
.irqs
.count
> 1) {
115 res
->irq2
= hw_res
.irqs
.irqs
[1];
122 hw_res_list_parsed_clean(&hw_res
);
128 * @param dev New device
129 * @return EOK on success or an error code.
131 static errno_t
pci_ide_dev_add(ddf_dev_t
*dev
)
133 pci_ide_ctrl_t
*ctrl
;
137 rc
= pci_ide_get_res(dev
, &res
);
139 ddf_msg(LVL_ERROR
, "Invalid HW resource configuration.");
143 ctrl
= ddf_dev_data_alloc(dev
, sizeof(pci_ide_ctrl_t
));
145 ddf_msg(LVL_ERROR
, "Failed allocating soft state.");
152 rc
= pci_ide_ctrl_init(ctrl
, &res
);
156 rc
= pci_ide_channel_init(ctrl
, &ctrl
->channel
[0], 0, &res
);
160 rc
= pci_ide_channel_init(ctrl
, &ctrl
->channel
[1], 1, &res
);
165 ddf_msg(LVL_ERROR
, "Failed initializing ATA controller.");
175 static char *pci_ide_fun_name(pci_ide_channel_t
*chan
, unsigned idx
)
179 if (asprintf(&fun_name
, "c%ud%u", chan
->chan_id
, idx
) < 0)
185 errno_t
pci_ide_fun_create(pci_ide_channel_t
*chan
, unsigned idx
, void *charg
)
188 char *fun_name
= NULL
;
189 ddf_fun_t
*fun
= NULL
;
190 pci_ide_fun_t
*ifun
= NULL
;
193 fun_name
= pci_ide_fun_name(chan
, idx
);
194 if (fun_name
== NULL
) {
195 ddf_msg(LVL_ERROR
, "Out of memory.");
200 fun
= ddf_fun_create(chan
->ctrl
->dev
, fun_exposed
, fun_name
);
202 ddf_msg(LVL_ERROR
, "Failed creating DDF function.");
207 /* Allocate soft state */
208 ifun
= ddf_fun_data_alloc(fun
, sizeof(pci_ide_fun_t
));
210 ddf_msg(LVL_ERROR
, "Failed allocating softstate.");
218 /* Set up a connection handler. */
219 ddf_fun_set_conn_handler(fun
, pci_ide_connection
);
221 rc
= ddf_fun_bind(fun
);
223 ddf_msg(LVL_ERROR
, "Failed binding DDF function %s: %s",
224 fun_name
, str_error(rc
));
230 rc
= ddf_fun_add_to_category(fun
, "disk");
232 ddf_msg(LVL_ERROR
, "Failed adding function %s to "
233 "category 'disk': %s", fun_name
, str_error(rc
));
243 ddf_fun_destroy(fun
);
244 if (fun_name
!= NULL
)
250 errno_t
pci_ide_fun_remove(pci_ide_channel_t
*chan
, unsigned idx
)
254 pci_ide_fun_t
*ifun
= chan
->fun
[idx
];
256 fun_name
= pci_ide_fun_name(chan
, idx
);
257 if (fun_name
== NULL
) {
258 ddf_msg(LVL_ERROR
, "Out of memory.");
263 ddf_msg(LVL_DEBUG
, "pci_ide_fun_remove(%p, '%s')", ifun
, fun_name
);
264 rc
= ddf_fun_offline(ifun
->fun
);
266 ddf_msg(LVL_ERROR
, "Error offlining function '%s'.", fun_name
);
270 rc
= ddf_fun_unbind(ifun
->fun
);
272 ddf_msg(LVL_ERROR
, "Failed unbinding function '%s'.", fun_name
);
276 ddf_fun_destroy(ifun
->fun
);
280 if (fun_name
!= NULL
)
285 errno_t
pci_ide_fun_unbind(pci_ide_channel_t
*chan
, unsigned idx
)
289 pci_ide_fun_t
*ifun
= chan
->fun
[idx
];
291 fun_name
= pci_ide_fun_name(chan
, idx
);
292 if (fun_name
== NULL
) {
293 ddf_msg(LVL_ERROR
, "Out of memory.");
298 ddf_msg(LVL_DEBUG
, "pci_ide_fun_unbind(%p, '%s')", ifun
, fun_name
);
299 rc
= ddf_fun_unbind(ifun
->fun
);
301 ddf_msg(LVL_ERROR
, "Failed unbinding function '%s'.", fun_name
);
305 ddf_fun_destroy(ifun
->fun
);
309 if (fun_name
!= NULL
)
314 static errno_t
pci_ide_dev_remove(ddf_dev_t
*dev
)
316 pci_ide_ctrl_t
*ctrl
= (pci_ide_ctrl_t
*)ddf_dev_data_get(dev
);
319 ddf_msg(LVL_DEBUG
, "pci_ide_dev_remove(%p)", dev
);
321 rc
= pci_ide_channel_fini(&ctrl
->channel
[0]);
325 rc
= pci_ide_channel_fini(&ctrl
->channel
[1]);
332 static errno_t
pci_ide_dev_gone(ddf_dev_t
*dev
)
334 pci_ide_ctrl_t
*ctrl
= (pci_ide_ctrl_t
*)ddf_dev_data_get(dev
);
337 ddf_msg(LVL_DEBUG
, "pci_ide_dev_gone(%p)", dev
);
339 rc
= pci_ide_ctrl_fini(ctrl
);
343 rc
= pci_ide_channel_fini(&ctrl
->channel
[0]);
347 rc
= pci_ide_channel_fini(&ctrl
->channel
[1]);
354 static errno_t
pci_ide_fun_online(ddf_fun_t
*fun
)
356 ddf_msg(LVL_DEBUG
, "pci_ide_fun_online()");
357 return ddf_fun_online(fun
);
360 static errno_t
pci_ide_fun_offline(ddf_fun_t
*fun
)
362 ddf_msg(LVL_DEBUG
, "pci_ide_fun_offline()");
363 return ddf_fun_offline(fun
);
366 static void pci_ide_connection(ipc_call_t
*icall
, void *arg
)
370 ifun
= (pci_ide_fun_t
*) ddf_fun_data_get((ddf_fun_t
*)arg
);
371 ata_connection(icall
, ifun
->charg
);
374 int main(int argc
, char *argv
[])
376 printf(NAME
": HelenOS PCI IDE device driver\n");
378 return ddf_driver_main(&pci_ide_driver
);