| /* from src/prism2/download/prism2dl.c |
| * |
| * utility for downloading prism2 images moved into kernelspace |
| * |
| * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. |
| * -------------------------------------------------------------------- |
| * |
| * linux-wlan |
| * |
| * The contents of this file are subject to the Mozilla Public |
| * License Version 1.1 (the "License"); you may not use this file |
| * except in compliance with the License. You may obtain a copy of |
| * the License at http://www.mozilla.org/MPL/ |
| * |
| * Software distributed under the License is distributed on an "AS |
| * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or |
| * implied. See the License for the specific language governing |
| * rights and limitations under the License. |
| * |
| * Alternatively, the contents of this file may be used under the |
| * terms of the GNU Public License version 2 (the "GPL"), in which |
| * case the provisions of the GPL are applicable instead of the |
| * above. If you wish to allow the use of your version of this file |
| * only under the terms of the GPL and not to allow others to use |
| * your version of this file under the MPL, indicate your decision |
| * by deleting the provisions above and replace them with the notice |
| * and other provisions required by the GPL. If you do not delete |
| * the provisions above, a recipient may use your version of this |
| * file under either the MPL or the GPL. |
| * |
| * -------------------------------------------------------------------- |
| * |
| * Inquiries regarding the linux-wlan Open Source project can be |
| * made directly to: |
| * |
| * AbsoluteValue Systems Inc. |
| * info@linux-wlan.com |
| * http://www.linux-wlan.com |
| * |
| * -------------------------------------------------------------------- |
| * |
| * Portions of the development of this software were funded by |
| * Intersil Corporation as part of PRISM(R) chipset product development. |
| * |
| * -------------------------------------------------------------------- |
| */ |
| |
| /*================================================================*/ |
| /* System Includes */ |
| #include <linux/sort.h> |
| #include <linux/firmware.h> |
| |
| /*================================================================*/ |
| /* Local Constants */ |
| |
| #define PRISM2_USB_FWFILE "prism2_ru.hex" |
| |
| #define S3DATA_MAX 5000 |
| #define S3PLUG_MAX 200 |
| #define S3CRC_MAX 200 |
| #define S3INFO_MAX 50 |
| #define SREC_LINE_MAX 264 |
| #define S3LEN_TXTOFFSET 2 |
| #define S3LEN_TXTLEN 2 |
| #define S3ADDR_TXTOFFSET 4 |
| #define S3ADDR_TXTLEN 8 |
| #define S3DATA_TXTOFFSET 12 |
| /*S3DATA_TXTLEN variable, depends on len field */ |
| /*S3CKSUM_TXTOFFSET variable, depends on len field */ |
| #define S3CKSUM_TXTLEN 2 |
| #define SERNUM_LEN_MAX 12 |
| |
| #define S3PLUG_ITEMCODE_TXTOFFSET (S3DATA_TXTOFFSET) |
| #define S3PLUG_ITEMCODE_TXTLEN 8 |
| #define S3PLUG_ADDR_TXTOFFSET (S3DATA_TXTOFFSET+8) |
| #define S3PLUG_ADDR_TXTLEN 8 |
| #define S3PLUG_LEN_TXTOFFSET (S3DATA_TXTOFFSET+16) |
| #define S3PLUG_LEN_TXTLEN 8 |
| |
| #define S3CRC_ADDR_TXTOFFSET (S3DATA_TXTOFFSET) |
| #define S3CRC_ADDR_TXTLEN 8 |
| #define S3CRC_LEN_TXTOFFSET (S3DATA_TXTOFFSET+8) |
| #define S3CRC_LEN_TXTLEN 8 |
| #define S3CRC_DOWRITE_TXTOFFSET (S3DATA_TXTOFFSET+16) |
| #define S3CRC_DOWRITE_TXTLEN 8 |
| |
| #define S3INFO_LEN_TXTOFFSET (S3DATA_TXTOFFSET) |
| #define S3INFO_LEN_TXTLEN 4 |
| #define S3INFO_TYPE_TXTOFFSET (S3DATA_TXTOFFSET+4) |
| #define S3INFO_TYPE_TXTLEN 4 |
| #define S3INFO_DATA_TXTOFFSET (S3DATA_TXTOFFSET+8) |
| /* S3INFO_DATA_TXTLEN variable, depends on INFO_LEN field */ |
| |
| #define S3ADDR_PLUG (0xff000000UL) |
| #define S3ADDR_CRC (0xff100000UL) |
| #define S3ADDR_INFO (0xff200000UL) |
| |
| #define PDAFILE_LINE_MAX 1024 |
| |
| #define CHUNKS_MAX 100 |
| |
| #define WRITESIZE_MAX 4096 |
| |
| /*================================================================*/ |
| /* Local Macros */ |
| |
| #define bswap_16(x) \ |
| (__extension__ \ |
| ({ register unsigned short int __v, __x = (x); \ |
| __asm__ ("rorw $8, %w0" \ |
| : "=r" (__v) \ |
| : "0" (__x) \ |
| : "cc"); \ |
| __v; })) |
| |
| #define bswap_32(x) \ |
| (__extension__ \ |
| ({ register unsigned int __v, __x = (x); \ |
| __asm__ ("rorw $8, %w0;" \ |
| "rorl $16, %0;" \ |
| "rorw $8, %w0" \ |
| : "=r" (__v) \ |
| : "0" (__x) \ |
| : "cc"); \ |
| __v; })) |
| |
| /*================================================================*/ |
| /* Local Types */ |
| |
| typedef struct s3datarec { |
| u32 len; |
| u32 addr; |
| u8 checksum; |
| u8 *data; |
| } s3datarec_t; |
| |
| typedef struct s3plugrec { |
| u32 itemcode; |
| u32 addr; |
| u32 len; |
| } s3plugrec_t; |
| |
| typedef struct s3crcrec { |
| u32 addr; |
| u32 len; |
| unsigned int dowrite; |
| } s3crcrec_t; |
| |
| typedef struct s3inforec { |
| u16 len; |
| u16 type; |
| union { |
| hfa384x_compident_t version; |
| hfa384x_caplevel_t compat; |
| u16 buildseq; |
| hfa384x_compident_t platform; |
| } info; |
| } s3inforec_t; |
| |
| typedef struct pda { |
| u8 buf[HFA384x_PDA_LEN_MAX]; |
| hfa384x_pdrec_t *rec[HFA384x_PDA_RECS_MAX]; |
| unsigned int nrec; |
| } pda_t; |
| |
| typedef struct imgchunk { |
| u32 addr; /* start address */ |
| u32 len; /* in bytes */ |
| u16 crc; /* CRC value (if it falls at a chunk boundary) */ |
| u8 *data; |
| } imgchunk_t; |
| |
| /*================================================================*/ |
| /* Local Static Definitions */ |
| |
| /*----------------------------------------------------------------*/ |
| /* s-record image processing */ |
| |
| /* Data records */ |
| unsigned int ns3data = 0; |
| s3datarec_t s3data[S3DATA_MAX]; |
| |
| /* Plug records */ |
| unsigned int ns3plug = 0; |
| s3plugrec_t s3plug[S3PLUG_MAX]; |
| |
| /* CRC records */ |
| unsigned int ns3crc = 0; |
| s3crcrec_t s3crc[S3CRC_MAX]; |
| |
| /* Info records */ |
| unsigned int ns3info = 0; |
| s3inforec_t s3info[S3INFO_MAX]; |
| |
| /* S7 record (there _better_ be only one) */ |
| u32 startaddr; |
| |
| /* Load image chunks */ |
| unsigned int nfchunks; |
| imgchunk_t fchunk[CHUNKS_MAX]; |
| |
| /* Note that for the following pdrec_t arrays, the len and code */ |
| /* fields are stored in HOST byte order. The mkpdrlist() function */ |
| /* does the conversion. */ |
| /*----------------------------------------------------------------*/ |
| /* PDA, built from [card|newfile]+[addfile1+addfile2...] */ |
| |
| pda_t pda; |
| hfa384x_compident_t nicid; |
| hfa384x_caplevel_t rfid; |
| hfa384x_caplevel_t macid; |
| hfa384x_caplevel_t priid; |
| |
| /*================================================================*/ |
| /* Local Function Declarations */ |
| |
| int prism2_fwapply(char *rfptr, int rfsize, wlandevice_t * wlandev); |
| int read_srecfile(char *rfptr, int rfsize); |
| int mkimage(imgchunk_t * clist, unsigned int *ccnt); |
| int read_cardpda(pda_t * pda, wlandevice_t * wlandev); |
| int mkpdrlist(pda_t * pda); |
| int s3datarec_compare(const void *p1, const void *p2); |
| int plugimage(imgchunk_t * fchunk, unsigned int nfchunks, |
| s3plugrec_t * s3plug, unsigned int ns3plug, pda_t * pda); |
| int crcimage(imgchunk_t * fchunk, unsigned int nfchunks, |
| s3crcrec_t * s3crc, unsigned int ns3crc); |
| int writeimage(wlandevice_t * wlandev, imgchunk_t * fchunk, |
| unsigned int nfchunks); |
| void free_chunks(imgchunk_t * fchunk, unsigned int *nfchunks); |
| void free_srecs(void); |
| |
| int validate_identity(void); |
| |
| /*================================================================*/ |
| /* Function Definitions */ |
| |
| /*---------------------------------------------------------------- |
| * prism2_fwtry |
| * |
| * Try and get firmware into memory |
| * |
| * Arguments: |
| * udev usb device structure |
| * wlandev wlan device structure |
| * |
| * Returns: |
| * 0 - success |
| * ~0 - failure |
| ----------------------------------------------------------------*/ |
| int prism2_fwtry(struct usb_device *udev, wlandevice_t * wlandev) |
| { |
| const struct firmware *fw_entry = NULL; |
| |
| printk(KERN_INFO "prism2_usb: Checking for firmware %s\n", |
| PRISM2_USB_FWFILE); |
| if (request_firmware(&fw_entry, PRISM2_USB_FWFILE, &udev->dev) != 0) { |
| printk(KERN_INFO |
| "prism2_usb: Firmware not available, but not essential\n"); |
| printk(KERN_INFO |
| "prism2_usb: can continue to use card anyway.\n"); |
| return 1; |
| } |
| |
| printk(KERN_INFO "prism2_usb: %s will be processed, size %d\n", |
| PRISM2_USB_FWFILE, fw_entry->size); |
| prism2_fwapply((char *)fw_entry->data, fw_entry->size, wlandev); |
| |
| release_firmware(fw_entry); |
| return 0; |
| } |
| |
| /*---------------------------------------------------------------- |
| * prism2_fwapply |
| * |
| * Apply the firmware loaded into memory |
| * |
| * Arguments: |
| * rfptr firmware image in kernel memory |
| * rfsize firmware size in kernel memory |
| * wlandev device |
| * |
| * Returns: |
| * 0 - success |
| * ~0 - failure |
| ----------------------------------------------------------------*/ |
| int prism2_fwapply(char *rfptr, int rfsize, wlandevice_t * wlandev) |
| { |
| signed int result = 0; |
| p80211msg_dot11req_mibget_t getmsg; |
| p80211itemd_t *item; |
| u32 *data; |
| |
| /* Initialize the data structures */ |
| ns3data = 0; |
| memset(s3data, 0, sizeof(s3data)); |
| ns3plug = 0; |
| memset(s3plug, 0, sizeof(s3plug)); |
| ns3crc = 0; |
| memset(s3crc, 0, sizeof(s3crc)); |
| ns3info = 0; |
| memset(s3info, 0, sizeof(s3info)); |
| startaddr = 0; |
| |
| nfchunks = 0; |
| memset(fchunk, 0, sizeof(fchunk)); |
| memset(&nicid, 0, sizeof(nicid)); |
| memset(&rfid, 0, sizeof(rfid)); |
| memset(&macid, 0, sizeof(macid)); |
| memset(&priid, 0, sizeof(priid)); |
| |
| /* clear the pda and add an initial END record */ |
| memset(&pda, 0, sizeof(pda)); |
| pda.rec[0] = (hfa384x_pdrec_t *) pda.buf; |
| pda.rec[0]->len = cpu_to_le16(2); /* len in words *//* len in words */ |
| pda.rec[0]->code = cpu_to_le16(HFA384x_PDR_END_OF_PDA); |
| pda.nrec = 1; |
| |
| /*-----------------------------------------------------*/ |
| /* Put card into fwload state */ |
| prism2sta_ifstate(wlandev, P80211ENUM_ifstate_fwload); |
| |
| /* Build the PDA we're going to use. */ |
| if (read_cardpda(&pda, wlandev)) { |
| printk(KERN_ERR "load_cardpda failed, exiting.\n"); |
| return (1); |
| } |
| |
| /* read the card's PRI-SUP */ |
| memset(&getmsg, 0, sizeof(getmsg)); |
| getmsg.msgcode = DIDmsg_dot11req_mibget; |
| getmsg.msglen = sizeof(getmsg); |
| strcpy(getmsg.devname, wlandev->name); |
| |
| getmsg.mibattribute.did = DIDmsg_dot11req_mibget_mibattribute; |
| getmsg.mibattribute.status = P80211ENUM_msgitem_status_data_ok; |
| getmsg.resultcode.did = DIDmsg_dot11req_mibget_resultcode; |
| getmsg.resultcode.status = P80211ENUM_msgitem_status_no_value; |
| |
| item = (p80211itemd_t *) getmsg.mibattribute.data; |
| item->did = DIDmib_p2_p2NIC_p2PRISupRange; |
| item->status = P80211ENUM_msgitem_status_no_value; |
| |
| data = (u32 *) item->data; |
| |
| /* DIDmsg_dot11req_mibget */ |
| prism2mgmt_mibset_mibget(wlandev, &getmsg); |
| if (getmsg.resultcode.data != P80211ENUM_resultcode_success) { |
| printk(KERN_ERR "Couldn't fetch PRI-SUP info\n"); |
| } |
| |
| /* Already in host order */ |
| priid.role = *data++; |
| priid.id = *data++; |
| priid.variant = *data++; |
| priid.bottom = *data++; |
| priid.top = *data++; |
| |
| /* Read the S3 file */ |
| result = read_srecfile(rfptr, rfsize); |
| if (result) { |
| printk(KERN_ERR "Failed to read the data exiting.\n"); |
| return (1); |
| } |
| /* Sort the S3 data records */ |
| sort(s3data, ns3data, sizeof(s3datarec_t), s3datarec_compare, NULL); |
| |
| result = validate_identity(); |
| |
| if (result) { |
| printk(KERN_ERR "Incompatible firmware image.\n"); |
| return (1); |
| } |
| |
| if (startaddr == 0x00000000) { |
| printk(KERN_ERR "Can't RAM download a Flash image!\n"); |
| return (1); |
| } |
| |
| /* Make the image chunks */ |
| result = mkimage(fchunk, &nfchunks); |
| |
| /* Do any plugging */ |
| result = plugimage(fchunk, nfchunks, s3plug, ns3plug, &pda); |
| if (result) { |
| printk(KERN_ERR "Failed to plug data.\n"); |
| return (1); |
| } |
| |
| /* Insert any CRCs */ |
| if (crcimage(fchunk, nfchunks, s3crc, ns3crc)) { |
| printk(KERN_ERR "Failed to insert all CRCs\n"); |
| return (1); |
| } |
| |
| /* Write the image */ |
| result = writeimage(wlandev, fchunk, nfchunks); |
| if (result) { |
| printk(KERN_ERR "Failed to ramwrite image data.\n"); |
| return (1); |
| } |
| |
| /* clear any allocated memory */ |
| free_chunks(fchunk, &nfchunks); |
| free_srecs(); |
| |
| printk(KERN_INFO "prism2_usb: firmware loading finished.\n"); |
| |
| return result; |
| } |
| |
| /*---------------------------------------------------------------- |
| * crcimage |
| * |
| * Adds a CRC16 in the two bytes prior to each block identified by |
| * an S3 CRC record. Currently, we don't actually do a CRC we just |
| * insert the value 0xC0DE in hfa384x order. |
| * |
| * Arguments: |
| * fchunk Array of image chunks |
| * nfchunks Number of image chunks |
| * s3crc Array of crc records |
| * ns3crc Number of crc records |
| * |
| * Returns: |
| * 0 success |
| * ~0 failure |
| ----------------------------------------------------------------*/ |
| int crcimage(imgchunk_t * fchunk, unsigned int nfchunks, s3crcrec_t * s3crc, |
| unsigned int ns3crc) |
| { |
| int result = 0; |
| int i; |
| int c; |
| u32 crcstart; |
| u32 crcend; |
| u32 cstart = 0; |
| u32 cend; |
| u8 *dest; |
| u32 chunkoff; |
| |
| for (i = 0; i < ns3crc; i++) { |
| if (!s3crc[i].dowrite) |
| continue; |
| crcstart = s3crc[i].addr; |
| crcend = s3crc[i].addr + s3crc[i].len; |
| /* Find chunk */ |
| for (c = 0; c < nfchunks; c++) { |
| cstart = fchunk[c].addr; |
| cend = fchunk[c].addr + fchunk[c].len; |
| /* the line below does an address & len match search */ |
| /* unfortunately, I've found that the len fields of */ |
| /* some crc records don't match with the length of */ |
| /* the actual data, so we're not checking right */ |
| /* now */ |
| /* if ( crcstart-2 >= cstart && crcend <= cend ) break; */ |
| |
| /* note the -2 below, it's to make sure the chunk has */ |
| /* space for the CRC value */ |
| if (crcstart - 2 >= cstart && crcstart < cend) |
| break; |
| } |
| if (c >= nfchunks) { |
| printk(KERN_ERR |
| "Failed to find chunk for " |
| "crcrec[%d], addr=0x%06x len=%d , " |
| "aborting crc.\n", |
| i, s3crc[i].addr, s3crc[i].len); |
| return 1; |
| } |
| |
| /* Insert crc */ |
| pr_debug("Adding crc @ 0x%06x\n", s3crc[i].addr - 2); |
| chunkoff = crcstart - cstart - 2; |
| dest = fchunk[c].data + chunkoff; |
| *dest = 0xde; |
| *(dest + 1) = 0xc0; |
| |
| } |
| return result; |
| } |
| |
| /*---------------------------------------------------------------- |
| * free_chunks |
| * |
| * Clears the chunklist data structures in preparation for a new file. |
| * |
| * Arguments: |
| * none |
| * |
| * Returns: |
| * nothing |
| ----------------------------------------------------------------*/ |
| void free_chunks(imgchunk_t * fchunk, unsigned int *nfchunks) |
| { |
| int i; |
| for (i = 0; i < *nfchunks; i++) { |
| if (fchunk[i].data != NULL) { |
| kfree(fchunk[i].data); |
| } |
| } |
| *nfchunks = 0; |
| memset(fchunk, 0, sizeof(fchunk)); |
| |
| } |
| |
| /*---------------------------------------------------------------- |
| * free_srecs |
| * |
| * Clears the srec data structures in preparation for a new file. |
| * |
| * Arguments: |
| * none |
| * |
| * Returns: |
| * nothing |
| ----------------------------------------------------------------*/ |
| void free_srecs(void) |
| { |
| int i; |
| for (i = 0; i < ns3data; i++) { |
| kfree(s3data[i].data); |
| } |
| ns3data = 0; |
| memset(s3data, 0, sizeof(s3data)); |
| ns3plug = 0; |
| memset(s3plug, 0, sizeof(s3plug)); |
| ns3crc = 0; |
| memset(s3crc, 0, sizeof(s3crc)); |
| ns3info = 0; |
| memset(s3info, 0, sizeof(s3info)); |
| startaddr = 0; |
| } |
| |
| /*---------------------------------------------------------------- |
| * mkimage |
| * |
| * Scans the currently loaded set of S records for data residing |
| * in contiguous memory regions. Each contiguous region is then |
| * made into a 'chunk'. This function assumes that we're building |
| * a new chunk list. Assumes the s3data items are in sorted order. |
| * |
| * Arguments: none |
| * |
| * Returns: |
| * 0 - success |
| * ~0 - failure (probably an errno) |
| ----------------------------------------------------------------*/ |
| int mkimage(imgchunk_t * clist, unsigned int *ccnt) |
| { |
| int result = 0; |
| int i; |
| int j; |
| int currchunk = 0; |
| u32 nextaddr = 0; |
| u32 s3start; |
| u32 s3end; |
| u32 cstart = 0; |
| u32 cend; |
| u32 coffset; |
| |
| /* There may already be data in the chunklist */ |
| *ccnt = 0; |
| |
| /* Establish the location and size of each chunk */ |
| for (i = 0; i < ns3data; i++) { |
| if (s3data[i].addr == nextaddr) { |
| /* existing chunk, grow it */ |
| clist[currchunk].len += s3data[i].len; |
| nextaddr += s3data[i].len; |
| } else { |
| /* New chunk */ |
| (*ccnt)++; |
| currchunk = *ccnt - 1; |
| clist[currchunk].addr = s3data[i].addr; |
| clist[currchunk].len = s3data[i].len; |
| nextaddr = s3data[i].addr + s3data[i].len; |
| /* Expand the chunk if there is a CRC record at */ |
| /* their beginning bound */ |
| for (j = 0; j < ns3crc; j++) { |
| if (s3crc[j].dowrite && |
| s3crc[j].addr == clist[currchunk].addr) { |
| clist[currchunk].addr -= 2; |
| clist[currchunk].len += 2; |
| } |
| } |
| } |
| } |
| |
| /* We're currently assuming there aren't any overlapping chunks */ |
| /* if this proves false, we'll need to add code to coalesce. */ |
| |
| /* Allocate buffer space for chunks */ |
| for (i = 0; i < *ccnt; i++) { |
| clist[i].data = kmalloc(clist[i].len, GFP_KERNEL); |
| if (clist[i].data == NULL) { |
| printk(KERN_ERR |
| "failed to allocate image space, exitting.\n"); |
| return (1); |
| } |
| memset(clist[i].data, 0, clist[i].len); |
| } |
| |
| /* Display chunks */ |
| for (i = 0; i < *ccnt; i++) { |
| pr_debug("chunk[%d]: addr=0x%06x len=%d\n", |
| i, clist[i].addr, clist[i].len); |
| } |
| |
| /* Copy srec data to chunks */ |
| for (i = 0; i < ns3data; i++) { |
| s3start = s3data[i].addr; |
| s3end = s3start + s3data[i].len - 1; |
| for (j = 0; j < *ccnt; j++) { |
| cstart = clist[j].addr; |
| cend = cstart + clist[j].len - 1; |
| if (s3start >= cstart && s3end <= cend) { |
| break; |
| } |
| } |
| if (((unsigned int)j) >= (*ccnt)) { |
| printk(KERN_ERR |
| "s3rec(a=0x%06x,l=%d), no chunk match, exiting.\n", |
| s3start, s3data[i].len); |
| return (1); |
| } |
| coffset = s3start - cstart; |
| memcpy(clist[j].data + coffset, s3data[i].data, s3data[i].len); |
| } |
| |
| return result; |
| } |
| |
| /*---------------------------------------------------------------- |
| * mkpdrlist |
| * |
| * Reads a raw PDA and builds an array of pdrec_t structures. |
| * |
| * Arguments: |
| * pda buffer containing raw PDA bytes |
| * pdrec ptr to an array of pdrec_t's. Will be filled on exit. |
| * nrec ptr to a variable that will contain the count of PDRs |
| * |
| * Returns: |
| * 0 - success |
| * ~0 - failure (probably an errno) |
| ----------------------------------------------------------------*/ |
| int mkpdrlist(pda_t * pda) |
| { |
| int result = 0; |
| u16 *pda16 = (u16 *) pda->buf; |
| int curroff; /* in 'words' */ |
| |
| pda->nrec = 0; |
| curroff = 0; |
| while (curroff < (HFA384x_PDA_LEN_MAX / 2) && |
| le16_to_cpu(pda16[curroff + 1]) != HFA384x_PDR_END_OF_PDA) { |
| pda->rec[pda->nrec] = (hfa384x_pdrec_t *) & (pda16[curroff]); |
| |
| if (le16_to_cpu(pda->rec[pda->nrec]->code) == HFA384x_PDR_NICID) { |
| memcpy(&nicid, &pda->rec[pda->nrec]->data.nicid, |
| sizeof(nicid)); |
| nicid.id = le16_to_cpu(nicid.id); |
| nicid.variant = le16_to_cpu(nicid.variant); |
| nicid.major = le16_to_cpu(nicid.major); |
| nicid.minor = le16_to_cpu(nicid.minor); |
| } |
| if (le16_to_cpu(pda->rec[pda->nrec]->code) == |
| HFA384x_PDR_MFISUPRANGE) { |
| memcpy(&rfid, &pda->rec[pda->nrec]->data.mfisuprange, |
| sizeof(rfid)); |
| rfid.id = le16_to_cpu(rfid.id); |
| rfid.variant = le16_to_cpu(rfid.variant); |
| rfid.bottom = le16_to_cpu(rfid.bottom); |
| rfid.top = le16_to_cpu(rfid.top); |
| } |
| if (le16_to_cpu(pda->rec[pda->nrec]->code) == |
| HFA384x_PDR_CFISUPRANGE) { |
| memcpy(&macid, &pda->rec[pda->nrec]->data.cfisuprange, |
| sizeof(macid)); |
| macid.id = le16_to_cpu(macid.id); |
| macid.variant = le16_to_cpu(macid.variant); |
| macid.bottom = le16_to_cpu(macid.bottom); |
| macid.top = le16_to_cpu(macid.top); |
| } |
| |
| (pda->nrec)++; |
| curroff += le16_to_cpu(pda16[curroff]) + 1; |
| |
| } |
| if (curroff >= (HFA384x_PDA_LEN_MAX / 2)) { |
| printk(KERN_ERR |
| "no end record found or invalid lengths in " |
| "PDR data, exiting. %x %d\n", curroff, pda->nrec); |
| return (1); |
| } |
| if (le16_to_cpu(pda16[curroff + 1]) == HFA384x_PDR_END_OF_PDA) { |
| pda->rec[pda->nrec] = (hfa384x_pdrec_t *) & (pda16[curroff]); |
| (pda->nrec)++; |
| } |
| return result; |
| } |
| |
| /*---------------------------------------------------------------- |
| * plugimage |
| * |
| * Plugs the given image using the given plug records from the given |
| * PDA and filename. |
| * |
| * Arguments: |
| * fchunk Array of image chunks |
| * nfchunks Number of image chunks |
| * s3plug Array of plug records |
| * ns3plug Number of plug records |
| * pda Current pda data |
| * |
| * Returns: |
| * 0 success |
| * ~0 failure |
| ----------------------------------------------------------------*/ |
| int plugimage(imgchunk_t * fchunk, unsigned int nfchunks, |
| s3plugrec_t * s3plug, unsigned int ns3plug, pda_t * pda) |
| { |
| int result = 0; |
| int i; /* plug index */ |
| int j; /* index of PDR or -1 if fname plug */ |
| int c; /* chunk index */ |
| u32 pstart; |
| u32 pend; |
| u32 cstart = 0; |
| u32 cend; |
| u32 chunkoff; |
| u8 *dest; |
| |
| /* for each plug record */ |
| for (i = 0; i < ns3plug; i++) { |
| pstart = s3plug[i].addr; |
| pend = s3plug[i].addr + s3plug[i].len; |
| /* find the matching PDR (or filename) */ |
| if (s3plug[i].itemcode != 0xffffffffUL) { /* not filename */ |
| for (j = 0; j < pda->nrec; j++) { |
| if (s3plug[i].itemcode == |
| le16_to_cpu(pda->rec[j]->code)) |
| break; |
| } |
| } else { |
| j = -1; |
| } |
| if (j >= pda->nrec && j != -1) { /* if no matching PDR, fail */ |
| printk(KERN_WARNING |
| "warning: Failed to find PDR for " |
| "plugrec 0x%04x.\n", s3plug[i].itemcode); |
| continue; /* and move on to the next PDR */ |
| #if 0 |
| /* MSM: They swear that unless it's the MAC address, |
| * the serial number, or the TX calibration records, |
| * then there's reasonable defaults in the f/w |
| * image. Therefore, missing PDRs in the card |
| * should only be a warning, not fatal. |
| * TODO: add fatals for the PDRs mentioned above. |
| */ |
| result = 1; |
| continue; |
| #endif |
| } |
| |
| /* Validate plug len against PDR len */ |
| if (j != -1 && s3plug[i].len < le16_to_cpu(pda->rec[j]->len)) { |
| printk(KERN_ERR |
| "error: Plug vs. PDR len mismatch for " |
| "plugrec 0x%04x, abort plugging.\n", |
| s3plug[i].itemcode); |
| result = 1; |
| continue; |
| } |
| |
| /* Validate plug address against chunk data and identify chunk */ |
| for (c = 0; c < nfchunks; c++) { |
| cstart = fchunk[c].addr; |
| cend = fchunk[c].addr + fchunk[c].len; |
| if (pstart >= cstart && pend <= cend) |
| break; |
| } |
| if (c >= nfchunks) { |
| printk(KERN_ERR |
| "error: Failed to find image chunk for " |
| "plugrec 0x%04x.\n", s3plug[i].itemcode); |
| result = 1; |
| continue; |
| } |
| |
| /* Plug data */ |
| chunkoff = pstart - cstart; |
| dest = fchunk[c].data + chunkoff; |
| pr_debug("Plugging item 0x%04x @ 0x%06x, len=%d, " |
| "cnum=%d coff=0x%06x\n", |
| s3plug[i].itemcode, pstart, s3plug[i].len, |
| c, chunkoff); |
| |
| if (j == -1) { /* plug the filename */ |
| memset(dest, 0, s3plug[i].len); |
| strncpy(dest, PRISM2_USB_FWFILE, s3plug[i].len - 1); |
| } else { /* plug a PDR */ |
| memcpy(dest, &(pda->rec[j]->data), s3plug[i].len); |
| } |
| } |
| return result; |
| |
| } |
| |
| /*---------------------------------------------------------------- |
| * read_cardpda |
| * |
| * Sends the command for the driver to read the pda from the card |
| * named in the device variable. Upon success, the card pda is |
| * stored in the "cardpda" variables. Note that the pda structure |
| * is considered 'well formed' after this function. That means |
| * that the nrecs is valid, the rec array has been set up, and there's |
| * a valid PDAEND record in the raw PDA data. |
| * |
| * Arguments: |
| * pda pda structure |
| * wlandev device |
| * |
| * Returns: |
| * 0 - success |
| * ~0 - failure (probably an errno) |
| ----------------------------------------------------------------*/ |
| int read_cardpda(pda_t * pda, wlandevice_t * wlandev) |
| { |
| int result = 0; |
| p80211msg_p2req_readpda_t msg; |
| |
| /* set up the msg */ |
| msg.msgcode = DIDmsg_p2req_readpda; |
| msg.msglen = sizeof(msg); |
| strcpy(msg.devname, wlandev->name); |
| msg.pda.did = DIDmsg_p2req_readpda_pda; |
| msg.pda.len = HFA384x_PDA_LEN_MAX; |
| msg.pda.status = P80211ENUM_msgitem_status_no_value; |
| msg.resultcode.did = DIDmsg_p2req_readpda_resultcode; |
| msg.resultcode.len = sizeof(u32); |
| msg.resultcode.status = P80211ENUM_msgitem_status_no_value; |
| |
| if (prism2mgmt_readpda(wlandev, &msg) != 0) { |
| /* prism2mgmt_readpda prints an errno if appropriate */ |
| result = -1; |
| } else if (msg.resultcode.data == P80211ENUM_resultcode_success) { |
| memcpy(pda->buf, msg.pda.data, HFA384x_PDA_LEN_MAX); |
| result = mkpdrlist(pda); |
| } else { |
| /* resultcode must've been something other than success */ |
| result = -1; |
| } |
| |
| return result; |
| } |
| |
| /*---------------------------------------------------------------- |
| * copy_line |
| * |
| * Copies a line of text, up to \n, \0, or SREC_LINE_MAX, or limit of |
| * From array |
| * |
| * Arguments: |
| * from From addr |
| * to To addr |
| * limit Addr of last character in From array that can be copied |
| * |
| * Returns: |
| * Num characters copied |
| ----------------------------------------------------------------*/ |
| int copyline(char *from, char *to, char *limit) |
| { |
| int c = 0; |
| |
| while ((c < SREC_LINE_MAX - 1) && (from + c <= limit) && |
| (from[c] != '\n') && (from[c] != '\0')) { |
| to[c] = from[c]; |
| c++; |
| } |
| |
| to[c] = '\0'; |
| return (c < SREC_LINE_MAX - 1) ? c + 1 : c; |
| } |
| |
| /*---------------------------------------------------------------- |
| * read_srecfile |
| * |
| * Reads the given srecord file and loads the records into the |
| * s3xxx arrays. This function can be called repeatedly (once for |
| * each of a set of files), if necessary. This function performs |
| * no validation of the data except for the grossest of S-record |
| * line format checks. Don't forget that these will be DOS files... |
| * CR/LF at the end of each line. |
| * |
| * Here's the SREC format we're dealing with: |
| * S[37]nnaaaaaaaaddd...dddcc |
| * |
| * nn - number of bytes starting with the address field |
| * aaaaaaaa - address in readable (or big endian) format |
| * dd....dd - 0-245 data bytes (two chars per byte) |
| * cc - checksum |
| * |
| * The S7 record's (there should be only one) address value gets |
| * saved in startaddr. It's the start execution address used |
| * for RAM downloads. |
| * |
| * The S3 records have a collection of subformats indicated by the |
| * value of aaaaaaaa: |
| * 0xff000000 - Plug record, data field format: |
| * xxxxxxxxaaaaaaaassssssss |
| * x - PDR code number (little endian) |
| * a - Address in load image to plug (little endian) |
| * s - Length of plug data area (little endian) |
| * |
| * 0xff100000 - CRC16 generation record, data field format: |
| * aaaaaaaassssssssbbbbbbbb |
| * a - Start address for CRC calculation (little endian) |
| * s - Length of data to calculate over (little endian) |
| * b - Boolean, true=write crc, false=don't write |
| * |
| * 0xff200000 - Info record, data field format: |
| * ssssttttdd..dd |
| * s - Size in words (little endian) |
| * t - Info type (little endian), see #defines and |
| * s3inforec_t for details about types. |
| * d - (s - 1) little endian words giving the contents of |
| * the given info type. |
| * |
| * Arguments: |
| * rfptr firmware image (s-record structure) in kernel memory |
| * rfsize firmware size in kernel memory |
| * |
| * Returns: |
| * 0 - success |
| * ~0 - failure (probably an errno) |
| ----------------------------------------------------------------*/ |
| int read_srecfile(char *rfptr, int rfsize) |
| { |
| int result = 0; |
| char buf[SREC_LINE_MAX]; |
| char tmpbuf[30]; |
| s3datarec_t tmprec; |
| int i, c; |
| int line = 0; |
| u16 *tmpinfo; |
| char *endptr = rfptr + rfsize; |
| |
| pr_debug("Reading S-record file ...\n"); |
| |
| while ((c = copyline(rfptr, buf, endptr)) >= 12) { |
| rfptr = rfptr + c; |
| line++; |
| if (buf[0] != 'S') { |
| printk(KERN_ERR "%d warning: No initial \'S\'\n", line); |
| return 1; |
| } |
| if (buf[1] == '7') { /* S7 record, start address */ |
| buf[12] = '\0'; |
| startaddr = simple_strtoul(buf + 4, NULL, 16); |
| pr_debug(" S7 start addr, line=%d " |
| " addr=0x%08x\n", line, startaddr); |
| continue; |
| } else if (buf[1] == '3') { |
| /* Ok, it's an S3, parse and put it in the right array */ |
| /* Record Length field (we only want datalen) */ |
| memcpy(tmpbuf, buf + S3LEN_TXTOFFSET, S3LEN_TXTLEN); |
| tmpbuf[S3LEN_TXTLEN] = '\0'; |
| tmprec.len = simple_strtoul(tmpbuf, NULL, 16) - 4 - 1; /* 4=addr, 1=cksum */ |
| /* Address field */ |
| memcpy(tmpbuf, buf + S3ADDR_TXTOFFSET, S3ADDR_TXTLEN); |
| tmpbuf[S3ADDR_TXTLEN] = '\0'; |
| tmprec.addr = simple_strtoul(tmpbuf, NULL, 16); |
| /* Checksum field */ |
| tmprec.checksum = |
| simple_strtoul(buf + strlen(buf) - 2, NULL, 16); |
| |
| switch (tmprec.addr) { |
| case S3ADDR_PLUG: |
| memcpy(tmpbuf, buf + S3PLUG_ITEMCODE_TXTOFFSET, |
| S3PLUG_ITEMCODE_TXTLEN); |
| tmpbuf[S3PLUG_ITEMCODE_TXTLEN] = '\0'; |
| s3plug[ns3plug].itemcode = |
| simple_strtoul(tmpbuf, NULL, 16); |
| s3plug[ns3plug].itemcode = |
| bswap_32(s3plug[ns3plug].itemcode); |
| |
| memcpy(tmpbuf, buf + S3PLUG_ADDR_TXTOFFSET, |
| S3PLUG_ADDR_TXTLEN); |
| tmpbuf[S3PLUG_ADDR_TXTLEN] = '\0'; |
| s3plug[ns3plug].addr = |
| simple_strtoul(tmpbuf, NULL, 16); |
| s3plug[ns3plug].addr = |
| bswap_32(s3plug[ns3plug].addr); |
| |
| memcpy(tmpbuf, buf + S3PLUG_LEN_TXTOFFSET, |
| S3PLUG_LEN_TXTLEN); |
| tmpbuf[S3PLUG_LEN_TXTLEN] = '\0'; |
| s3plug[ns3plug].len = |
| simple_strtoul(tmpbuf, NULL, 16); |
| s3plug[ns3plug].len = |
| bswap_32(s3plug[ns3plug].len); |
| |
| pr_debug(" S3 plugrec, line=%d " |
| "itemcode=0x%04x addr=0x%08x len=%d\n", |
| line, |
| s3plug[ns3plug].itemcode, |
| s3plug[ns3plug].addr, |
| s3plug[ns3plug].len); |
| |
| ns3plug++; |
| if (ns3plug == S3PLUG_MAX) { |
| printk(KERN_ERR |
| "S3 plugrec limit reached - aborting\n"); |
| return 1; |
| } |
| break; |
| case S3ADDR_CRC: |
| memcpy(tmpbuf, buf + S3CRC_ADDR_TXTOFFSET, |
| S3CRC_ADDR_TXTLEN); |
| tmpbuf[S3CRC_ADDR_TXTLEN] = '\0'; |
| s3crc[ns3crc].addr = |
| simple_strtoul(tmpbuf, NULL, 16); |
| s3crc[ns3crc].addr = |
| bswap_32(s3crc[ns3crc].addr); |
| |
| memcpy(tmpbuf, buf + S3CRC_LEN_TXTOFFSET, |
| S3CRC_LEN_TXTLEN); |
| tmpbuf[S3CRC_LEN_TXTLEN] = '\0'; |
| s3crc[ns3crc].len = |
| simple_strtoul(tmpbuf, NULL, 16); |
| s3crc[ns3crc].len = bswap_32(s3crc[ns3crc].len); |
| |
| memcpy(tmpbuf, buf + S3CRC_DOWRITE_TXTOFFSET, |
| S3CRC_DOWRITE_TXTLEN); |
| tmpbuf[S3CRC_DOWRITE_TXTLEN] = '\0'; |
| s3crc[ns3crc].dowrite = |
| simple_strtoul(tmpbuf, NULL, 16); |
| s3crc[ns3crc].dowrite = |
| bswap_32(s3crc[ns3crc].dowrite); |
| |
| pr_debug(" S3 crcrec, line=%d " |
| "addr=0x%08x len=%d write=0x%08x\n", |
| line, |
| s3crc[ns3crc].addr, |
| s3crc[ns3crc].len, |
| s3crc[ns3crc].dowrite); |
| ns3crc++; |
| if (ns3crc == S3CRC_MAX) { |
| printk(KERN_ERR |
| "S3 crcrec limit reached - aborting\n"); |
| return 1; |
| } |
| break; |
| case S3ADDR_INFO: |
| memcpy(tmpbuf, buf + S3INFO_LEN_TXTOFFSET, |
| S3INFO_LEN_TXTLEN); |
| tmpbuf[S3INFO_LEN_TXTLEN] = '\0'; |
| s3info[ns3info].len = |
| simple_strtoul(tmpbuf, NULL, 16); |
| s3info[ns3info].len = |
| bswap_16(s3info[ns3info].len); |
| |
| memcpy(tmpbuf, buf + S3INFO_TYPE_TXTOFFSET, |
| S3INFO_TYPE_TXTLEN); |
| tmpbuf[S3INFO_TYPE_TXTLEN] = '\0'; |
| s3info[ns3info].type = |
| simple_strtoul(tmpbuf, NULL, 16); |
| s3info[ns3info].type = |
| bswap_16(s3info[ns3info].type); |
| |
| pr_debug(" S3 inforec, line=%d " |
| "len=0x%04x type=0x%04x\n", |
| line, |
| s3info[ns3info].len, |
| s3info[ns3info].type); |
| if (((s3info[ns3info].len - 1) * sizeof(u16)) > |
| sizeof(s3info[ns3info].info)) { |
| printk(KERN_ERR |
| " S3 inforec length too long - aborting\n"); |
| return 1; |
| } |
| |
| tmpinfo = |
| (u16 *) & (s3info[ns3info].info.version); |
| for (i = 0; i < s3info[ns3info].len - 1; i++) { |
| memcpy(tmpbuf, |
| buf + S3INFO_DATA_TXTOFFSET + |
| (i * 4), 4); |
| tmpbuf[4] = '\0'; |
| tmpinfo[i] = |
| simple_strtoul(tmpbuf, NULL, 16); |
| tmpinfo[i] = bswap_16(tmpinfo[i]); |
| } |
| pr_debug(" info="); |
| for (i = 0; i < s3info[ns3info].len - 1; i++) { |
| pr_debug("%04x ", tmpinfo[i]); |
| } |
| pr_debug("\n"); |
| |
| ns3info++; |
| if (ns3info == S3INFO_MAX) { |
| printk(KERN_ERR |
| "S3 inforec limit reached - aborting\n"); |
| return 1; |
| } |
| break; |
| default: /* Data record */ |
| s3data[ns3data].addr = tmprec.addr; |
| s3data[ns3data].len = tmprec.len; |
| s3data[ns3data].checksum = tmprec.checksum; |
| s3data[ns3data].data = |
| kmalloc(tmprec.len, GFP_KERNEL); |
| for (i = 0; i < tmprec.len; i++) { |
| memcpy(tmpbuf, |
| buf + S3DATA_TXTOFFSET + (i * 2), |
| 2); |
| tmpbuf[2] = '\0'; |
| s3data[ns3data].data[i] = |
| simple_strtoul(tmpbuf, NULL, 16); |
| } |
| ns3data++; |
| if (ns3data == S3DATA_MAX) { |
| printk(KERN_ERR |
| "S3 datarec limit reached - aborting\n"); |
| return 1; |
| } |
| break; |
| } |
| } else { |
| printk(KERN_WARNING |
| "%d warning: Unknown S-record detected.\n", |
| line); |
| } |
| } |
| return result; |
| } |
| |
| /*---------------------------------------------------------------- |
| * s3datarec_compare |
| * |
| * Comparison function for sort(). |
| * |
| * Arguments: |
| * p1 ptr to the first item |
| * p2 ptr to the second item |
| * Returns: |
| * 0 items are equal |
| * <0 p1 < p2 |
| * >0 p1 > p2 |
| ----------------------------------------------------------------*/ |
| int s3datarec_compare(const void *p1, const void *p2) |
| { |
| const s3datarec_t *s1 = p1; |
| const s3datarec_t *s2 = p2; |
| if (s1->addr == s2->addr) |
| return 0; |
| if (s1->addr < s2->addr) |
| return -1; |
| return 1; |
| } |
| |
| /*---------------------------------------------------------------- |
| * writeimage |
| * |
| * Takes the chunks, builds p80211 messages and sends them down |
| * to the driver for writing to the card. |
| * |
| * Arguments: |
| * wlandev device |
| * fchunk Array of image chunks |
| * nfchunks Number of image chunks |
| * |
| * Returns: |
| * 0 success |
| * ~0 failure |
| ----------------------------------------------------------------*/ |
| int writeimage(wlandevice_t * wlandev, imgchunk_t * fchunk, |
| unsigned int nfchunks) |
| { |
| int result = 0; |
| p80211msg_p2req_ramdl_state_t rstatemsg; |
| p80211msg_p2req_ramdl_write_t rwritemsg; |
| p80211msg_t *msgp; |
| u32 resultcode; |
| int i; |
| int j; |
| unsigned int nwrites; |
| u32 curroff; |
| u32 currlen; |
| u32 currdaddr; |
| |
| /* Initialize the messages */ |
| memset(&rstatemsg, 0, sizeof(rstatemsg)); |
| strcpy(rstatemsg.devname, wlandev->name); |
| rstatemsg.msgcode = DIDmsg_p2req_ramdl_state; |
| rstatemsg.msglen = sizeof(rstatemsg); |
| rstatemsg.enable.did = DIDmsg_p2req_ramdl_state_enable; |
| rstatemsg.exeaddr.did = DIDmsg_p2req_ramdl_state_exeaddr; |
| rstatemsg.resultcode.did = DIDmsg_p2req_ramdl_state_resultcode; |
| rstatemsg.enable.status = P80211ENUM_msgitem_status_data_ok; |
| rstatemsg.exeaddr.status = P80211ENUM_msgitem_status_data_ok; |
| rstatemsg.resultcode.status = P80211ENUM_msgitem_status_no_value; |
| rstatemsg.enable.len = sizeof(u32); |
| rstatemsg.exeaddr.len = sizeof(u32); |
| rstatemsg.resultcode.len = sizeof(u32); |
| |
| memset(&rwritemsg, 0, sizeof(rwritemsg)); |
| strcpy(rwritemsg.devname, wlandev->name); |
| rwritemsg.msgcode = DIDmsg_p2req_ramdl_write; |
| rwritemsg.msglen = sizeof(rwritemsg); |
| rwritemsg.addr.did = DIDmsg_p2req_ramdl_write_addr; |
| rwritemsg.len.did = DIDmsg_p2req_ramdl_write_len; |
| rwritemsg.data.did = DIDmsg_p2req_ramdl_write_data; |
| rwritemsg.resultcode.did = DIDmsg_p2req_ramdl_write_resultcode; |
| rwritemsg.addr.status = P80211ENUM_msgitem_status_data_ok; |
| rwritemsg.len.status = P80211ENUM_msgitem_status_data_ok; |
| rwritemsg.data.status = P80211ENUM_msgitem_status_data_ok; |
| rwritemsg.resultcode.status = P80211ENUM_msgitem_status_no_value; |
| rwritemsg.addr.len = sizeof(u32); |
| rwritemsg.len.len = sizeof(u32); |
| rwritemsg.data.len = WRITESIZE_MAX; |
| rwritemsg.resultcode.len = sizeof(u32); |
| |
| /* Send xxx_state(enable) */ |
| pr_debug("Sending dl_state(enable) message.\n"); |
| rstatemsg.enable.data = P80211ENUM_truth_true; |
| rstatemsg.exeaddr.data = startaddr; |
| |
| msgp = (p80211msg_t *) & rstatemsg; |
| result = prism2mgmt_ramdl_state(wlandev, msgp); |
| if (result) { |
| printk(KERN_ERR |
| "writeimage state enable failed w/ result=%d, " |
| "aborting download\n", result); |
| return result; |
| } |
| resultcode = rstatemsg.resultcode.data; |
| if (resultcode != P80211ENUM_resultcode_success) { |
| printk(KERN_ERR |
| "writeimage()->xxxdl_state msg indicates failure, " |
| "w/ resultcode=%d, aborting download.\n", resultcode); |
| return 1; |
| } |
| |
| /* Now, loop through the data chunks and send WRITESIZE_MAX data */ |
| for (i = 0; i < nfchunks; i++) { |
| nwrites = fchunk[i].len / WRITESIZE_MAX; |
| nwrites += (fchunk[i].len % WRITESIZE_MAX) ? 1 : 0; |
| curroff = 0; |
| for (j = 0; j < nwrites; j++) { |
| currlen = |
| (fchunk[i].len - (WRITESIZE_MAX * j)) > |
| WRITESIZE_MAX ? WRITESIZE_MAX : (fchunk[i].len - |
| (WRITESIZE_MAX * |
| j)); |
| curroff = j * WRITESIZE_MAX; |
| currdaddr = fchunk[i].addr + curroff; |
| /* Setup the message */ |
| rwritemsg.addr.data = currdaddr; |
| rwritemsg.len.data = currlen; |
| memcpy(rwritemsg.data.data, |
| fchunk[i].data + curroff, currlen); |
| |
| /* Send flashdl_write(pda) */ |
| pr_debug |
| ("Sending xxxdl_write message addr=%06x len=%d.\n", |
| currdaddr, currlen); |
| |
| msgp = (p80211msg_t *) & rwritemsg; |
| result = prism2mgmt_ramdl_write(wlandev, msgp); |
| |
| /* Check the results */ |
| if (result) { |
| printk(KERN_ERR |
| "writeimage chunk write failed w/ result=%d, " |
| "aborting download\n", result); |
| return result; |
| } |
| resultcode = rstatemsg.resultcode.data; |
| if (resultcode != P80211ENUM_resultcode_success) { |
| printk(KERN_ERR |
| "writeimage()->xxxdl_write msg indicates failure, " |
| "w/ resultcode=%d, aborting download.\n", |
| resultcode); |
| return 1; |
| } |
| |
| } |
| } |
| |
| /* Send xxx_state(disable) */ |
| pr_debug("Sending dl_state(disable) message.\n"); |
| rstatemsg.enable.data = P80211ENUM_truth_false; |
| rstatemsg.exeaddr.data = 0; |
| |
| msgp = (p80211msg_t *) & rstatemsg; |
| result = prism2mgmt_ramdl_state(wlandev, msgp); |
| if (result) { |
| printk(KERN_ERR |
| "writeimage state disable failed w/ result=%d, " |
| "aborting download\n", result); |
| return result; |
| } |
| resultcode = rstatemsg.resultcode.data; |
| if (resultcode != P80211ENUM_resultcode_success) { |
| printk(KERN_ERR |
| "writeimage()->xxxdl_state msg indicates failure, " |
| "w/ resultcode=%d, aborting download.\n", resultcode); |
| return 1; |
| } |
| return result; |
| } |
| |
| int validate_identity(void) |
| { |
| int i; |
| int result = 1; |
| |
| pr_debug("NIC ID: %#x v%d.%d.%d\n", |
| nicid.id, nicid.major, nicid.minor, nicid.variant); |
| pr_debug("MFI ID: %#x v%d %d->%d\n", |
| rfid.id, rfid.variant, rfid.bottom, rfid.top); |
| pr_debug("CFI ID: %#x v%d %d->%d\n", |
| macid.id, macid.variant, macid.bottom, macid.top); |
| pr_debug("PRI ID: %#x v%d %d->%d\n", |
| priid.id, priid.variant, priid.bottom, priid.top); |
| |
| for (i = 0; i < ns3info; i++) { |
| switch (s3info[i].type) { |
| case 1: |
| pr_debug("Version: ID %#x %d.%d.%d\n", |
| s3info[i].info.version.id, |
| s3info[i].info.version.major, |
| s3info[i].info.version.minor, |
| s3info[i].info.version.variant); |
| break; |
| case 2: |
| pr_debug("Compat: Role %#x Id %#x v%d %d->%d\n", |
| s3info[i].info.compat.role, |
| s3info[i].info.compat.id, |
| s3info[i].info.compat.variant, |
| s3info[i].info.compat.bottom, |
| s3info[i].info.compat.top); |
| |
| /* MAC compat range */ |
| if ((s3info[i].info.compat.role == 1) && |
| (s3info[i].info.compat.id == 2)) { |
| if (s3info[i].info.compat.variant != |
| macid.variant) { |
| result = 2; |
| } |
| } |
| |
| /* PRI compat range */ |
| if ((s3info[i].info.compat.role == 1) && |
| (s3info[i].info.compat.id == 3)) { |
| if ((s3info[i].info.compat.bottom > priid.top) |
| || (s3info[i].info.compat.top < |
| priid.bottom)) { |
| result = 3; |
| } |
| } |
| /* SEC compat range */ |
| if ((s3info[i].info.compat.role == 1) && |
| (s3info[i].info.compat.id == 4)) { |
| |
| } |
| |
| break; |
| case 3: |
| pr_debug("Seq: %#x\n", s3info[i].info.buildseq); |
| |
| break; |
| case 4: |
| pr_debug("Platform: ID %#x %d.%d.%d\n", |
| s3info[i].info.version.id, |
| s3info[i].info.version.major, |
| s3info[i].info.version.minor, |
| s3info[i].info.version.variant); |
| |
| if (nicid.id != s3info[i].info.version.id) |
| continue; |
| if (nicid.major != s3info[i].info.version.major) |
| continue; |
| if (nicid.minor != s3info[i].info.version.minor) |
| continue; |
| if ((nicid.variant != s3info[i].info.version.variant) && |
| (nicid.id != 0x8008)) |
| continue; |
| |
| if (result != 2) |
| result = 0; |
| break; |
| case 0x8001: |
| pr_debug("name inforec len %d\n", s3info[i].len); |
| |
| break; |
| default: |
| pr_debug("Unknown inforec type %d\n", s3info[i].type); |
| } |
| } |
| // walk through |
| |
| return result; |
| } |