📄 cs.c
字号:
static void cs_sleep(unsigned int n_cs){ current->state = TASK_INTERRUPTIBLE; schedule_timeout( (n_cs * HZ + 99) / 100);}static void shutdown_socket(socket_info_t *s){ client_t **c; DEBUG(1, "cs: shutdown_socket(%p)\n", s); /* Blank out the socket state */ s->state &= SOCKET_PRESENT|SOCKET_SETUP_PENDING; init_socket(s); s->irq.AssignedIRQ = s->irq.Config = 0; s->lock_count = 0; s->cis_used = 0; if (s->fake_cis) { kfree(s->fake_cis); s->fake_cis = NULL; }#ifdef CONFIG_CARDBUS cb_release_cis_mem(s); cb_free(s);#endif s->functions = 0; if (s->config) { kfree(s->config); s->config = NULL; } for (c = &s->clients; *c; ) { if ((*c)->state & CLIENT_UNBOUND) { client_t *d = *c; *c = (*c)->next; kfree(d); } else { c = &((*c)->next); } } free_regions(&s->a_region); free_regions(&s->c_region);} /* shutdown_socket *//* * Return zero if we think the card isn't actually present */static int setup_socket(socket_info_t *s){ int val, ret; int setup_timeout = 100; /* Wait for "not pending" */ for (;;) { get_socket_status(s, &val); if (!(val & SS_PENDING)) break; if (--setup_timeout) { cs_sleep(10); continue; } printk(KERN_NOTICE "cs: socket %p voltage interrogation" " timed out\n", s); ret = 0; goto out; } if (val & SS_DETECT) { DEBUG(1, "cs: setup_socket(%p): applying power\n", s); s->state |= SOCKET_PRESENT; s->socket.flags &= SS_DEBOUNCED; if (val & SS_3VCARD) s->socket.Vcc = s->socket.Vpp = 33; else if (!(val & SS_XVCARD)) s->socket.Vcc = s->socket.Vpp = 50; else { printk(KERN_NOTICE "cs: socket %p: unsupported " "voltage key\n", s); s->socket.Vcc = 0; } if (val & SS_CARDBUS) { s->state |= SOCKET_CARDBUS;#ifndef CONFIG_CARDBUS printk(KERN_NOTICE "cs: unsupported card type detected!\n");#endif } set_socket(s, &s->socket); cs_sleep(vcc_settle); reset_socket(s); ret = 1; } else { DEBUG(0, "cs: setup_socket(%p): no card!\n", s); ret = 0; }out: return ret;} /* setup_socket *//*====================================================================== Reset_socket() and unreset_socket() handle hard resets. Resets have several causes: card insertion, a call to reset_socket, or recovery from a suspend/resume cycle. Unreset_socket() sends a CS event that matches the cause of the reset. ======================================================================*/static void reset_socket(socket_info_t *s){ DEBUG(1, "cs: resetting socket %p\n", s); s->socket.flags |= SS_OUTPUT_ENA | SS_RESET; set_socket(s, &s->socket); udelay((long)reset_time); s->socket.flags &= ~SS_RESET; set_socket(s, &s->socket); cs_sleep(unreset_delay); unreset_socket(s);} /* reset_socket */#define EVENT_MASK \(SOCKET_SETUP_PENDING|SOCKET_SUSPEND|SOCKET_RESET_PENDING)static void unreset_socket(socket_info_t *s){ int setup_timeout = unreset_limit; int val; /* Wait for "ready" */ for (;;) { get_socket_status(s, &val); if (val & SS_READY) break; DEBUG(2, "cs: socket %d not ready yet\n", s->sock); if (--setup_timeout) { cs_sleep(unreset_check); continue; } printk(KERN_NOTICE "cs: socket %p timed out during" " reset. Try increasing setup_delay.\n", s); s->state &= ~EVENT_MASK; return; } DEBUG(1, "cs: reset done on socket %p\n", s); if (s->state & SOCKET_SUSPEND) { s->state &= ~EVENT_MASK; if (verify_cis_cache(s) != 0) parse_events(s, SS_DETECT); else send_event(s, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW); } else if (s->state & SOCKET_SETUP_PENDING) {#ifdef CONFIG_CARDBUS if (s->state & SOCKET_CARDBUS) cb_alloc(s);#endif send_event(s, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW); s->state &= ~SOCKET_SETUP_PENDING; } else { send_event(s, CS_EVENT_CARD_RESET, CS_EVENT_PRI_LOW); if (s->reset_handle) { s->reset_handle->event_callback_args.info = NULL; EVENT(s->reset_handle, CS_EVENT_RESET_COMPLETE, CS_EVENT_PRI_LOW); } s->state &= ~EVENT_MASK; }} /* unreset_socket *//*====================================================================== The central event handler. Send_event() sends an event to all valid clients. Parse_events() interprets the event bits from a card status change report. Do_shotdown() handles the high priority stuff associated with a card removal. ======================================================================*/static int send_event(socket_info_t *s, event_t event, int priority){ client_t *client = s->clients; int ret; DEBUG(1, "cs: send_event(sock %d, event %d, pri %d)\n", s->sock, event, priority); ret = 0; for (; client; client = client->next) { if (client->state & (CLIENT_UNBOUND|CLIENT_STALE)) continue; if (client->EventMask & event) { ret = EVENT(client, event, priority); if (ret != 0) return ret; } } return ret;} /* send_event */static void do_shutdown(socket_info_t *s){ client_t *client; if (s->state & SOCKET_SHUTDOWN_PENDING) return; s->state |= SOCKET_SHUTDOWN_PENDING; send_event(s, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH); for (client = s->clients; client; client = client->next) if (!(client->Attributes & INFO_MASTER_CLIENT)) client->state |= CLIENT_STALE; if (s->state & (SOCKET_SETUP_PENDING|SOCKET_RESET_PENDING)) { DEBUG(0, "cs: flushing pending setup\n"); s->state &= ~EVENT_MASK; } cs_sleep(shutdown_delay); s->state &= ~SOCKET_PRESENT; shutdown_socket(s);}static void parse_events(void *info, u_int events){ socket_info_t *s = info; if (events & SS_DETECT) { int status; get_socket_status(s, &status); if ((s->state & SOCKET_PRESENT) && (!(s->state & SOCKET_SUSPEND) || !(status & SS_DETECT))) do_shutdown(s); if (status & SS_DETECT) { if (s->state & SOCKET_SETUP_PENDING) { DEBUG(1, "cs: delaying pending setup\n"); return; } s->state |= SOCKET_SETUP_PENDING; if (s->state & SOCKET_SUSPEND) cs_sleep(resume_delay); else cs_sleep(setup_delay); s->socket.flags |= SS_DEBOUNCED; if (setup_socket(s) == 0) s->state &= ~SOCKET_SETUP_PENDING; s->socket.flags &= ~SS_DEBOUNCED; } } if (events & SS_BATDEAD) send_event(s, CS_EVENT_BATTERY_DEAD, CS_EVENT_PRI_LOW); if (events & SS_BATWARN) send_event(s, CS_EVENT_BATTERY_LOW, CS_EVENT_PRI_LOW); if (events & SS_READY) { if (!(s->state & SOCKET_RESET_PENDING)) send_event(s, CS_EVENT_READY_CHANGE, CS_EVENT_PRI_LOW); else DEBUG(1, "cs: ready change during reset\n"); }} /* parse_events *//*====================================================================== Another event handler, for power management events. This does not comply with the latest PC Card spec for handling power management events. ======================================================================*/void pcmcia_suspend_socket (socket_info_t *s){ if ((s->state & SOCKET_PRESENT) && !(s->state & SOCKET_SUSPEND)) { send_event(s, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW); suspend_socket(s); s->state |= SOCKET_SUSPEND; }}void pcmcia_resume_socket (socket_info_t *s){ int stat; /* Do this just to reinitialize the socket */ init_socket(s); get_socket_status(s, &stat); /* If there was or is a card here, we need to do something about it... but parse_events will sort it all out. */ if ((s->state & SOCKET_PRESENT) || (stat & SS_DETECT)) parse_events(s, SS_DETECT);}static int handle_pm_event(struct pm_dev *dev, pm_request_t rqst, void *data){ int i; socket_info_t *s; /* only for busses that don't suspend/resume slots directly */ switch (rqst) { case PM_SUSPEND: DEBUG(1, "cs: received suspend notification\n"); for (i = 0; i < sockets; i++) { s = socket_table [i]; if (!s->use_bus_pm) pcmcia_suspend_socket (socket_table [i]); } break; case PM_RESUME: DEBUG(1, "cs: received resume notification\n"); for (i = 0; i < sockets; i++) { s = socket_table [i]; if (!s->use_bus_pm) pcmcia_resume_socket (socket_table [i]); } break; } return 0;} /* handle_pm_event *//*====================================================================== Special stuff for managing IO windows, because they are scarce. ======================================================================*/static int alloc_io_space(socket_info_t *s, u_int attr, ioaddr_t *base, ioaddr_t num, u_int lines, char *name){ int i; ioaddr_t try, align; align = (*base) ? (lines ? 1<<lines : 0) : 1; if (align && (align < num)) { if (*base) { DEBUG(0, "odd IO request: num %04x align %04x\n", num, align); align = 0; } else while (align && (align < num)) align <<= 1; } if (*base & ~(align-1)) { DEBUG(0, "odd IO request: base %04x align %04x\n", *base, align); align = 0; } /* Check for an already-allocated window that must conflict with what was asked for. It is a hack because it does not catch all potential conflicts, just the most obvious ones. */ for (i = 0; i < MAX_IO_WIN; i++) if ((s->io[i].NumPorts != 0) && ((s->io[i].BasePort & (align-1)) == *base)) return 1; for (i = 0; i < MAX_IO_WIN; i++) { if (s->io[i].NumPorts == 0) { if (find_io_region(base, num, align, name) == 0) { s->io[i].Attributes = attr; s->io[i].BasePort = *base; s->io[i].NumPorts = s->io[i].InUse = num; break; } else return 1; } else if (s->io[i].Attributes != attr) continue; /* Try to extend top of window */ try = s->io[i].BasePort + s->io[i].NumPorts; if ((*base == 0) || (*base == try)) if (find_io_region(&try, num, 0, name) == 0) { *base = try; s->io[i].NumPorts += num; s->io[i].InUse += num; break; } /* Try to extend bottom of window */ try = s->io[i].BasePort - num; if ((*base == 0) || (*base == try)) if (find_io_region(&try, num, 0, name) == 0) { s->io[i].BasePort = *base = try; s->io[i].NumPorts += num; s->io[i].InUse += num; break; } } return (i == MAX_IO_WIN);} /* alloc_io_space */static void release_io_space(socket_info_t *s, ioaddr_t base, ioaddr_t num){ int i; release_region(base, num); for (i = 0; i < MAX_IO_WIN; i++) { if ((s->io[i].BasePort <= base) && (s->io[i].BasePort+s->io[i].NumPorts >= base+num)) { s->io[i].InUse -= num; /* Free the window if no one else is using it */ if (s->io[i].InUse == 0) s->io[i].NumPorts = 0; } }}/*====================================================================== Access_configuration_register() reads and writes configuration registers in attribute memory. Memory window 0 is reserved for this and the tuple reading services. ======================================================================*/int pcmcia_access_configuration_register(client_handle_t handle, conf_reg_t *reg){ socket_info_t *s; config_t *c; int addr; u_char val; if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; s = SOCKET(handle); if (handle->Function == BIND_FN_ALL) { if (reg->Function >= s->functions) return CS_BAD_ARGS; c = &s->config[reg->Function]; } else c = CONFIG(handle); if (!(c->state & CONFIG_LOCKED)) return CS_CONFIGURATION_LOCKED; addr = (c->ConfigBase + reg->Offset) >> 1; switch (reg->Action) { case CS_READ: read_cis_mem(s, 1, addr, 1, &val); reg->Value = val; break; case CS_WRITE: val = reg->Value; write_cis_mem(s, 1, addr, 1, &val); break; default: return CS_BAD_ARGS; break; } return CS_SUCCESS;} /* access_configuration_register *//*====================================================================== Bind_device() associates a device driver with a particular socket. It is normally called by Driver Services after it has identified a newly inserted card. An instance of that driver will then be eligible to register as a client of this socket. ======================================================================*/int pcmcia_bind_device(bind_req_t *req)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -