1#include "ixj-ver.h" 2 3#include <linux/module.h> 4 5#include <linux/init.h> 6#include <linux/sched.h> 7#include <linux/kernel.h> /* printk() */ 8#include <linux/fs.h> /* everything... */ 9#include <linux/errno.h> /* error codes */ 10#include <linux/slab.h> 11 12#include <pcmcia/version.h> 13#include <pcmcia/cs_types.h> 14#include <pcmcia/cs.h> 15#include <pcmcia/cistpl.h> 16#include <pcmcia/ds.h> 17 18#include "ixj.h" 19 20/* 21 * PCMCIA service support for Quicknet cards 22 */ 23 24#ifdef PCMCIA_DEBUG 25static int pc_debug = PCMCIA_DEBUG; 26MODULE_PARM(pc_debug, "i"); 27#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) 28#else 29#define DEBUG(n, args...) 30#endif 31 32typedef struct ixj_info_t { 33 int ndev; 34 dev_node_t node; 35 struct ixj *port; 36} ixj_info_t; 37 38static dev_link_t *ixj_attach(void); 39static void ixj_detach(dev_link_t *); 40static void ixj_config(dev_link_t * link); 41static void ixj_cs_release(u_long arg); 42static int ixj_event(event_t event, int priority, event_callback_args_t * args); 43static dev_info_t dev_info = "ixj_cs"; 44static dev_link_t *dev_list = NULL; 45 46static void cs_error(client_handle_t handle, int func, int ret) 47{ 48 error_info_t err = 49 { 50 func, ret 51 }; 52 CardServices(ReportError, handle, &err); 53} 54 55static dev_link_t *ixj_attach(void) 56{ 57 client_reg_t client_reg; 58 dev_link_t *link; 59 int ret; 60 DEBUG(0, "ixj_attach()\n"); 61 /* Create new ixj device */ 62 link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL); 63 if (!link) 64 return NULL; 65 memset(link, 0, sizeof(struct dev_link_t)); 66 link->release.function = &ixj_cs_release; 67 link->release.data = (u_long) link; 68 link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; 69 link->io.Attributes2 = IO_DATA_PATH_WIDTH_8; 70 link->io.IOAddrLines = 3; 71 link->conf.Vcc = 50; 72 link->conf.IntType = INT_MEMORY_AND_IO; 73 link->priv = kmalloc(sizeof(struct ixj_info_t), GFP_KERNEL); 74 if (!link->priv) 75 return NULL; 76 memset(link->priv, 0, sizeof(struct ixj_info_t)); 77 /* Register with Card Services */ 78 link->next = dev_list; 79 dev_list = link; 80 client_reg.dev_info = &dev_info; 81 client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; 82 client_reg.EventMask = 83 CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | 84 CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | 85 CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; 86 client_reg.event_handler = &ixj_event; 87 client_reg.Version = 0x0210; 88 client_reg.event_callback_args.client_data = link; 89 ret = CardServices(RegisterClient, &link->handle, &client_reg); 90 if (ret != CS_SUCCESS) { 91 cs_error(link->handle, RegisterClient, ret); 92 ixj_detach(link); 93 return NULL; 94 } 95 return link; 96} 97 98static void ixj_detach(dev_link_t * link) 99{ 100 dev_link_t **linkp; 101 long flags; 102 int ret; 103 DEBUG(0, "ixj_detach(0x%p)\n", link); 104 for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) 105 if (*linkp == link) 106 break; 107 if (*linkp == NULL) 108 return; 109 save_flags(flags); 110 cli(); 111 if (link->state & DEV_RELEASE_PENDING) { 112 del_timer(&link->release); 113 link->state &= ~DEV_RELEASE_PENDING; 114 } 115 restore_flags(flags); 116 if (link->state & DEV_CONFIG) 117 ixj_cs_release((u_long) link); 118 if (link->handle) { 119 ret = CardServices(DeregisterClient, link->handle); 120 if (ret != CS_SUCCESS) 121 cs_error(link->handle, DeregisterClient, ret); 122 } 123 /* Unlink device structure, free bits */ 124 *linkp = link->next; 125 kfree(link->priv); 126 kfree(link); 127} 128 129#define CS_CHECK(fn, args...) \ 130while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed 131 132#define CFG_CHECK(fn, args...) \ 133if (CardServices(fn, args) != 0) goto next_entry 134 135static void ixj_get_serial(dev_link_t * link, IXJ * j) 136{ 137 client_handle_t handle; 138 tuple_t tuple; 139 u_short buf[128]; 140 char *str; 141 int last_ret, last_fn, i, place; 142 handle = link->handle; 143 DEBUG(0, "ixj_get_serial(0x%p)\n", link); 144 tuple.TupleData = (cisdata_t *) buf; 145 tuple.TupleOffset = 0; 146 tuple.TupleDataMax = 80; 147 tuple.Attributes = 0; 148 tuple.DesiredTuple = CISTPL_VERS_1; 149 CS_CHECK(GetFirstTuple, handle, &tuple); 150 CS_CHECK(GetTupleData, handle, &tuple); 151 str = (char *) buf; 152 printk("PCMCIA Version %d.%d\n", str[0], str[1]); 153 str += 2; 154 printk("%s", str); 155 str = str + strlen(str) + 1; 156 printk(" %s", str); 157 str = str + strlen(str) + 1; 158 place = 1; 159 for (i = strlen(str) - 1; i >= 0; i--) { 160 switch (str[i]) { 161 case '0': 162 case '1': 163 case '2': 164 case '3': 165 case '4': 166 case '5': 167 case '6': 168 case '7': 169 case '8': 170 case '9': 171 j->serial += (str[i] - 48) * place; 172 break; 173 case 'A': 174 case 'B': 175 case 'C': 176 case 'D': 177 case 'E': 178 case 'F': 179 j->serial += (str[i] - 55) * place; 180 break; 181 case 'a': 182 case 'b': 183 case 'c': 184 case 'd': 185 case 'e': 186 case 'f': 187 j->serial += (str[i] - 87) * place; 188 break; 189 } 190 place = place * 0x10; 191 } 192 str = str + strlen(str) + 1; 193 printk(" version %s\n", str); 194 cs_failed: 195 return; 196} 197 198static void ixj_config(dev_link_t * link) 199{ 200 IXJ *j; 201 client_handle_t handle; 202 ixj_info_t *info; 203 tuple_t tuple; 204 u_short buf[128]; 205 cisparse_t parse; 206 config_info_t conf; 207 cistpl_cftable_entry_t *cfg = &parse.cftable_entry; 208 cistpl_cftable_entry_t dflt = 209 { 210 0 211 }; 212 int last_ret, last_fn; 213 handle = link->handle; 214 info = link->priv; 215 DEBUG(0, "ixj_config(0x%p)\n", link); 216 tuple.TupleData = (cisdata_t *) buf; 217 tuple.TupleOffset = 0; 218 tuple.TupleDataMax = 255; 219 tuple.Attributes = 0; 220 tuple.DesiredTuple = CISTPL_CONFIG; 221 CS_CHECK(GetFirstTuple, handle, &tuple); 222 CS_CHECK(GetTupleData, handle, &tuple); 223 CS_CHECK(ParseTuple, handle, &tuple, &parse); 224 link->conf.ConfigBase = parse.config.base; 225 link->conf.Present = parse.config.rmask[0]; 226 link->state |= DEV_CONFIG; 227 CS_CHECK(GetConfigurationInfo, handle, &conf); 228 tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; 229 tuple.Attributes = 0; 230 CS_CHECK(GetFirstTuple, handle, &tuple); 231 while (1) { 232 CFG_CHECK(GetTupleData, handle, &tuple); 233 CFG_CHECK(ParseTuple, handle, &tuple, &parse); 234 if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) { 235 cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io; 236 link->conf.ConfigIndex = cfg->index; 237 link->io.BasePort1 = io->win[0].base; 238 link->io.NumPorts1 = io->win[0].len; 239 if (io->nwin == 2) { 240 link->io.BasePort2 = io->win[1].base; 241 link->io.NumPorts2 = io->win[1].len; 242 } 243 CFG_CHECK(RequestIO, link->handle, &link->io); 244 /* If we've got this far, we're done */ 245 break; 246 } 247 next_entry: 248 if (cfg->flags & CISTPL_CFTABLE_DEFAULT) 249 dflt = *cfg; 250 CS_CHECK(GetNextTuple, handle, &tuple); 251 } 252 253 CS_CHECK(RequestConfiguration, handle, &link->conf); 254 255 /* 256 * Register the card with the core. 257 */ 258 j=ixj_pcmcia_probe(link->io.BasePort1,link->io.BasePort1 + 0x10); 259 260 info->ndev = 1; 261 info->node.major = PHONE_MAJOR; 262 link->dev = &info->node; 263 ixj_get_serial(link, j); 264 link->state &= ~DEV_CONFIG_PENDING; 265 return; 266 cs_failed: 267 cs_error(link->handle, last_fn, last_ret); 268 ixj_cs_release((u_long) link); 269} 270 271static void ixj_cs_release(u_long arg) 272{ 273 dev_link_t *link = (dev_link_t *) arg; 274 ixj_info_t *info = link->priv; 275 DEBUG(0, "ixj_cs_release(0x%p)\n", link); 276 info->ndev = 0; 277 link->dev = NULL; 278 CardServices(ReleaseConfiguration, link->handle); 279 CardServices(ReleaseIO, link->handle, &link->io); 280 link->state &= ~DEV_CONFIG; 281} 282 283static int ixj_event(event_t event, int priority, event_callback_args_t * args) 284{ 285 dev_link_t *link = args->client_data; 286 DEBUG(1, "ixj_event(0x%06x)\n", event); 287 switch (event) { 288 case CS_EVENT_CARD_REMOVAL: 289 link->state &= ~DEV_PRESENT; 290 if (link->state & DEV_CONFIG) { 291 link->release.expires = jiffies + (HZ / 20); 292 link->state |= DEV_RELEASE_PENDING; 293 add_timer(&link->release); 294 } 295 break; 296 case CS_EVENT_CARD_INSERTION: 297 link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; 298 ixj_config(link); 299 break; 300 case CS_EVENT_PM_SUSPEND: 301 link->state |= DEV_SUSPEND; 302 /* Fall through... */ 303 case CS_EVENT_RESET_PHYSICAL: 304 if (link->state & DEV_CONFIG) 305 CardServices(ReleaseConfiguration, link->handle); 306 break; 307 case CS_EVENT_PM_RESUME: 308 link->state &= ~DEV_SUSPEND; 309 /* Fall through... */ 310 case CS_EVENT_CARD_RESET: 311 if (DEV_OK(link)) 312 CardServices(RequestConfiguration, link->handle, &link->conf); 313 break; 314 } 315 return 0; 316} 317 318int __init ixj_register_pcmcia(void) 319{ 320 servinfo_t serv; 321 DEBUG(0, "%s\n", version); 322 CardServices(GetCardServicesInfo, &serv); 323 if (serv.Revision != CS_RELEASE_CODE) { 324 printk(KERN_NOTICE "ixj_cs: Card Services release does not match!\n"); 325 return -EINVAL; 326 } 327 register_pcmcia_driver(&dev_info, &ixj_attach, &ixj_detach); 328 return 0; 329} 330 331static void ixj_pcmcia_unload(void) 332{ 333 DEBUG(0, "ixj_cs: unloading\n"); 334 unregister_pcmcia_driver(&dev_info); 335 while (dev_list != NULL) 336 ixj_detach(dev_list); 337} 338 339module_init(ixj_register_pcmcia); 340module_exit(ixj_pcmcia_unload); 341 342MODULE_LICENSE("GPL"); 343 344