#include <kernel/kernel.h> #include <kernel/driver.h> #include <kernel/handle.h> #include <kernel/sys.h> #include <kernel/thread.h> #include <kernel/proc.h> #include <kernel/config.h> #include <kernel/ramdisk.h> #include <kernel/fs.h> #include <stdlib.h> #include <stdio.h> #include <ctype.h> #include <wchar.h> #include <errno.h> #include <string.h> extern process_t proc_idle; typedef struct devlink_t devlink_t; struct devlink_t { devlink_t *prev, *next; device_config_t* cfg; device_t* dev; }; typedef struct devlookup_t devlookup_t; struct devlookup_t { devlookup_t *next; wchar_t name_mask[20]; word vendor_id; word device_id; dword subsystem; wchar_t to_filename[MAX_PATH]; }; devlink_t *dev_first, *dev_last; static devlookup_t *dlu_first, *dlu_scan; static int line_count; typedef struct devfile_t devfile_t; struct devfile_t { file_t file; device_t* dev; }; bool devFsRequest(device_t* dev, request_t* req); device_t vfs_devices = { NULL, devFsRequest, NULL, NULL, NULL }; bool devFsRequest(device_t* dev, request_t* req) { devfile_t* fd; const wchar_t* name; assert(dev == &vfs_devices); switch (req->code) { case FS_OPEN: assert(req->params.fs_open.name[0] == '/'); fd = hndAlloc(sizeof(devfile_t), NULL); assert(fd != NULL); fd->file.fsd = dev; fd->file.pos = 0; name = req->params.fs_open.name + 1; fd->dev = devOpen(name, NULL); //wprintf(L"devFsRequest: FS_OPEN(%s), dev = %p\n", name, dev); if (fd->dev == NULL) { req->params.fs_open.fd = NULL; hndFree(fd); return false; } req->params.fs_open.fd = &fd->file; hndSignal(req->event, true); return true; case FS_CLOSE: fd = (devfile_t*) req->params.fs_close.fd; assert(fd != NULL); if (!devClose(fd->dev)) { req->result = EINVALID; return false; } hndFree(fd); hndSignal(req->event, true); return true; case FS_READ: fd = (devfile_t*) req->params.fs_read.fd; assert(fd != NULL); //wprintf(L"devFsRequest: FS_READ, dev = %p\n", fd->dev); req->result = devReadSync(fd->dev, fd->file.pos, (void*) req->params.fs_read.buffer, &req->params.fs_read.length); fd->file.pos += req->params.fs_read.length; hndSignal(req->event, true); return req->result == 0; case FS_WRITE: fd = (devfile_t*) req->params.fs_write.fd; assert(fd != NULL); //wprintf(L"devFsRequest: FS_WRITE, dev = %p\n", fd->dev); req->result = devWriteSync(fd->dev, fd->file.pos, (const void*) req->params.fs_write.buffer, &req->params.fs_write.length); fd->file.pos += req->params.fs_write.length; hndSignal(req->event, true); return req->result == 0; } req->result = ENOTIMPL; return false; } void devPrintConfigLine(hashelem_t* elem) { if (elem->data == NULL) return; if (wcsicmp(elem->str, (const wchar_t*) L"driver") == 0) wcscpy(dlu_scan->to_filename, elem->data); else if (wcsicmp(elem->str, (const wchar_t*) L"mask") == 0) wcscpy(dlu_scan->name_mask, elem->data); else if (wcsicmp(elem->str, (const wchar_t*) L"vendor") == 0) dlu_scan->vendor_id = wcstol(elem->data, NULL, 16); else if (wcsicmp(elem->str, (const wchar_t*) L"device") == 0) dlu_scan->device_id = wcstol(elem->data, NULL, 16); else if (wcsicmp(elem->str, (const wchar_t*) L"subsystem") == 0) dlu_scan->subsystem = wcstol(elem->data, NULL, 16); line_count++; } void devInit() { char *ptr, *base; hashtable_t* table; size_t length; devlookup_t *dlu, *dlu_last; base = ptr = ramOpen(L"drivers.cfg"); if (!ptr) return; length = ramFileLength(L"drivers.cfg"); dlu_last = NULL; while (ptr < base + length) { table = cfgParseStrLine((const char**) &ptr); dlu = malloc(sizeof(devlookup_t)); dlu->to_filename[0] = 0; wcscpy(dlu->name_mask, L"*"); dlu->vendor_id = dlu->device_id = 0xffff; dlu->subsystem = 0xffffffff; line_count = 0; dlu_scan = dlu; hashList(table, devPrintConfigLine); dlu_scan = NULL; if (line_count) { //wprintf(L"%s/%04x/%04x/%08x => %s\n", //dlu->name_mask, dlu->vendor_id, dlu->device_id, dlu->subsystem, dlu->to_filename); dlu->next = NULL; if (dlu_first == NULL) dlu_first = dlu; if (dlu_last) dlu_last->next = dlu; dlu_last = dlu; } else free(dlu); cfgDeleteTable(table); } } void devCleanup() { devlink_t *link, *next; for (link = dev_first; link; link = next) { next = link->next; devRemove(link->dev); if (link->cfg) { hndFree(link->cfg->resources); hndFree(link->cfg); } free(link); } } driver_t* devInstallNewDevice(const wchar_t* name, device_config_t* cfg) { wchar_t temp[16]; driver_t* drv; module_t* mod; bool (STDCALL *drvInit)(driver_t*); devlookup_t* dlu; /*if (cfg->vendor_id != 0xffff) wprintf(L"%s: %04x/%04x/%08x\n", name, cfg->vendor_id, cfg->device_id, cfg->subsystem);*/ temp[0] = 0; for (dlu = dlu_first; dlu; dlu = dlu->next) { //wprintf(L"%s = %s? %d\n", dev_default[i].name_mask, name, //!match(dev_default[i].name_mask, name)); if ((dlu->vendor_id == 0xffff && match(dlu->name_mask, name) == 0) || (cfg != NULL && (cfg->vendor_id != 0xffff) && (cfg->device_id == dlu->device_id) && (cfg->vendor_id == dlu->vendor_id) && (cfg->subsystem == dlu->subsystem))) { wcscpy(temp, dlu->to_filename); if (cfg) wprintf(L"Matched %s/%04x/%04x/%04x to %s\n", name, cfg->vendor_id, cfg->device_id, cfg->subsystem, temp); else wprintf(L"Matched %s to %s\n", name, temp); break; } } if (temp[0] == 0) { if (cfg) wprintf(L"No match for %04x/%04x/%04x\n", cfg->vendor_id, cfg->device_id, cfg->subsystem, temp); else wprintf(L"No match for %s\n", name); return NULL; } mod = peLoad(&proc_idle, temp, 0); if (!mod) return NULL; drv = hndAlloc(sizeof(driver_t), NULL); assert(drv != NULL); memset(drv, 0, sizeof(driver_t)); drv->mod = mod; drvInit = (void*) mod->entry; if (!drvInit || !drvInit(drv)) { wprintf(L"drvInit failed\n"); return NULL; } return drv; } //! Retrieves the device_t structure for the specified device /*! * The device will receive a DEV_OPEN request before function returns. * \param name The name of the device to open * \param params Any additional parameters to pass to this instance of the * device * \return A pointer to the original device_t structure passed when the driver * called devRegister(). */ device_t* devOpen(const wchar_t* name, const wchar_t* params) { devlink_t* link; request_t req; //wprintf(L"devOpen(%p, %p)\n", name, params); link = sysOpen(name); if (link && link->dev) { req.code = DEV_OPEN; req.params.open.params = params; if (devRequestSync(link->dev, &req) == 0) return link->dev; else return NULL; } else return NULL; } //! Closes a device opened by devOpen() /*! * This function issues the DEV_CLOSE request to the device. * \param dev The device to close * \return true if the close request completed successfully. */ bool devClose(device_t* dev) { request_t req; if (!dev) return false; req.code = DEV_CLOSE; return devRequestSync(dev, &req) == 0; } //! Removes a device from the device manager /*! * This function issues the DEV_REMOVE request to the device. * The driver itself is responsible for freeing all the resources * allocated for the device, including the device_t structure * itself. * \param dev The device to remove * \return true if the remove request completed successfully. */ bool devRemove(device_t* dev) { request_t req; if (!dev) return false; req.code = DEV_REMOVE; return devRequestSync(dev, &req) == 0; } //! Issues a request to a device /*! * The device's request function is called and it is passed the req structure. * req is not copied, so if the request is queued by the device, the memory * must remain valid until the request completes. * If the function succeeds, req->event will contain a handle to an event * which will be signalled when the request completes. The driver is free * to signal this before it the function returns, or it could be signalled * when the driver determines that its hardware has completed the request. * \param dev The device to which the request is to be issued * \param req The request to pass to the driver * \return The error code returned by the driver, or zero if no error * occurred. The return value is equal to the result member of the * request structure provided. */ status_t devRequest(device_t* dev, request_t* req) { if (dev && dev->request) { //wprintf(L"devRequest: %c%c\n", req->code / 256, req->code % 256); req->result = 0; req->event = hndAlloc(0, NULL); //req->cks = 0xdeadbeef; assert(req->event != NULL); req->next = NULL; req->queued = 0; if (dev->request(dev, req)) { //assert(req->cks == 0xdeadbeef); return req->result; } else { //wprintf(L"devRequest failed (%p): %x\n", dev->request, req->result); hndFree(req->event); req->event = NULL; //assert(req->cks == 0xdeadbeef); if (req->result == 0) req->result = EINVALID; return req->result; } } else { req->result = EINVALID; return req->result; } } //! Issues a synchronous request to a device /*! * This function is identical to devRequest(), except that it always waits * for the request to complete before returning. * \param dev The device to which the request is to be issued * \param req The request to pass to the driver * \return The error code returned by the driver, or zero if no error * occurred. The return value is equal to the result member of the * request structure provided. */ status_t devRequestSync(device_t* dev, request_t* req) { //bool wasntSignalled = false; if (devRequest(dev, req) == 0) { if (req->result == 0) { assert(req->event != NULL); /*if (!hndIsSignalled(req->event)) { wprintf(L"devRequestSync(%p): event not signalled...", dev->request); wasntSignalled = true; }*/ while (!hndIsSignalled(req->event)) asm("sti"); //if (wasntSignalled) //wprintf(L"done\n"); hndFree(req->event); return 0; } else return req->result; } else return req->result; } //! Connects an IRQ line to a kernel device /*! * Currently, each IRQ can have only one associated device. * To de-register an IRQ, use a NULL device pointer. * When the IRQ is triggered, the device's request function will be * called with the DEV_ISR code. * \param dev The device to which the specified IRQ is to be connected * \param irq The IRQ to connect. * \param install true to install the IRQ in the device manager's IRQ chain, * false to remove it. * \return true if the operation completed successfully. */ bool devRegisterIrq(device_t* dev, byte irq, bool install) { irq_t *pirq, *next; dword crit; if (irq >= countof(irq_last)) return false; crit = critb(); if (install) { pirq = malloc(sizeof(irq_t)); pirq->dev = dev; pirq->next = NULL; pirq->prev = irq_last[irq]; if (irq_last[irq]) irq_last[irq]->next = pirq; if (irq_first[irq] == NULL) irq_first[irq] = pirq; irq_last[irq] = pirq; } else { pirq = irq_first[irq]; while (pirq) { next = pirq->next; if (pirq->dev == dev) { if (pirq->prev) pirq->prev->next = pirq->next; if (pirq->next) pirq->next->prev = pirq->prev; free(pirq); } pirq = next; } } crite(crit); return true; } //! Registers a device with the device manager /*! * The device manager keeps an internal list of devices. This function * adds entries to that list. * This function is used under two circumstances: * \li Case 1: to add a device for which a device_t structure has already been * allocated, * \li Case 2: to install a device from an unknown driver given a set of * configuration information. * * To use case 1, allocate a device_t structure and (optionally) a * device_config_t structure, and pass them to devRegister() (along with * a name) to create an entry in the device manager's namespace. * This is generally used to bypass the PnP and explicitly create a * device link. * * To use case 2, provide a name and a valid device_config_t structure, * and pass them to devRegister(). Use NULL for dev. The device manager * will use the name and configuration information to match a driver to * this device. This method is generally used by devices which enumerate * one or more other devices; for example, the PCI bus driver needs to * add several child devices, for which it knows the configuration but * doesn't know the name of the driver to use. * * If you adhere to the PnP standard your driver's add_device routine will get * called automatically when the relevant bus driver calls devRegister(). * You should only need to use devRegister() if you add further devices * not already added by another device. * * \param name The name (ID) of the device to add * \param dev The device object to add. If this is NULL, the device * manager will attempt to load a driver based on the device * configuration provided (which must not be NULL). * \param cfg The configuration of the device to be added. This may only * be NULL if dev is not NULL. * * \return true if the device could be added (and if loading the driver * succeeded, if necessary). */ bool devRegister(const wchar_t* name, device_t* dev, device_config_t* cfg) { devlink_t* link; driver_t* drv; link = sysOpen(name); if (link == NULL) { link = malloc(sizeof(devlink_t)); link->cfg = NULL; link->dev = NULL; link->prev = dev_last; link->next = NULL; if (dev_last != NULL) dev_last->next = link; if (dev_first == NULL) dev_first = link; dev_last = link; sysMount(name, link); } if (cfg) link->cfg = cfg; if (dev == NULL && link->dev == NULL) { _cputws_check(L" \n" CHECK_L2); _cputws_check(name); _cputws_check(L"\r\t"); drv = devInstallNewDevice(name, cfg); if (drv == NULL || !drv->add_device) { link->dev = NULL; return false; } dev = drv->add_device(drv, name, cfg); hndFree(drv); link->dev = dev; } else if (dev) link->dev = dev; if (dev) { dev->req_first = dev->req_last = NULL; dev->config = cfg; } return true; } //! Adds a request to a device's request queue void devStartRequest(device_t* dev, request_t* req) { req->next = NULL; if (dev->req_last) dev->req_last->next = req; dev->req_last = req; if (dev->req_first == NULL) dev->req_first = req; req->user_length = req->params.buffered.length; req->queued++; } //! Marks a request as finished and removes it from the device's queue void devFinishRequest(device_t* dev, request_t* req) { request_t *next; hndSignal(req->event, true); if (dev->req_first == req) dev->req_first = req->next; if (dev->req_last == req) { dev->req_last = NULL; for (next = dev->req_first; next; next = next->next) if (next->next == req) { dev->req_last = next; break; } } req->next = NULL; req->queued--; } //! Reads from a device synchronously status_t devReadSync(device_t* dev, qword pos, void* buffer, size_t* length) { request_t req; req.code = DEV_READ; req.params.read.buffer = buffer; req.params.read.length = *length; req.params.read.pos = pos; if (devRequestSync(dev, &req) == 0) { *length = req.params.read.length; return 0; } else return req.result; } //! Writes to a device synchronously status_t devWriteSync(device_t* dev, qword pos, const void* buffer, size_t* length) { request_t req; req.code = DEV_WRITE; req.params.write.buffer = buffer; req.params.write.length = *length; req.params.write.pos = pos; if (devRequestSync(dev, &req) == 0) { *length = req.params.write.length; return 0; } else return req.result; } //! Issues a request from user mode status_t devUserRequest(device_t* dev, request_t* req, size_t size) { request_t* kreq; void* buffer; bool ret; if (size < sizeof(request_t)) size = sizeof(request_t); if (!dev) return EINVALID; kreq = (request_t*) hndAlloc(size, NULL); assert(kreq != NULL); memcpy(kreq, req, size); req->kernel_request = kreq; req->original_request_size = 0; kreq->kernel_request = NULL; kreq->original_request_size = size; //wprintf(L"[dur] User request = %p, kernel request = %p\n", //req, req->kernel_request); if (devIsBufferedRequest(req->code)) { buffer = malloc(req->params.buffered.length); //wprintf(L"[dur buffered] User buffer = %p, kernel buffer = %p, size = %d\n", //req->params.buffered.buffer, buffer, req->params.buffered.length); assert(buffer != NULL); memcpy(buffer, (void*) req->params.buffered.buffer, req->params.buffered.length); kreq->params.buffered.buffer = (addr_t) buffer; } ret = devRequest(dev, kreq); if (ret) { hndFree(kreq); req->kernel_request = NULL; } /* * event and result need to be reflected now, because they will have been * updated by devRequest, and are needed before devUserFinishRequest is * called. */ req->event = kreq->event; req->result = kreq->result; return req->result; } //! Retrieves the results from a request previously made from user mode status_t devUserFinishRequest(request_t* req, bool delete_event) { addr_t user_buffer; request_t* kreq; size_t size; if (req == NULL || req->kernel_request == NULL) return EINVALID; if (req->kernel_request->queued) _cputws(L"Request still queued\n"); kreq = req->kernel_request; size = kreq->original_request_size; //wprintf(L"[dufr] User request = %p, kernel request = %p, size = %d, sizeof = %d\n", //req, req->kernel_request, size, sizeof(request_t)); if (devIsBufferedRequest(req->code)) { user_buffer = req->params.buffered.buffer; //wprintf(L"[dufr buffered] User buffer = %p, kernel buffer = %p, length = %d\n", //user_buffer, req->kernel_request->params.buffered.buffer, //req->params.buffered.length); memcpy((void*) user_buffer, (void*) req->kernel_request->params.buffered.buffer, req->params.buffered.length); free((void*) req->kernel_request->params.buffered.buffer); } memcpy(req, kreq, size); hndFree(kreq); if (delete_event) hndFree(req->event); //_cputws(L"[dufr] All finished!\n"); return req->result; } //! Searches for a specific resource in a device configuration list /*! * \param cfg The configuration structure to search * \param cls The type of resource to return * \param index The resource index to return * \return The number of the index'th resource of type cls in the * configuration structure, or (dword) -1 if unsuccessful. */ dword devFindResource(const device_config_t *cfg, int cls, int index) { int i, j; j = 0; for (i = 0; i < cfg->num_resources; i++) { if (cfg->resources[i].cls == cls) { if (j == index) return i; j++; } } return (dword) -1; }