📄 ap_bus.c
字号:
} return 0;}/** * uevent function for AP devices. It sets up a single environment * variable DEV_TYPE which contains the hardware device type. */static int ap_uevent (struct device *dev, struct kobj_uevent_env *env){ struct ap_device *ap_dev = to_ap_dev(dev); int retval = 0; if (!ap_dev) return -ENODEV; /* Set up DEV_TYPE environment variable. */ retval = add_uevent_var(env, "DEV_TYPE=%04X", ap_dev->device_type); if (retval) return retval; /* Add MODALIAS= */ retval = add_uevent_var(env, "MODALIAS=ap:t%02X", ap_dev->device_type); return retval;}static struct bus_type ap_bus_type = { .name = "ap", .match = &ap_bus_match, .uevent = &ap_uevent,};static int ap_device_probe(struct device *dev){ struct ap_device *ap_dev = to_ap_dev(dev); struct ap_driver *ap_drv = to_ap_drv(dev->driver); int rc; ap_dev->drv = ap_drv; spin_lock_bh(&ap_device_lock); list_add(&ap_dev->list, &ap_device_list); spin_unlock_bh(&ap_device_lock); rc = ap_drv->probe ? ap_drv->probe(ap_dev) : -ENODEV; return rc;}/** * Flush all requests from the request/pending queue of an AP device. * @ap_dev: pointer to the AP device. */static void __ap_flush_queue(struct ap_device *ap_dev){ struct ap_message *ap_msg, *next; list_for_each_entry_safe(ap_msg, next, &ap_dev->pendingq, list) { list_del_init(&ap_msg->list); ap_dev->pendingq_count--; ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV)); } list_for_each_entry_safe(ap_msg, next, &ap_dev->requestq, list) { list_del_init(&ap_msg->list); ap_dev->requestq_count--; ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV)); }}void ap_flush_queue(struct ap_device *ap_dev){ spin_lock_bh(&ap_dev->lock); __ap_flush_queue(ap_dev); spin_unlock_bh(&ap_dev->lock);}EXPORT_SYMBOL(ap_flush_queue);static int ap_device_remove(struct device *dev){ struct ap_device *ap_dev = to_ap_dev(dev); struct ap_driver *ap_drv = ap_dev->drv; ap_flush_queue(ap_dev); del_timer_sync(&ap_dev->timeout); if (ap_drv->remove) ap_drv->remove(ap_dev); spin_lock_bh(&ap_device_lock); list_del_init(&ap_dev->list); spin_unlock_bh(&ap_device_lock); spin_lock_bh(&ap_dev->lock); atomic_sub(ap_dev->queue_count, &ap_poll_requests); spin_unlock_bh(&ap_dev->lock); return 0;}int ap_driver_register(struct ap_driver *ap_drv, struct module *owner, char *name){ struct device_driver *drv = &ap_drv->driver; drv->bus = &ap_bus_type; drv->probe = ap_device_probe; drv->remove = ap_device_remove; drv->owner = owner; drv->name = name; return driver_register(drv);}EXPORT_SYMBOL(ap_driver_register);void ap_driver_unregister(struct ap_driver *ap_drv){ driver_unregister(&ap_drv->driver);}EXPORT_SYMBOL(ap_driver_unregister);/** * AP bus attributes. */static ssize_t ap_domain_show(struct bus_type *bus, char *buf){ return snprintf(buf, PAGE_SIZE, "%d\n", ap_domain_index);}static BUS_ATTR(ap_domain, 0444, ap_domain_show, NULL);static ssize_t ap_config_time_show(struct bus_type *bus, char *buf){ return snprintf(buf, PAGE_SIZE, "%d\n", ap_config_time);}static ssize_t ap_config_time_store(struct bus_type *bus, const char *buf, size_t count){ int time; if (sscanf(buf, "%d\n", &time) != 1 || time < 5 || time > 120) return -EINVAL; ap_config_time = time; if (!timer_pending(&ap_config_timer) || !mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ)) { ap_config_timer.expires = jiffies + ap_config_time * HZ; add_timer(&ap_config_timer); } return count;}static BUS_ATTR(config_time, 0644, ap_config_time_show, ap_config_time_store);static ssize_t ap_poll_thread_show(struct bus_type *bus, char *buf){ return snprintf(buf, PAGE_SIZE, "%d\n", ap_poll_kthread ? 1 : 0);}static ssize_t ap_poll_thread_store(struct bus_type *bus, const char *buf, size_t count){ int flag, rc; if (sscanf(buf, "%d\n", &flag) != 1) return -EINVAL; if (flag) { rc = ap_poll_thread_start(); if (rc) return rc; } else ap_poll_thread_stop(); return count;}static BUS_ATTR(poll_thread, 0644, ap_poll_thread_show, ap_poll_thread_store);static struct bus_attribute *const ap_bus_attrs[] = { &bus_attr_ap_domain, &bus_attr_config_time, &bus_attr_poll_thread, NULL};/** * Pick one of the 16 ap domains. */static int ap_select_domain(void){ int queue_depth, device_type, count, max_count, best_domain; int rc, i, j; /** * We want to use a single domain. Either the one specified with * the "domain=" parameter or the domain with the maximum number * of devices. */ if (ap_domain_index >= 0 && ap_domain_index < AP_DOMAINS) /* Domain has already been selected. */ return 0; best_domain = -1; max_count = 0; for (i = 0; i < AP_DOMAINS; i++) { count = 0; for (j = 0; j < AP_DEVICES; j++) { ap_qid_t qid = AP_MKQID(j, i); rc = ap_query_queue(qid, &queue_depth, &device_type); if (rc) continue; count++; } if (count > max_count) { max_count = count; best_domain = i; } } if (best_domain >= 0){ ap_domain_index = best_domain; return 0; } return -ENODEV;}/** * Find the device type if query queue returned a device type of 0. * @ap_dev: pointer to the AP device. */static int ap_probe_device_type(struct ap_device *ap_dev){ static unsigned char msg[] = { 0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x01,0x00,0x43,0x43,0x41,0x2d,0x41,0x50, 0x50,0x4c,0x20,0x20,0x20,0x01,0x01,0x01, 0x00,0x00,0x00,0x00,0x50,0x4b,0x00,0x00, 0x00,0x00,0x01,0x1c,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x05,0xb8,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x70,0x00,0x41,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x54,0x32,0x01,0x00,0xa0,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0xb8,0x05,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00, 0x49,0x43,0x53,0x46,0x20,0x20,0x20,0x20, 0x50,0x4b,0x0a,0x00,0x50,0x4b,0x43,0x53, 0x2d,0x31,0x2e,0x32,0x37,0x00,0x11,0x22, 0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00, 0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88, 0x99,0x00,0x11,0x22,0x33,0x44,0x55,0x66, 0x77,0x88,0x99,0x00,0x11,0x22,0x33,0x44, 0x55,0x66,0x77,0x88,0x99,0x00,0x11,0x22, 0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00, 0x11,0x22,0x33,0x5d,0x00,0x5b,0x00,0x77, 0x88,0x1e,0x00,0x00,0x57,0x00,0x00,0x00, 0x00,0x04,0x00,0x00,0x4f,0x00,0x00,0x00, 0x03,0x02,0x00,0x00,0x40,0x01,0x00,0x01, 0xce,0x02,0x68,0x2d,0x5f,0xa9,0xde,0x0c, 0xf6,0xd2,0x7b,0x58,0x4b,0xf9,0x28,0x68, 0x3d,0xb4,0xf4,0xef,0x78,0xd5,0xbe,0x66, 0x63,0x42,0xef,0xf8,0xfd,0xa4,0xf8,0xb0, 0x8e,0x29,0xc2,0xc9,0x2e,0xd8,0x45,0xb8, 0x53,0x8c,0x6f,0x4e,0x72,0x8f,0x6c,0x04, 0x9c,0x88,0xfc,0x1e,0xc5,0x83,0x55,0x57, 0xf7,0xdd,0xfd,0x4f,0x11,0x36,0x95,0x5d, }; struct ap_queue_status status; unsigned long long psmid; char *reply; int rc, i; reply = (void *) get_zeroed_page(GFP_KERNEL); if (!reply) { rc = -ENOMEM; goto out; } status = __ap_send(ap_dev->qid, 0x0102030405060708ULL, msg, sizeof(msg)); if (status.response_code != AP_RESPONSE_NORMAL) { rc = -ENODEV; goto out_free; } /* Wait for the test message to complete. */ for (i = 0; i < 6; i++) { mdelay(300); status = __ap_recv(ap_dev->qid, &psmid, reply, 4096); if (status.response_code == AP_RESPONSE_NORMAL && psmid == 0x0102030405060708ULL) break; } if (i < 6) { /* Got an answer. */ if (reply[0] == 0x00 && reply[1] == 0x86) ap_dev->device_type = AP_DEVICE_TYPE_PCICC; else ap_dev->device_type = AP_DEVICE_TYPE_PCICA; rc = 0; } else rc = -ENODEV;out_free: free_page((unsigned long) reply);out: return rc;}/** * Scan the ap bus for new devices. */static int __ap_scan_bus(struct device *dev, void *data){ return to_ap_dev(dev)->qid == (ap_qid_t)(unsigned long) data;}static void ap_device_release(struct device *dev){ struct ap_device *ap_dev = to_ap_dev(dev); kfree(ap_dev);}static void ap_scan_bus(struct work_struct *unused){ struct ap_device *ap_dev; struct device *dev; ap_qid_t qid; int queue_depth, device_type; int rc, i; if (ap_select_domain() != 0) return; for (i = 0; i < AP_DEVICES; i++) { qid = AP_MKQID(i, ap_domain_index); dev = bus_find_device(&ap_bus_type, NULL, (void *)(unsigned long)qid, __ap_scan_bus); rc = ap_query_queue(qid, &queue_depth, &device_type); if (dev) { if (rc == -EBUSY) { set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(AP_RESET_TIMEOUT); rc = ap_query_queue(qid, &queue_depth, &device_type); } ap_dev = to_ap_dev(dev); spin_lock_bh(&ap_dev->lock); if (rc || ap_dev->unregistered) { spin_unlock_bh(&ap_dev->lock); device_unregister(dev); put_device(dev); continue; } spin_unlock_bh(&ap_dev->lock); put_device(dev); continue; } if (rc) continue; rc = ap_init_queue(qid); if (rc) continue; ap_dev = kzalloc(sizeof(*ap_dev), GFP_KERNEL); if (!ap_dev) break; ap_dev->qid = qid; ap_dev->queue_depth = queue_depth; ap_dev->unregistered = 1; spin_lock_init(&ap_dev->lock); INIT_LIST_HEAD(&ap_dev->pendingq); INIT_LIST_HEAD(&ap_dev->requestq); INIT_LIST_HEAD(&ap_dev->list); setup_timer(&ap_dev->timeout, ap_request_timeout, (unsigned long) ap_dev); if (device_type == 0) ap_probe_device_type(ap_dev); else ap_dev->device_type = device_type; ap_dev->device.bus = &ap_bus_type; ap_dev->device.parent = ap_root_device; snprintf(ap_dev->device.bus_id, BUS_ID_SIZE, "card%02x", AP_QID_DEVICE(ap_dev->qid)); ap_dev->device.release = ap_device_release; rc = device_register(&ap_dev->device); if (rc) { kfree(ap_dev); continue; } /* Add device attributes. */ rc = sysfs_create_group(&ap_dev->device.kobj, &ap_dev_attr_group); if (!rc) { spin_lock_bh(&ap_dev->lock); ap_dev->unregistered = 0; spin_unlock_bh(&ap_dev->lock); } else device_unregister(&ap_dev->device); }}static voidap_config_timeout(unsigned long ptr){ queue_work(ap_work_queue, &ap_config_work); ap_config_timer.expires = jiffies + ap_config_time * HZ; add_timer(&ap_config_timer);}/** * Set up the timer to run the poll tasklet */static inline void ap_schedule_poll_timer(void){ if (timer_pending(&ap_poll_timer)) return; mod_timer(&ap_poll_timer, jiffies + AP_POLL_TIME);}/** * Receive pending reply messages from an AP device. * @ap_dev: pointer to the AP device * @flags: pointer to control flags, bit 2^0 is set if another poll is * required, bit 2^1 is set if the poll timer needs to get armed * Returns 0 if the device is still present, -ENODEV if not. */static int ap_poll_read(struct ap_device *ap_dev, unsigned long *flags){ struct ap_queue_status status; struct ap_message *ap_msg; if (ap_dev->queue_count <= 0) return 0; status = __ap_recv(ap_dev->qid, &ap_dev->reply->psmid, ap_dev->reply->message, ap_dev->reply->length); switch (status.response_code) { case AP_RESPONSE_NORMAL: atomic_dec(&ap_poll_requests); ap_decrease_queue_count(ap_dev); list_for_each_entry(ap_msg, &ap_dev->pendingq, list) { if (ap_msg->psmid != ap_dev->reply->psmid) continue; list_del_init(&ap_msg->list); ap_dev->pendingq_count--; ap_dev->drv->receive(ap_dev, ap_msg, ap_dev->reply); break; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -